@antv/g2plot
Version:
G2 Plot, a market of plots built with the Grammar of Graphics'
259 lines • 8.25 kB
JavaScript
import { Circle } from '@antv/g';
import { Global, registerShape } from '@antv/g2';
import * as _ from '@antv/util';
var ShapeUtil = {
splitPoints: function (obj) {
var points = [];
var x = obj.x;
var y = obj.y;
y = _.isArray(y) ? y : [y];
_.each(y, function (yItem, index) {
var point = {
x: _.isArray(x) ? x[index] : x,
y: yItem,
};
points.push(point);
});
return points;
},
addFillAttrs: function (attrs, cfg) {
if (cfg.color && !attrs.fill) {
attrs.fill = cfg.color;
}
if (_.isNumber(cfg.opacity)) {
attrs.opacity = attrs.fillOpacity = cfg.opacity;
}
},
addStrokeAttrs: function (attrs, cfg) {
if (cfg.color && !attrs.stroke) {
attrs.stroke = cfg.color;
}
if (_.isNumber(cfg.opacity)) {
attrs.opacity = attrs.strokeOpacity = cfg.opacity;
}
},
};
var ValueUtil = {
lerp: function (a, b, factor) {
return (1 - factor) * a + factor * b;
},
};
var getFillAttrs = function (cfg) {
var defaultAttrs = Global.theme.shape.interval;
var attrs = _.mix({}, defaultAttrs, cfg.style);
ShapeUtil.addFillAttrs(attrs, cfg);
if (cfg.color && !attrs.stroke) {
attrs.stroke = attrs.stroke || cfg.color;
}
return attrs;
};
var getLineAttrs = function (cfg) {
var defaultAttrs = Global.theme.shape.hollowInterval;
var attrs = _.mix({}, defaultAttrs, cfg.style);
ShapeUtil.addStrokeAttrs(attrs, cfg);
return attrs;
};
/**
* 用贝塞尔曲线模拟正弦波
* 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
* @return {Array} 正弦片段曲线
*/
function getWaterWavePositions(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],
];
}
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],
];
}
if (stage === 2) {
return [
[x + ((1 / 2) * waveLength) / Math.PI / 2, -amplitude / 2],
[x + ((1 / 2) * waveLength) / Math.PI, -amplitude],
[x + waveLength / 4, -amplitude],
];
}
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],
];
}
/**
* 获取水波路径
* @param {number} radius 半径
* @param {number} waterLevel 水位
* @param {number} waveLength 波长
* @param {number} phase 相位
* @param {number} amplitude 震幅
* @param {number} cx 圆心x
* @param {number} cy 圆心y
* @return {Array} path 路径
* @reference http://gitlab.alipay-inc.com/datavis/g6/blob/1.2.0/src/graph/utils/path.js#L135
*/
function getWaterWavePath(radius, waterLevel, waveLength, phase, amplitude, cx, cy) {
var curves = Math.ceil(((2 * radius) / waveLength) * 4) * 2;
var path = [];
var _phase = phase;
// map phase to [-Math.PI * 2, 0]
while (_phase < -Math.PI * 2) {
_phase += Math.PI * 2;
}
while (_phase > 0) {
_phase -= Math.PI * 2;
}
_phase = (_phase / Math.PI / 2) * waveLength;
var left = cx - radius + _phase - radius * 2;
/**
* top-left corner as start point
*
* draws this point
* |
* \|/
* ~~~~~~~~
* | |
* +------+
*/
path.push(['M', left, waterLevel]);
/**
* top wave
*
* ~~~~~~~~ <- draws this sine wave
* | |
* +------+
*/
var waveRight = 0;
for (var c = 0; c < curves; ++c) {
var stage = c % 4;
var pos = getWaterWavePositions((c * waveLength) / 4, stage, waveLength, amplitude);
path.push([
'C',
pos[0][0] + left,
-pos[0][1] + waterLevel,
pos[1][0] + left,
-pos[1][1] + waterLevel,
pos[2][0] + left,
-pos[2][1] + waterLevel,
]);
if (c === curves - 1) {
waveRight = pos[2][0];
}
}
/**
* top-right corner
*
* ~~~~~~~~
* 3. draws this line -> | | <- 1. draws this line
* +------+
* ^
* |
* 2. draws this line
*/
path.push(['L', waveRight + left, cy + radius]);
path.push(['L', left, cy + radius]);
path.push(['L', left, waterLevel]);
return path;
}
/**
* 添加水波
* @param {number} x 中心x
* @param {number} y 中心y
* @param {number} level 水位等级 0~1
* @param {number} waveCount 水波数
* @param {number} colors 色值
* @param {number} group 图组
* @param {number} clip 用于剪切的图形
* @param {number} radius 绘制图形的高度
*/
function addWaterWave(x, y, level, waveCount, color, group, clip, radius) {
var bbox = clip.getBBox();
var width = bbox.maxX - bbox.minX;
var height = bbox.maxY - bbox.minY;
var duration = 5000;
for (var i = 0; i < waveCount; i++) {
var factor = waveCount <= 1 ? 0 : i / (waveCount - 1);
var wave = group.addShape('path', {
attrs: {
path: getWaterWavePath(radius, bbox.minY + height * level, width / 4, 0, width / ValueUtil.lerp(56, 64, factor), x, y),
fill: color,
clip: clip,
opacity: ValueUtil.lerp(0.6, 0.3, factor),
},
});
// FIXME wave animation error in svg
if (Global.renderer === 'canvas') {
wave.animate({
transform: [['t', width / 2, 0]],
repeat: true,
}, ValueUtil.lerp(duration, 0.7 * duration, factor));
}
}
}
registerShape('interval', 'liquid-fill-gauge', {
draw: function (cfg, container) {
var cy = 0.5;
var sumX = 0;
var minX = Infinity;
_.each(cfg.points, function (p) {
if (p.x < minX) {
minX = p.x;
}
sumX += p.x;
});
var cx = sumX / cfg.points.length;
var cp = this.parsePoint({ x: cx, y: cy });
var minP = this.parsePoint({ x: minX, y: 0.5 });
var xWidth = cp.x - minP.x;
var radius = Math.min(xWidth, minP.y);
var fill = getFillAttrs(cfg).fill;
var clipCircle = new Circle({
attrs: {
x: cp.x,
y: cp.y,
r: radius,
},
});
var waves = container.addGroup({
name: 'waves',
attrs: {
x: cp.x,
y: cp.y,
clip: clipCircle,
},
});
addWaterWave(cp.x, cp.y, 1 - cfg.points[1].y, // cfg.y / (2 * cp.y),
3, fill, waves, clipCircle, radius * 4);
return container.addShape('circle', {
name: 'wrap',
attrs: _.mix(getLineAttrs(cfg), {
x: cp.x,
y: cp.y,
r: radius,
fill: 'transparent',
}),
});
},
});
//# sourceMappingURL=liquid.js.map