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] ] } }