wordcloud2.js
程序员文章站
2022-04-13 16:08:23
https://blogs.msdn.microsoft.com/dotnet/2019/01/10/announcing-ml-net-0-9-machine-learning-for-net/ https://marketplace.visualstudio.com/items?itemName ......
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>
随机样式效果: