wordcloud2.js
https://blogs.msdn.microsoft.com/dotnet/2019/01/10/announcing-ml-net-0-9-machine-learning-for-net/
https://marketplace.visualstudio.com/items?itemName=MLNET.07
https://dotnet.microsoft.com/learn/machinelearning-ai/ml-dotnet-get-started-tutorial
https://docs.microsoft.com/en-us/dotnet/machine-learning/
https://github.com/dotnet/machinelearning
wordcloud2.js:
/*!
* wordcloud2.js
* http://timdream.org/wordcloud2.js/
*https://github.com/timdream/wordcloud2.js/
* Copyright 2011 - 2013 Tim Chien
* Released under the MIT license
*/ 'use strict'; // setImmediate
if (!window.setImmediate) {
window.setImmediate = (function setupSetImmediate() {
return window.msSetImmediate ||
window.webkitSetImmediate ||
window.mozSetImmediate ||
window.oSetImmediate ||
(function setupSetZeroTimeout() {
if (!window.postMessage || !window.addEventListener) {
return null;
} var callbacks = [undefined];
var message = 'zero-timeout-message'; // Like setTimeout, but only takes a function argument. There's
// no time argument (always zero) and no arguments (you have to
// use a closure).
var setZeroTimeout = function setZeroTimeout(callback) {
var id = callbacks.length;
callbacks.push(callback);
window.postMessage(message + id.toString(36), '*'); return id;
}; window.addEventListener('message', function setZeroTimeoutMessage(evt) {
// Skipping checking event source, retarded IE confused this window
// object with another in the presence of iframe
if (typeof evt.data !== 'string' ||
evt.data.substr(0, message.length) !== message/* ||
evt.source !== window */) {
return;
} evt.stopImmediatePropagation(); var id = parseInt(evt.data.substr(message.length), 36);
if (!callbacks[id]) {
return;
} callbacks[id]();
callbacks[id] = undefined;
}, true); /* specify clearImmediate() here since we need the scope */
window.clearImmediate = function clearZeroTimeout(id) {
if (!callbacks[id]) {
return;
} callbacks[id] = undefined;
}; return setZeroTimeout;
})() ||
// fallback
function setImmediateFallback(fn) {
window.setTimeout(fn, 0);
};
})();
} if (!window.clearImmediate) {
window.clearImmediate = (function setupClearImmediate() {
return window.msClearImmediate ||
window.webkitClearImmediate ||
window.mozClearImmediate ||
window.oClearImmediate ||
// "clearZeroTimeout" is implement on the previous block ||
// fallback
function clearImmediateFallback(timer) {
window.clearTimeout(timer);
};
})();
} (function(global) { // Check if WordCloud can run on this browser
var isSupported = (function isSupported() {
var canvas = document.createElement('canvas');
if (!canvas || !canvas.getContext) {
return false;
} var ctx = canvas.getContext('2d');
if (!ctx.getImageData) {
return false;
}
if (!ctx.fillText) {
return false;
} if (!Array.prototype.some) {
return false;
}
if (!Array.prototype.push) {
return false;
} return true;
}()); // Find out if the browser impose minium font size by
// drawing small texts on a canvas and measure it's width.
var miniumFontSize = (function getMiniumFontSize() {
if (!isSupported) {
return;
} var ctx = document.createElement('canvas').getContext('2d'); // start from 20
var size = 20; // two sizes to measure
var hanWidth, mWidth; while (size) {
ctx.font = size.toString(10) + 'px sans-serif';
if ((ctx.measureText('\uFF37').width === hanWidth) &&
(ctx.measureText('m').width) === mWidth) {
return (size + 1);
} hanWidth = ctx.measureText('\uFF37').width;
mWidth = ctx.measureText('m').width; size--;
} return 0;
})(); // Based on http://jsfromhell.com/array/shuffle
var shuffleArray = function shuffleArray(arr) {
for (var j, x, i = arr.length; i;
j = Math.floor(Math.random() * i),
x = arr[--i], arr[i] = arr[j],
arr[j] = x) {}
return arr;
}; var WordCloud = function WordCloud(elements, options) {
if (!isSupported) {
return;
} if (!Array.isArray(elements)) {
elements = [elements];
} elements.forEach(function(el, i) {
if (typeof el === 'string') {
elements[i] = document.getElementById(el);
if (!elements[i]) {
throw 'The element id specified is not found.';
}
} else if (!el.tagName && !el.appendChild) {
throw 'You must pass valid HTML elements, or ID of the element.';
}
}); /* Default values to be overwritten by options object */
var settings = {
list: [],
fontFamily: '"Trebuchet MS", "Heiti TC", "微軟æ£é»‘é«”", ' +
'"Arial Unicode MS", "Droid Fallback Sans", sans-serif',
fontWeight: 'normal',
color: 'random-dark',
minSize: 0, // 0 to disable
weightFactor: 1,
clearCanvas: true,
backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1) gridSize: 8,
origin: null, drawMask: false,
maskColor: 'rgba(255,0,0,0.3)',
maskGapWidth: 0.3, wait: 0,
abortThreshold: 0, // disabled
abort: function noop() {}, minRotation: - Math.PI / 2,
maxRotation: Math.PI / 2, shuffle: true,
rotateRatio: 0.1, shape: 'circle',
ellipticity: 0.65, hover: null,
click: null
}; if (options) {
for (var key in options) {
if (key in settings) {
settings[key] = options[key];
}
}
} /* Convert weightFactor into a function */
if (typeof settings.weightFactor !== 'function') {
var factor = settings.weightFactor;
settings.weightFactor = function weightFactor(pt) {
return pt * factor; //in px
};
} /* Convert shape into a function */
if (typeof settings.shape !== 'function') {
switch (settings.shape) {
case 'circle':
/* falls through */
default:
// 'circle' is the default and a shortcut in the code loop.
settings.shape = 'circle';
break; case 'cardioid':
settings.shape = function shapeCardioid(theta) {
return 1 - Math.sin(theta);
};
break; /* To work out an X-gon, one has to calculate "m",
where 1/(cos(2*PI/X)+m*sin(2*PI/X)) = 1/(cos(0)+m*sin(0))
http://www.wolframalpha.com/input/?i=1%2F%28cos%282*PI%2FX%29%2Bm*sin%28
2*PI%2FX%29%29+%3D+1%2F%28cos%280%29%2Bm*sin%280%29%29 Copy the solution into polar equation r = 1/(cos(t') + m*sin(t'))
where t' equals to mod(t, 2PI/X); */ case 'diamond':
case 'square':
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
// %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D
// +0+..+2*PI
settings.shape = function shapeSquare(theta) {
var thetaPrime = theta % (2 * Math.PI / 4);
return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime));
};
break; case 'triangle-forward':
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
// %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29
// %29%29%2C+t+%3D+0+..+2*PI
settings.shape = function shapeTriangle(theta) {
var thetaPrime = theta % (2 * Math.PI / 3);
return 1 / (Math.cos(thetaPrime) +
Math.sqrt(3) * Math.sin(thetaPrime));
};
break; case 'triangle':
case 'triangle-upright':
settings.shape = function shapeTriangle(theta) {
var thetaPrime = (theta + Math.PI * 3 / 2) % (2 * Math.PI / 3);
return 1 / (Math.cos(thetaPrime) +
Math.sqrt(3) * Math.sin(thetaPrime));
};
break; case 'pentagon':
settings.shape = function shapePentagon(theta) {
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 5);
return 1 / (Math.cos(thetaPrime) +
0.726543 * Math.sin(thetaPrime));
};
break; case 'star':
settings.shape = function shapeStar(theta) {
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 10);
if ((theta + 0.955) % (2 * Math.PI / 5) - (2 * Math.PI / 10) >= 0) {
return 1 / (Math.cos((2 * Math.PI / 10) - thetaPrime) +
3.07768 * Math.sin((2 * Math.PI / 10) - thetaPrime));
} else {
return 1 / (Math.cos(thetaPrime) +
3.07768 * Math.sin(thetaPrime));
}
};
break;
}
} /* Make sure gridSize is a whole number and is not smaller than 4px */
settings.gridSize = Math.max(Math.floor(settings.gridSize), 4); /* shorthand */
var g = settings.gridSize;
var maskRectWidth = g - settings.maskGapWidth; /* normalize rotation settings */
var rotationRange = Math.abs(settings.maxRotation - settings.minRotation);
var minRotation = Math.min(settings.maxRotation, settings.minRotation); /* information/object available to all functions, set when start() */
var grid, // 2d array containing filling information
ngx, ngy, // width and height of the grid
center, // position of the center of the cloud
maxRadius; /* timestamp for measuring each putWord() action */
var escapeTime; /* function for getting the color of the text */
var getTextColor;
switch (settings.color) {
case 'random-dark':
getTextColor = function getRandomDarkColor() {
return 'rgb(' +
Math.floor(Math.random() * 128).toString(10) + ',' +
Math.floor(Math.random() * 128).toString(10) + ',' +
Math.floor(Math.random() * 128).toString(10) + ')';
};
break; case 'random-light':
getTextColor = function getRandomLightColor() {
return 'rgb(' +
Math.floor(Math.random() * 128 + 128).toString(10) + ',' +
Math.floor(Math.random() * 128 + 128).toString(10) + ',' +
Math.floor(Math.random() * 128 + 128).toString(10) + ')';
};
break; default:
if (typeof settings.color === 'function') {
getTextColor = settings.color;
}
break;
} /* Interactive */
var interactive = false;
var infoGrid = [];
var hovered; var getInfoGridFromMouseEvent = function getInfoGridFromMouseEvent(evt) {
var canvas = evt.currentTarget;
var rect = canvas.getBoundingClientRect();
var eventX = evt.clientX - rect.left;
var eventY = evt.clientY - rect.top; var x = Math.floor(eventX * ((canvas.width / rect.width) || 1) / g);
var y = Math.floor(eventY * ((canvas.height / rect.height) || 1) / g); return infoGrid[x][y];
}; var wordcloudhover = function wordcloudhover(evt) {
var info = getInfoGridFromMouseEvent(evt); if (hovered === info) {
return;
} hovered = info;
if (!info) {
settings.hover(undefined, undefined, evt); return;
} settings.hover(info.item, info.dimension, evt); }; var wordcloudclick = function wordcloudclick(evt) {
var info = getInfoGridFromMouseEvent(evt);
if (!info) {
return;
} settings.click(info.item, info.dimension, evt);
}; /* Get points on the grid for a given radius away from the center */
var pointsAtRadius = [];
var getPointsAtRadius = function getPointsAtRadius(radius) {
if (pointsAtRadius[radius]) {
return pointsAtRadius[radius];
} // Look for these number of points on each radius
var T = radius * 8; // Getting all the points at this radius
var t = T;
var points = []; if (radius === 0) {
points.push([center[0], center[1], 0]);
} while (t--) {
// distort the radius to put the cloud in shape
var rx = 1;
if (settings.shape !== 'circle') {
rx = settings.shape(t / T * 2 * Math.PI); // 0 to 1
} // Push [x, y, t]; t is used solely for getTextColor()
points.push([
center[0] + radius * rx * Math.cos(-t / T * 2 * Math.PI),
center[1] + radius * rx * Math.sin(-t / T * 2 * Math.PI) *
settings.ellipticity,
t / T * 2 * Math.PI]);
} pointsAtRadius[radius] = points;
return points;
}; /* Return true if we had spent too much time */
var exceedTime = function exceedTime() {
return ((settings.abortThreshold > 0) &&
((new Date()).getTime() - escapeTime > settings.abortThreshold));
}; /* Get the deg of rotation according to settings, and luck. */
var getRotateDeg = function getRotateDeg() {
if (settings.rotateRatio === 0) {
return 0;
} if (Math.random() > settings.rotateRatio) {
return 0;
} if (rotationRange === 0) {
return minRotation;
} return minRotation + Math.random() * rotationRange;
}; var getTextInfo = function getTextInfo(word, weight, rotateDeg) {
// calculate the acutal font size
// fontSize === 0 means weightFactor function wants the text skipped,
// and size < minSize means we cannot draw the text.
var debug = false;
var fontSize = settings.weightFactor(weight);
if (fontSize <= settings.minSize) {
return false;
} // Scale factor here is to make sure fillText is not limited by
// the minium font size set by browser.
// It will always be 1 or 2n.
var mu = 1;
if (fontSize < miniumFontSize) {
mu = (function calculateScaleFactor() {
var mu = 2;
while (mu * fontSize < miniumFontSize) {
mu += 2;
}
return mu;
})();
} var fcanvas = document.createElement('canvas');
var fctx = fcanvas.getContext('2d', { willReadFrequently: true }); fctx.font = settings.fontWeight + ' ' +
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; // Estimate the dimension of the text with measureText().
var fw = fctx.measureText(word).width / mu;
var fh = Math.max(fontSize * mu,
fctx.measureText('m').width,
fctx.measureText('\uFF37').width) / mu; // Create a boundary box that is larger than our estimates,
// so text don't get cut of (it sill might)
var boxWidth = fw + fh * 2;
var boxHeight = fh * 3;
var fgw = Math.ceil(boxWidth / g);
var fgh = Math.ceil(boxHeight / g);
boxWidth = fgw * g;
boxHeight = fgh * g; // Calculate the proper offsets to make the text centered at
// the preferred position. // This is simply half of the width.
var fillTextOffsetX = - fw / 2;
// Instead of moving the box to the exact middle of the preferred
// position, for Y-offset we move 0.4 instead, so Latin alphabets look
// vertical centered.
var fillTextOffsetY = - fh * 0.4; // Calculate the actual dimension of the canvas, considering the rotation.
var cgh = Math.ceil((boxWidth * Math.abs(Math.sin(rotateDeg)) +
boxHeight * Math.abs(Math.cos(rotateDeg))) / g);
var cgw = Math.ceil((boxWidth * Math.abs(Math.cos(rotateDeg)) +
boxHeight * Math.abs(Math.sin(rotateDeg))) / g);
var width = cgw * g;
var height = cgh * g; fcanvas.setAttribute('width', width);
fcanvas.setAttribute('height', height); if (debug) {
// Attach fcanvas to the DOM
document.body.appendChild(fcanvas);
// Save it's state so that we could restore and draw the grid correctly.
fctx.save();
} // Scale the canvas with |mu|.
fctx.scale(1 / mu, 1 / mu);
fctx.translate(width * mu / 2, height * mu / 2);
fctx.rotate(- rotateDeg); // Once the width/height is set, ctx info will be reset.
// Set it again here.
fctx.font = settings.fontWeight + ' ' +
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; // Fill the text into the fcanvas.
// XXX: We cannot because textBaseline = 'top' here because
// Firefox and Chrome uses different default line-height for canvas.
// Please read https://bugzil.la/737852#c6.
// Here, we use textBaseline = 'middle' and draw the text at exactly
// 0.5 * fontSize lower.
fctx.fillStyle = '#000';
fctx.textBaseline = 'middle';
fctx.fillText(word, fillTextOffsetX * mu,
(fillTextOffsetY + fontSize * 0.5) * mu); // Get the pixels of the text
var imageData = fctx.getImageData(0, 0, width, height).data; if (exceedTime()) {
return false;
} if (debug) {
// Draw the box of the original estimation
fctx.strokeRect(fillTextOffsetX * mu,
fillTextOffsetY, fw * mu, fh * mu);
fctx.restore();
} // Read the pixels and save the information to the occupied array
var occupied = [];
var gx = cgw, gy, x, y;
var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2];
while (gx--) {
gy = cgh;
while (gy--) {
y = g;
singleGridLoop: {
while (y--) {
x = g;
while (x--) {
if (imageData[((gy * g + y) * width +
(gx * g + x)) * 4 + 3]) {
occupied.push([gx, gy]); if (gx < bounds[3]) {
bounds[3] = gx;
}
if (gx > bounds[1]) {
bounds[1] = gx;
}
if (gy < bounds[0]) {
bounds[0] = gy;
}
if (gy > bounds[2]) {
bounds[2] = gy;
} if (debug) {
fctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5);
}
break singleGridLoop;
}
}
}
if (debug) {
fctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5);
}
}
}
} if (debug) {
fctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
fctx.fillRect(bounds[3] * g,
bounds[0] * g,
(bounds[1] - bounds[3] + 1) * g,
(bounds[2] - bounds[0] + 1) * g);
} // Return information needed to create the text on the real canvas
return {
mu: mu,
occupied: occupied,
bounds: bounds,
gw: cgw,
gh: cgh,
fillTextOffsetX: fillTextOffsetX,
fillTextOffsetY: fillTextOffsetY,
fillTextWidth: fw,
fillTextHeight: fh,
fontSize: fontSize
};
}; /* Determine if there is room available in the given dimension */
var canFitText = function canFitText(gx, gy, gw, gh, occupied) {
// Go through the occupied points,
// return false if the space is not available.
var i = occupied.length;
while (i--) {
var px = gx + occupied[i][0];
var py = gy + occupied[i][1]; if (px >= ngx || py >= ngy || px < 0 || py < 0 || !grid[px][py]) {
return false;
}
}
return true;
}; /* Actually draw the text on the grid */
var drawText = function drawText(gx, gy, info, word, weight,
distance, theta, rotateDeg, attributes) { var fontSize = info.fontSize;
var color;
if (getTextColor) {
color = getTextColor(word, weight, fontSize, distance, theta);
} else {
color = settings.color;
} var dimension;
var bounds = info.bounds;
dimension = {
x: (gx + bounds[3]) * g,
y: (gy + bounds[0]) * g,
w: (bounds[1] - bounds[3] + 1) * g,
h: (bounds[2] - bounds[0] + 1) * g
}; elements.forEach(function(el) {
if (el.getContext) {
var ctx = el.getContext('2d');
var mu = info.mu; // Save the current state before messing it
ctx.save();
ctx.scale(1 / mu, 1 / mu); ctx.font = settings.fontWeight + ' ' +
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily;
ctx.fillStyle = color; // Translate the canvas position to the origin coordinate of where
// the text should be put.
ctx.translate((gx + info.gw / 2) * g * mu,
(gy + info.gh / 2) * g * mu); if (rotateDeg !== 0) {
ctx.rotate(- rotateDeg);
} // Finally, fill the text. // XXX: We cannot because textBaseline = 'top' here because
// Firefox and Chrome uses different default line-height for canvas.
// Please read https://bugzil.la/737852#c6.
// Here, we use textBaseline = 'middle' and draw the text at exactly
// 0.5 * fontSize lower.
ctx.textBaseline = 'middle';
ctx.fillText(word, info.fillTextOffsetX * mu,
(info.fillTextOffsetY + fontSize * 0.5) * mu); // The below box is always matches how <span>s are positioned
/* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY,
info.fillTextWidth, info.fillTextHeight); */ // Restore the state.
ctx.restore();
} else {
// drawText on DIV element
var span = document.createElement('span');
var transformRule = '';
transformRule = 'rotate(' + (- rotateDeg / Math.PI * 180) + 'deg) ';
if (info.mu !== 1) {
transformRule +=
'translateX(-' + (info.fillTextWidth / 4) + 'px) ' +
'scale(' + (1 / info.mu) + ')';
}
var styleRules = {
'position': 'absolute',
'display': 'block',
'font': settings.fontWeight + ' ' +
(fontSize * info.mu) + 'px ' + settings.fontFamily,
'left': ((gx + info.gw / 2) * g + info.fillTextOffsetX) + 'px',
'top': ((gy + info.gh / 2) * g + info.fillTextOffsetY) + 'px',
'width': info.fillTextWidth + 'px',
'height': info.fillTextHeight + 'px',
'color': color,
'lineHeight': fontSize + 'px',
'whiteSpace': 'nowrap',
'transform': transformRule,
'webkitTransform': transformRule,
'msTransform': transformRule,
'transformOrigin': '50% 40%',
'webkitTransformOrigin': '50% 40%',
'msTransformOrigin': '50% 40%'
};
span.textContent = word;
for (var cssProp in styleRules) {
span.style[cssProp] = styleRules[cssProp];
}
if (attributes) {
for (var attribute in attributes) {
span.setAttribute(attribute, attributes[attribute]);
}
}
el.appendChild(span);
}
});
}; /* Help function to updateGrid */
var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) {
if (x >= ngx || y >= ngy || x < 0 || y < 0) {
return;
} grid[x][y] = false; if (drawMask) {
var ctx = elements[0].getContext('2d');
ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth);
} if (interactive) {
infoGrid[x][y] = { item: item, dimension: dimension };
}
}; /* Update the filling information of the given space with occupied points.
Draw the mask on the canvas if necessary. */
var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) {
var occupied = info.occupied;
var drawMask = settings.drawMask;
var ctx;
if (drawMask) {
ctx = elements[0].getContext('2d');
ctx.save();
ctx.fillStyle = settings.maskColor;
} var dimension;
if (interactive) {
var bounds = info.bounds;
dimension = {
x: (gx + bounds[3]) * g,
y: (gy + bounds[0]) * g,
w: (bounds[1] - bounds[3] + 1) * g,
h: (bounds[2] - bounds[0] + 1) * g
};
} var i = occupied.length;
while (i--) {
fillGridAt(gx + occupied[i][0], gy + occupied[i][1],
drawMask, dimension, item);
} if (drawMask) {
ctx.restore();
}
}; /* putWord() processes each item on the list,
calculate it's size and determine it's position, and actually
put it on the canvas. */
var putWord = function putWord(item) {
var word, weight, attributes;
if (Array.isArray(item)) {
word = item[0];
weight = item[1];
} else {
word = item.word;
weight = item.weight;
attributes = item.attributes;
}
var rotateDeg = getRotateDeg(); // get info needed to put the text onto the canvas
var info = getTextInfo(word, weight, rotateDeg); // not getting the info means we shouldn't be drawing this one.
if (!info) {
return false;
} if (exceedTime()) {
return false;
} // Skip the loop if we have already know the bounding box of
// word is larger than the canvas.
var bounds = info.bounds;
if ((bounds[1] - bounds[3] + 1) > ngx ||
(bounds[2] - bounds[0] + 1) > ngy) {
return false;
} // Determine the position to put the text by
// start looking for the nearest points
var r = maxRadius + 1; var tryToPutWordAtPoint = function(gxy) {
var gx = Math.floor(gxy[0] - info.gw / 2);
var gy = Math.floor(gxy[1] - info.gh / 2);
var gw = info.gw;
var gh = info.gh; // If we cannot fit the text at this position, return false
// and go to the next position.
if (!canFitText(gx, gy, gw, gh, info.occupied)) {
return false;
} // Actually put the text on the canvas
drawText(gx, gy, info, word, weight,
(maxRadius - r), gxy[2], rotateDeg, attributes); // Mark the spaces on the grid as filled
updateGrid(gx, gy, gw, gh, info, item); // Return true so some() will stop and also return true.
return true;
}; while (r--) {
var points = getPointsAtRadius(maxRadius - r); if (settings.shuffle) {
points = [].concat(points);
shuffleArray(points);
} // Try to fit the words by looking at each point.
// array.some() will stop and return true
// when putWordAtPoint() returns true.
// If all the points returns false, array.some() returns false.
var drawn = points.some(tryToPutWordAtPoint); if (drawn) {
// leave putWord() and return true
return true;
}
}
// we tried all distances but text won't fit, return false
return false;
}; /* Send DOM event to all elements. Will stop sending event and return
if the previous one is canceled (for cancelable events). */
var sendEvent = function sendEvent(type, cancelable, detail) {
if (cancelable) {
return !elements.some(function(el) {
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(type, true, cancelable, detail || {});
return !el.dispatchEvent(evt);
}, this);
} else {
elements.forEach(function(el) {
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(type, true, cancelable, detail || {});
el.dispatchEvent(evt);
}, this);
}
}; /* Start drawing on a canvas */
var start = function start() {
// For dimensions, clearCanvas etc.,
// we only care about the first element.
var canvas = elements[0]; if (canvas.getContext) {
ngx = Math.floor(canvas.width / g);
ngy = Math.floor(canvas.height / g);
} else {
var rect = canvas.getBoundingClientRect();
ngx = Math.floor(rect.width / g);
ngy = Math.floor(rect.height / g);
} // Sending a wordcloudstart event which cause the previous loop to stop.
// Do nothing if the event is canceled.
if (!sendEvent('wordcloudstart', true)) {
return;
} // Determine the center of the word cloud
center = (settings.origin) ?
[settings.origin[0]/g, settings.origin[1]/g] :
[ngx / 2, ngy / 2]; // Maxium radius to look for space
maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy)); /* Clear the canvas only if the clearCanvas is set,
if not, update the grid to the current canvas state */
grid = []; var gx, gy, i;
if (!canvas.getContext || settings.clearCanvas) {
elements.forEach(function(el) {
if (el.getContext) {
var ctx = el.getContext('2d');
ctx.fillStyle = settings.backgroundColor;
ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1));
ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1));
} else {
el.textContent = '';
el.style.backgroundColor = settings.backgroundColor;
}
}); /* fill the grid with empty state */
gx = ngx;
while (gx--) {
grid[gx] = [];
gy = ngy;
while (gy--) {
grid[gx][gy] = true;
}
}
} else {
/* Determine bgPixel by creating
another canvas and fill the specified background color. */
var bctx = document.createElement('canvas').getContext('2d'); bctx.fillStyle = settings.backgroundColor;
bctx.fillRect(0, 0, 1, 1);
var bgPixel = bctx.getImageData(0, 0, 1, 1).data; /* Read back the pixels of the canvas we got to tell which part of the
canvas is empty.
(no clearCanvas only works with a canvas, not divs) */
var imageData =
canvas.getContext('2d').getImageData(0, 0, ngx * g, ngy * g).data; gx = ngx;
var x, y;
while (gx--) {
grid[gx] = [];
gy = ngy;
while (gy--) {
y = g;
singleGridLoop: while (y--) {
x = g;
while (x--) {
i = 4;
while (i--) {
if (imageData[((gy * g + y) * ngx * g +
(gx * g + x)) * 4 + i] !== bgPixel[i]) {
grid[gx][gy] = false;
break singleGridLoop;
}
}
}
}
if (grid[gx][gy] !== false) {
grid[gx][gy] = true;
}
}
} imageData = bctx = bgPixel = undefined;
} // fill the infoGrid with empty state if we need it
if (settings.hover || settings.click) { interactive = true; /* fill the grid with empty state */
gx = ngx + 1;
while (gx--) {
infoGrid[gx] = [];
} if (settings.hover) {
canvas.addEventListener('mousemove', wordcloudhover);
} if (settings.click) {
canvas.addEventListener('click', wordcloudclick);
} canvas.addEventListener('wordcloudstart', function stopInteraction() {
canvas.removeEventListener('wordcloudstart', stopInteraction); canvas.removeEventListener('mousemove', wordcloudhover);
canvas.removeEventListener('click', wordcloudclick);
hovered = undefined;
});
} i = 0;
var loopingFunction, stoppingFunction;
if (settings.wait !== 0) {
loopingFunction = window.setTimeout;
stoppingFunction = window.clearTimeout;
} else {
loopingFunction = window.setImmediate;
stoppingFunction = window.clearImmediate;
} var addEventListener = function addEventListener(type, listener) {
elements.forEach(function(el) {
el.addEventListener(type, listener);
}, this);
}; var removeEventListener = function removeEventListener(type, listener) {
elements.forEach(function(el) {
el.removeEventListener(type, listener);
}, this);
}; var anotherWordCloudStart = function anotherWordCloudStart() {
removeEventListener('wordcloudstart', anotherWordCloudStart);
stoppingFunction(timer);
}; addEventListener('wordcloudstart', anotherWordCloudStart); var timer = loopingFunction(function loop() {
if (i >= settings.list.length) {
stoppingFunction(timer);
sendEvent('wordcloudstop', false);
removeEventListener('wordcloudstart', anotherWordCloudStart); return;
}
escapeTime = (new Date()).getTime();
var drawn = putWord(settings.list[i]);
var canceled = !sendEvent('wordclouddrawn', true, {
item: settings.list[i], drawn: drawn });
if (exceedTime() || canceled) {
stoppingFunction(timer);
settings.abort();
sendEvent('wordcloudabort', false);
sendEvent('wordcloudstop', false);
removeEventListener('wordcloudstart', anotherWordCloudStart);
return;
}
i++;
timer = loopingFunction(loop, settings.wait);
}, settings.wait);
}; // All set, start the drawing
start();
}; WordCloud.isSupported = isSupported;
WordCloud.miniumFontSize = miniumFontSize; // Expose the library as an AMD module
if (typeof global.define === 'function' && global.define.amd) {
global.define('wordcloud', [], function() { return WordCloud; });
} else {
global.WordCloud = WordCloud;
} })(window);
html5:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset='utf-8'>
<title>wordcloud2</title>
<style>
#cloud { border-radius:3px;border:1px solid #d0d0d0; }
#cloud span { cursor: pointer; }
</style>
</head>
<body>
<div id='cloud' style="width:640px;height:450px;position:relative;"></div>
<div id="details" style="width:640px;text-align:center;line-height:2em;margin-top:0.5em"></div>
<script src='wordcloud2.js'></script>
<script>
//https://github.com/timdream/wordcloud2.js/
var tags = [
["c#", 601251],
["java", 585413],
["javascript", 557407],
["php", 534590],
["android", 466436],
["jquery", 438303],
["python", 274216],
["c++", 269570],
["html", 259946],
["mysql", 226906],
["ios", 216765],
["asp.net", 209653],
["css", 199932],
["sql", 192739],
["iphone", 190382],
[".net", 179522],
["objective-c", 172609],
["ruby-on-rails", 152860],
["c", 129998],
["ruby", 97414],
["sql-server", 91050],
["ajax", 85036],
["xml", 84295],
["regex", 81991],
["arrays", 80728],
["wpf", 80062],
["asp.net-mvc", 79697],
["database", 70777],
["linux", 70772],
["json", 70396],
["django", 68893],
["vb.net", 63061],
["windows", 62042],
["xcode", 60950],
["eclipse", 60512],
["string", 54249],
["facebook", 53745],
["html5", 51015],
["ruby-on-rails-3", 50109],
["r", 49842],
["multithreading", 49806],
["winforms", 46643],
["wordpress", 46632],
["image", 45910],
["forms", 41984],
["performance", 40607],
["osx", 40401],
["visual-studio-2010", 40228],
["spring", 40207],
["node.js", 40041],
["excel", 39973],
["algorithm", 38661],
["oracle", 38565],
["swing", 38255],
["git", 37842],
["linq", 37489],
["asp.net-mvc-3", 36902],
["apache", 35533],
["web-services", 35385],
["wcf", 35242],
["perl", 35138],
["entity-framework", 34139],
["sql-server-2008", 33827],
["visual-studio", 33664],
["bash", 33139],
["hibernate", 32263],
["actionscript-3", 31760],
["ipad", 29476],
["matlab", 29210],
["qt", 28918],
["cocoa-touch", 28753],
["list", 28556],
["cocoa", 28385],
["file", 28200],
["sqlite", 28114],
[".htaccess", 28006],
["flash", 27908],
["api", 27480],
["angularjs", 27042],
["jquery-ui", 26906],
["function", 26485],
["codeigniter", 26426],
["mongodb", 26223],
["class", 25925],
["silverlight", 25878],
["tsql", 25706],
["css3", 25535],
["delphi", 25421],
["security", 25325],
["google-maps", 24919],
["vba", 24301],
["internet-explorer", 24270],
["postgresql", 24236],
["jsp", 24224],
["shell", 24184],
["google-app-engine", 23892],
["oop", 23634],
["sockets", 23464],
["validation", 23429],
["unit-testing", 23249]
]; WordCloud(document.getElementById('cloud'), {
list : tags.map(function(word) { return [word[0], Math.round(word[1]/5500)]; })
}); var clicked = function(ev) {
if (ev.target.nodeName === "SPAN") {
var tag = ev.target.textContent;
var tagElem;
if (tags.some(function(el) { if (el[0] === tag) {tagElem = el; return true;} return false; })) {
document.getElementById("details").innerHTML = "There were " + tagElem[1] + " <a href='http://www.dusystem.com/query?tag=" + tag + "&sum=" + tagElem[1] + "'>Stack Overflow questions tagged '" + tag + "'</a>"; //innerText
}
} else {
document.getElementById("details").innerHTML = "";
}
}
document.getElementById("cloud").addEventListener("click", clicked) </script>
</body>
</html>
随机样式效果:
wordcloud2.js的更多相关文章
- 词云(wordcloud2.js js2wordcloud.js)
npm安装: npm install js2wordcloud --save 用法 var wc = new Js2WordCloud(document.getElementById('contain ...
- Python3.7+jieba(结巴分词)配合Wordcloud2.js来构造网站标签云(关键词集合)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_138 其实很早以前就想搞一套完备的标签云架构了,迫于没有时间(其实就是懒),一直就没有弄出来完整的代码,说到底标签对于网站来说还是 ...
- R(八): R分词统计-老九门
分析文本内容基本的步骤:提取文本中的词语 -> 统计词语频率 -> 词频属性可视化.词频:能反映词语在文本中的重要性,一般越重要的词语,在文本中出现的次数就会越多.词云:让词语的频率属性可 ...
- Js2WordCloud 词云用法
1.引入 npm 安装: npm install js2wordcloud --save 通过script引入: <script src="dist/js2wordcloud.min. ...
- js2wordcloud 词云包的使用
js文件下载: https://github.com/liangbizhi/js2wordcloud/tree/master/ dist文件夹内 引用: <script src="** ...
- JavaScript数据可视化编程书籍上面的例子(flotr2)
先看demo再看例子 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...
- Python3.7+Django2.0.4配合Mongodb打造高性能高扩展标签云存储方案
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_141 书接上回,之前有一篇文章提到了标签云系统的构建:Python3.7+jieba(结巴分词)配合Wordcloud2.js来构 ...
- Vue.js 和 MVVM 小细节
MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自 ...
- js学习笔记:操作iframe
iframe可以说是比较老得话题了,而且网上也基本上在说少用iframe,其原因大致为:堵塞页面加载.安全问题.兼容性问题.搜索引擎抓取不到等等,不过相对于这些缺点,iframe的优点更牛,跨域请求. ...
随机推荐
- Mesos源码分析(7): Mesos-Slave的启动
Mesos-Slave的启动是从src/slave/main.cpp中的main函数开始的. 看过了Mesos-Master的启动过程,Mesos-Slave的启动没有那么复杂了. 1. ...
- 简单实用而不追求时髦的 Vim 配置
前言 由于 Vim 的广泛流行,在网络上关于 Vim 的自定义配置汗牛充栋.既有高手 Tim Pope 的极简配置 tpope/vim-sensible(这个配置一个插件都没有),也有 spf13/s ...
- 企业IT管理员IE11升级指南【9】—— IE10与IE11的功能对比
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- [Swift]LeetCode63. 不同路径 II | Unique Paths II
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...
- [Swift]LeetCode1005. K 次取反后最大化的数组和 | Maximize Sum Of Array After K Negations
Given an array A of integers, we must modify the array in the following way: we choose an i and repl ...
- Qt创建堆叠窗口
1.QT创建堆叠窗口使用类,但是使用它时主窗口不能是MainWindow,否则会出现布局错误,本例中使用基类为QDialog QStackedWidget 2.可以配合列表框QListWidget和Q ...
- ThinkPHP 数据库操作(七) : 视图查询、子查询、原生查询
视图查询 视图查询可以实现不依赖数据库视图的多表查询,并不需要数据库支持视图,例如: Db::view('User','id,name') ->view('Profile','truename, ...
- PHPExcel使用笔记
PHPExcel使用笔记 - 常见操作总结 最近做项目时,PHPExcel插件用得比较频繁,将其常见的操作总结一下- $objPHPExcel->getDefaultStyle()->ge ...
- peewee insert 数据时报错:'buffer' object has no attribute 'translate'
错误信息: "'buffer' object has no attribute 'translate'" 场景:使用peewee insert 数据时,BlobField 字段存储 ...
- leetcode — best-time-to-buy-and-sell-stock-iii
/** * Source : https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ * * * Say you h ...