163 lines
5.2 KiB
JavaScript
163 lines
5.2 KiB
JavaScript
|
|
import * as echarts from 'echarts/lib/echarts';
|
||
|
|
|
||
|
|
function makeSprite(size, canvas, draw) {
|
||
|
|
// http://simonsarris.com/blog/346-how-you-clear-your-canvas-matters
|
||
|
|
// http://jsperf.com/canvasclear
|
||
|
|
// Set width and height is fast
|
||
|
|
// And use the exist canvas if possible
|
||
|
|
// http://jsperf.com/create-canvas-vs-set-width-height/2
|
||
|
|
var canvas = canvas || document.createElement('canvas');
|
||
|
|
canvas.width = size;
|
||
|
|
canvas.height = size;
|
||
|
|
var ctx = canvas.getContext('2d');
|
||
|
|
draw && draw(ctx);
|
||
|
|
return canvas;
|
||
|
|
}
|
||
|
|
|
||
|
|
function makePath(symbol, symbolSize, style, marginBias) {
|
||
|
|
if (!echarts.util.isArray(symbolSize)) {
|
||
|
|
symbolSize = [symbolSize, symbolSize];
|
||
|
|
}
|
||
|
|
|
||
|
|
var margin = spriteUtil.getMarginByStyle(style, marginBias);
|
||
|
|
var width = symbolSize[0] + margin.left + margin.right;
|
||
|
|
var height = symbolSize[1] + margin.top + margin.bottom;
|
||
|
|
var path = echarts.helper.createSymbol(symbol, 0, 0, symbolSize[0], symbolSize[1]);
|
||
|
|
var size = Math.max(width, height);
|
||
|
|
path.x = margin.left;
|
||
|
|
path.y = margin.top;
|
||
|
|
|
||
|
|
if (width > height) {
|
||
|
|
path.y += (size - height) / 2;
|
||
|
|
} else {
|
||
|
|
path.x += (size - width) / 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
var rect = path.getBoundingRect();
|
||
|
|
path.x -= rect.x;
|
||
|
|
path.y -= rect.y;
|
||
|
|
path.setStyle(style);
|
||
|
|
path.update();
|
||
|
|
path.__size = size;
|
||
|
|
return path;
|
||
|
|
} // http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf
|
||
|
|
|
||
|
|
|
||
|
|
function generateSDF(ctx, sourceImageData, range) {
|
||
|
|
var sourceWidth = sourceImageData.width;
|
||
|
|
var sourceHeight = sourceImageData.height;
|
||
|
|
var width = ctx.canvas.width;
|
||
|
|
var height = ctx.canvas.height;
|
||
|
|
var scaleX = sourceWidth / width;
|
||
|
|
var scaleY = sourceHeight / height;
|
||
|
|
|
||
|
|
function sign(r) {
|
||
|
|
return r < 128 ? 1 : -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
function searchMinDistance(x, y) {
|
||
|
|
var minDistSqr = Infinity;
|
||
|
|
x = Math.floor(x * scaleX);
|
||
|
|
y = Math.floor(y * scaleY);
|
||
|
|
var i = y * sourceWidth + x;
|
||
|
|
var r = sourceImageData.data[i * 4];
|
||
|
|
var a = sign(r); // Search for min distance
|
||
|
|
|
||
|
|
for (var y2 = Math.max(y - range, 0); y2 < Math.min(y + range, sourceHeight); y2++) {
|
||
|
|
for (var x2 = Math.max(x - range, 0); x2 < Math.min(x + range, sourceWidth); x2++) {
|
||
|
|
var i = y2 * sourceWidth + x2;
|
||
|
|
var r2 = sourceImageData.data[i * 4];
|
||
|
|
var b = sign(r2);
|
||
|
|
var dx = x2 - x;
|
||
|
|
var dy = y2 - y;
|
||
|
|
|
||
|
|
if (a !== b) {
|
||
|
|
var distSqr = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (distSqr < minDistSqr) {
|
||
|
|
minDistSqr = distSqr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return a * Math.sqrt(minDistSqr);
|
||
|
|
}
|
||
|
|
|
||
|
|
var sdfImageData = ctx.createImageData(width, height);
|
||
|
|
|
||
|
|
for (var y = 0; y < height; y++) {
|
||
|
|
for (var x = 0; x < width; x++) {
|
||
|
|
var dist = searchMinDistance(x, y);
|
||
|
|
var normalized = dist / range * 0.5 + 0.5;
|
||
|
|
var i = (y * width + x) * 4;
|
||
|
|
sdfImageData.data[i++] = (1.0 - normalized) * 255;
|
||
|
|
sdfImageData.data[i++] = (1.0 - normalized) * 255;
|
||
|
|
sdfImageData.data[i++] = (1.0 - normalized) * 255;
|
||
|
|
sdfImageData.data[i++] = 255;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return sdfImageData;
|
||
|
|
}
|
||
|
|
|
||
|
|
var spriteUtil = {
|
||
|
|
getMarginByStyle: function (style) {
|
||
|
|
var minMargin = style.minMargin || 0;
|
||
|
|
var lineWidth = 0;
|
||
|
|
|
||
|
|
if (style.stroke && style.stroke !== 'none') {
|
||
|
|
lineWidth = style.lineWidth == null ? 1 : style.lineWidth;
|
||
|
|
}
|
||
|
|
|
||
|
|
var shadowBlurSize = style.shadowBlur || 0;
|
||
|
|
var shadowOffsetX = style.shadowOffsetX || 0;
|
||
|
|
var shadowOffsetY = style.shadowOffsetY || 0;
|
||
|
|
var margin = {};
|
||
|
|
margin.left = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize, minMargin);
|
||
|
|
margin.right = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize, minMargin);
|
||
|
|
margin.top = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize, minMargin);
|
||
|
|
margin.bottom = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize, minMargin);
|
||
|
|
return margin;
|
||
|
|
},
|
||
|
|
// TODO Not consider shadowOffsetX, shadowOffsetY.
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {string} symbol
|
||
|
|
* @param {number | Array.<number>} symbolSize
|
||
|
|
* @param {Object} style
|
||
|
|
*/
|
||
|
|
createSymbolSprite: function (symbol, symbolSize, style, canvas) {
|
||
|
|
var path = makePath(symbol, symbolSize, style);
|
||
|
|
var margin = spriteUtil.getMarginByStyle(style);
|
||
|
|
return {
|
||
|
|
image: makeSprite(path.__size, canvas, function (ctx) {
|
||
|
|
echarts.innerDrawElementOnCanvas(ctx, path);
|
||
|
|
}),
|
||
|
|
margin: margin
|
||
|
|
};
|
||
|
|
},
|
||
|
|
createSDFFromCanvas: function (canvas, size, range, outCanvas) {
|
||
|
|
// TODO Create a low resolution SDF from high resolution image.
|
||
|
|
return makeSprite(size, outCanvas, function (outCtx) {
|
||
|
|
var ctx = canvas.getContext('2d');
|
||
|
|
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||
|
|
outCtx.putImageData(generateSDF(outCtx, imgData, range), 0, 0);
|
||
|
|
});
|
||
|
|
},
|
||
|
|
createSimpleSprite: function (size, canvas) {
|
||
|
|
return makeSprite(size, canvas, function (ctx) {
|
||
|
|
var halfSize = size / 2;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.arc(halfSize, halfSize, 60, 0, Math.PI * 2, false);
|
||
|
|
ctx.closePath();
|
||
|
|
var gradient = ctx.createRadialGradient(halfSize, halfSize, 0, halfSize, halfSize, halfSize);
|
||
|
|
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
|
||
|
|
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
|
||
|
|
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
||
|
|
ctx.fillStyle = gradient;
|
||
|
|
ctx.fill();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
export default spriteUtil;
|