164 lines
4.9 KiB
JavaScript
164 lines
4.9 KiB
JavaScript
import * as echarts from 'echarts/lib/echarts';
|
|
|
|
export default echarts.graphic.extendShape({
|
|
type: 'ec-liquid-fill',
|
|
|
|
shape: {
|
|
waveLength: 0,
|
|
radius: 0,
|
|
radiusY: 0,
|
|
cx: 0,
|
|
cy: 0,
|
|
waterLevel: 0,
|
|
amplitude: 0,
|
|
phase: 0,
|
|
inverse: false
|
|
},
|
|
|
|
buildPath: function (ctx, shape) {
|
|
if (shape.radiusY == null) {
|
|
shape.radiusY = shape.radius;
|
|
}
|
|
|
|
/**
|
|
* We define a sine wave having 4 waves, and make sure at least 8 curves
|
|
* is drawn. Otherwise, it may cause blank area for some waves when
|
|
* wave length is large enough.
|
|
*/
|
|
var curves = Math.max(
|
|
Math.ceil(2 * shape.radius / shape.waveLength * 4) * 2,
|
|
8
|
|
);
|
|
|
|
// map phase to [-Math.PI * 2, 0]
|
|
while (shape.phase < -Math.PI * 2) {
|
|
shape.phase += Math.PI * 2;
|
|
}
|
|
while (shape.phase > 0) {
|
|
shape.phase -= Math.PI * 2;
|
|
}
|
|
var phase = shape.phase / Math.PI / 2 * shape.waveLength;
|
|
|
|
var left = shape.cx - shape.radius + phase - shape.radius * 2;
|
|
|
|
/**
|
|
* top-left corner as start point
|
|
*
|
|
* draws this point
|
|
* |
|
|
* \|/
|
|
* ~~~~~~~~
|
|
* | |
|
|
* +------+
|
|
*/
|
|
ctx.moveTo(left, shape.waterLevel);
|
|
|
|
/**
|
|
* top wave
|
|
*
|
|
* ~~~~~~~~ <- draws this sine wave
|
|
* | |
|
|
* +------+
|
|
*/
|
|
var waveRight = 0;
|
|
for (var c = 0; c < curves; ++c) {
|
|
var stage = c % 4;
|
|
var pos = getWaterPositions(c * shape.waveLength / 4, stage,
|
|
shape.waveLength, shape.amplitude);
|
|
ctx.bezierCurveTo(pos[0][0] + left, -pos[0][1] + shape.waterLevel,
|
|
pos[1][0] + left, -pos[1][1] + shape.waterLevel,
|
|
pos[2][0] + left, -pos[2][1] + shape.waterLevel);
|
|
|
|
if (c === curves - 1) {
|
|
waveRight = pos[2][0];
|
|
}
|
|
}
|
|
|
|
if (shape.inverse) {
|
|
/**
|
|
* top-right corner
|
|
* 2. draws this line
|
|
* |
|
|
* +------+
|
|
* 3. draws this line -> | | <- 1. draws this line
|
|
* ~~~~~~~~
|
|
*/
|
|
ctx.lineTo(waveRight + left, shape.cy - shape.radiusY);
|
|
ctx.lineTo(left, shape.cy - shape.radiusY);
|
|
ctx.lineTo(left, shape.waterLevel);
|
|
}
|
|
else {
|
|
/**
|
|
* top-right corner
|
|
*
|
|
* ~~~~~~~~
|
|
* 3. draws this line -> | | <- 1. draws this line
|
|
* +------+
|
|
* ^
|
|
* |
|
|
* 2. draws this line
|
|
*/
|
|
ctx.lineTo(waveRight + left, shape.cy + shape.radiusY);
|
|
ctx.lineTo(left, shape.cy + shape.radiusY);
|
|
ctx.lineTo(left, shape.waterLevel);
|
|
}
|
|
|
|
ctx.closePath();
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* Using Bezier curves to fit sine wave.
|
|
* There is 4 control points for each curve of wave,
|
|
* which is at 1/4 wave length of the sine wave.
|
|
*
|
|
* The control points for a wave from (a) to (d) are a-b-c-d:
|
|
* c *----* d
|
|
* b *
|
|
* |
|
|
* ... a * ..................
|
|
*
|
|
* whose positions are a: (0, 0), b: (0.5, 0.5), c: (1, 1), d: (PI / 2, 1)
|
|
*
|
|
* @param {number} x x position of the left-most point (a)
|
|
* @param {number} stage 0-3, stating which part of the wave it is
|
|
* @param {number} waveLength wave length of the sine wave
|
|
* @param {number} amplitude wave amplitude
|
|
*/
|
|
function getWaterPositions(x, stage, waveLength, amplitude) {
|
|
if (stage === 0) {
|
|
return [
|
|
[x + 1 / 2 * waveLength / Math.PI / 2, amplitude / 2],
|
|
[x + 1 / 2 * waveLength / Math.PI, amplitude],
|
|
[x + waveLength / 4, amplitude]
|
|
];
|
|
}
|
|
else if (stage === 1) {
|
|
return [
|
|
[x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 2),
|
|
amplitude],
|
|
[x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 1),
|
|
amplitude / 2],
|
|
[x + waveLength / 4, 0]
|
|
]
|
|
}
|
|
else if (stage === 2) {
|
|
return [
|
|
[x + 1 / 2 * waveLength / Math.PI / 2, -amplitude / 2],
|
|
[x + 1 / 2 * waveLength / Math.PI, -amplitude],
|
|
[x + waveLength / 4, -amplitude]
|
|
]
|
|
}
|
|
else {
|
|
return [
|
|
[x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 2),
|
|
-amplitude],
|
|
[x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 1),
|
|
-amplitude / 2],
|
|
[x + waveLength / 4, 0]
|
|
]
|
|
}
|
|
}
|