UNPKG

@alicloud/cloud-charts

Version:

![](https://img.shields.io/npm/v/@alicloud/cloud-charts?color=%23ff8200)

563 lines (469 loc) 15.6 kB
import _extends from "@babel/runtime/helpers/extends"; import { merge } from '../common/common'; import tween from '../common/tween'; var uniqueId = 0; function generateUniqueId() { return "shoot-" + uniqueId++; } var PI = 2 * Math.PI; var sin = Math.sin, cos = Math.cos, atan = Math.atan, sqrt = Math.sqrt; var pow2 = function pow2(x) { return Math.pow(x, 2); }; function random() { return Math.random() * 5 + 2.5; } var Shoot = /*#__PURE__*/function () { function Shoot(canvas, getPosition, config) { this.sCtx = void 0; this.tween = void 0; this.canvas = void 0; this.getPosition = void 0; this.config = void 0; // this.uuid = generateUniqueId(); this.getPosition = getPosition; this.config = merge({ autoUpdate: true, maxFps: 60, interval: 10000, // 单次飞线总时间 dTime: 4000, // 单条飞线预计的时间 // batch: false, shootTime: { // 飞行过程中的各个时间 值域[0, 1] fromTime: 0, // 出发时间(瞬时) fromStop: 0.4, // 出发点保留时间(持续) fromFade: 0.1, // 出发点消失所用时间(持续) toBegin: 0.3, // 到达目标点的时间(瞬时) toTime: 0.1, // 到达点显示所用时间(持续) toStop: 0, // 到达点停留持续时间(持续) toFade: 0.1 // 到达点消失所用时间(持续) }, fromRadius: 3, // 出发点半径 toRadius: 3, // 到达点半径 fromBorder: 1, // 出发点边框宽度 toBorder: 1, // 到达点边框宽度 shootPointColor: { fromPoint: '46, 133, 255', // 出发点颜色 fromShadow: '46, 133, 255', // 出发点阴影颜色 toPoint: '46, 133, 255', // 到达点颜色 toShadow: '46, 133, 255' // 到达点阴影颜色 }, lineWidth: 2, // 飞线宽度 lineColor: { from: '46, 133, 255', // 线出发颜色 to: '46, 133, 255' // 线到达颜色 }, bullet: { r: 2.5, // 弹头半径 length: 20, // 弹头长度 color: 'rgb(46, 133, 255)', shadowColor: 'rgb(46, 133, 255)' }, keys: { from: 'from', to: 'to', fromValue: 'fromValue', toValue: 'toValue', curvature: 'curvature' // 曲率半径,值越大越平坦 } }, config); canvas.width = this.config.width; canvas.height = this.config.height; // 射击canvas层 this.canvas = canvas; this.sCtx = this.canvas.getContext('2d'); this.sCtx.lineWidth = this.config.lineWidth; } // 清除画布 var _proto = Shoot.prototype; _proto.clear = function clear(ctx) { var _this$config = this.config, width = _this$config.width, height = _this$config.height; ctx.clearRect(0, 0, width, height); }; _proto.changeSize = function changeSize(w, h) { this.config.width = w; this.config.height = h; this.canvas.width = w; this.canvas.height = h; // 更新uuid让动画更新 // this.uuid = generateUniqueId(); }; _proto.draw = function draw(data) { if (!data || data.length === 0) { return; } var self = this; var dTime = self.config.dTime; // 由于要保证interval时间内完成全部动画 var interval = self.config.interval; var autoUpdate = self.config.autoUpdate; var maxFps = self.config.maxFps; var times = interval / dTime >> 0; var keys = self.config.keys; var sCtx = self.sCtx; var shoots = []; var shootMap = {}; var time = self.config.shootTime; var l = data.length; var getPosition = self.getPosition; var fCo; var tCo; var s; // 先清除画布 self.clear(sCtx); for (var i = 0; i < l; i++) { var _d = data[i]; fCo = _extends({}, _d[keys.from]); tCo = _extends({}, _d[keys.to]); // 设置了 getPosition 函数,需要处理一遍 if (getPosition) { var fP = getPosition(fCo); fCo.x = fP.x; fCo.y = fP.y; var tP = getPosition(tCo); tCo.x = tP.x; tCo.y = tP.y; } // const fromCityName = d[keys.from]; // const toCityName = d[keys.to]; // // if (typeof fromCityName === 'object') { // fCo = fromCityName; // } else { // // 获取出发城市在画布上的坐标 // fCo = self.map.getCoord(fromCityName); // } // // if (typeof toCityName === 'object') { // tCo = toCityName; // } else { // // 获取到达城市在画布上的坐标 // tCo = self.map.getCoord(toCityName); // } if (fCo && tCo) { var color = {}; // 如果数据带有颜色配置 if (_d._color) { Object.assign(color, _d._color); } s = self.emit(fCo, tCo, _d, color, time); s.index = (times - 1) * Math.random(); // 判断是否是多点同时射击一个点 if (!shootMap[s.index]) { shootMap[s.index] = []; } shootMap[s.index].forEach(function (city) { if (city === tCo) { // 正在被攻击 s.shooting = true; } }); if (!s.shooting) { shootMap[s.index].push(tCo); } shoots.push(s); } } this.tween = tween(generateUniqueId(), { duration: interval, autoUpdate: autoUpdate, maxFps: maxFps }, function (t) { self.clear(self.sCtx); shoots.forEach(function (shootFunction) { shootFunction(t * times - shootFunction.index); }); }); }; _proto.emit = function emit(fCo, tCo, data, color, time) { var self = this; var keys = self.config.keys; // 发射出现时间段 var fromTime = time.fromTime; // 发射停留时间段 var fromStop = time.fromStop; // 发射消失时间段 var fromFade = time.fromFade; // 击中开始时间点 var toBegin = time.toBegin; // 击中出现时间段 var toTime = time.toTime; // 击中停留时间段 var toStop = time.toStop; // 击中消失时间段 var toFade = time.toFade; // 发射消失时间点 var fromFadeBegin = fromTime + fromStop; // 命中消失时间点 var toFadeBegin = toBegin + toTime + toStop; // 发射半径 var fr = self.config.fromRadius; var tr = self.config.toRadius; var h = data[keys.curvature] || random(); var shootDurable = self.config.shootDurable; var _s; _s = function s(t) { if (fCo) { // 出发: // 1. 出现 if (t < fromTime) { self.from(fCo, fr, color)(t / fromTime); // 2. 停留 } else if (t > fromTime && t < fromFadeBegin) { self.from(fCo, fr, color)(1); // 3. 消失 } else if (t > fromFadeBegin) { self.from(fCo, fr, color, true)((t - fromFadeBegin) / fromFade); } } if (tCo) { // 轨迹 if (t >= fromTime && t < toBegin) { // 出发 - 到达瞬间 self.track(fCo, tCo, false, color, h)((t - fromTime) / (toBegin - fromTime)); } else if (t > toBegin && t < toFadeBegin) { // 到达后停留 // TODO add by kaihong.tkh if (shootDurable) { var _time = -(t - fromTime) / (toBegin - fromTime); _time -= Math.floor(_time); _time = 1 - _time; self.track(fCo, tCo, true, color, h)(_time); } else { self.track(fCo, tCo, true, color, h)(0); } } else if (t > toFadeBegin && t < toFadeBegin + toFade) { // 停留后消失时间 self.track(fCo, tCo, true, color, h)((t - toFadeBegin) / toFade); } // 如果不是正在被射击 if (!_s.shooting) { // 到达: // 1. 放大 if (t >= toBegin && t < toBegin + toTime) { if (!_s.to) { _s.to = true; } self.to(tCo, tr, color)((t - toBegin) / toTime); // 2. 停留 } else if (t > toBegin + toTime && t < toFadeBegin) { self.to(tCo, tr, color)(1); // 3. 消失 } else if (t >= toFadeBegin) { self.to(tCo, tr, color, true, 3)((t - toFadeBegin) / toFade); } } } }; return _s; } // CHECK zoom 没有传入 ; _proto.from = function from(co, r, color, zoom) { var self = this; var c = "rgba(" + (color.fColor || this.config.shootPointColor.fromPoint) + ","; var b = self.config.fromBorder; var sCtx = self.sCtx; return function (t) { if (t > 1 || t < 0) { return; } if (zoom) { t = 1 - t; } sCtx.save(); // 画背景圆 sCtx.beginPath(); sCtx.strokeStyle = c + t + ")"; sCtx.lineWidth = b * t; sCtx.fillStyle = c + "0.3)"; // shadow sCtx.shadowColor = "rgba(" + (color.fColor || self.config.shootPointColor.fromShadow) + ",1)"; sCtx.shadowBlur = 5; sCtx.shadowOffsetX = 0; sCtx.shadowOffsetY = 0; sCtx.arc(co.x, co.y, r * t, 0, PI); sCtx.fill(); sCtx.stroke(); // 画中心圆 sCtx.beginPath(); sCtx.fillStyle = c + "1)"; sCtx.arc(co.x, co.y, 2 * t, 0, PI); sCtx.fill(); sCtx.restore(); }; } // CHECK anticlockwise 没有传入 ; _proto.to = function to(co, r, color, zoom, n, anticlockwise) { var self = this; var c = "rgba(" + (color.tColor || this.config.shootPointColor.toPoint) + ","; var b = self.config.toBorder; var sCtx = self.sCtx; return function (t) { var rad = 0; if (t > 1 || t < 0) { return; } sCtx.save(); // 每次转的角度 if (n) { rad = n * PI * t; if (anticlockwise) { rad = -rad; } } if (zoom) { t = 1 - t; } // 画背景圆 sCtx.beginPath(); sCtx.fillStyle = c + "0.3)"; sCtx.arc(co.x, co.y, r * t, 0, PI); sCtx.fill(); // 画离散弧线 sCtx.beginPath(); sCtx.strokeStyle = c + t + ")"; sCtx.lineWidth = b * t; // shadow sCtx.shadowColor = "rgba(" + (color.tColor || self.config.shootPointColor.toShadow) + ",1)"; sCtx.shadowBlur = 10; sCtx.shadowOffsetX = 0; sCtx.shadowOffsetY = 0; sCtx.arc(co.x, co.y, r * t, rad, PI / 6 + rad); sCtx.moveTo(co.x + r * cos(PI / 3 + rad) * t, co.y + r * sin(PI / 3 + rad) * t); sCtx.arc(co.x, co.y, r * t, PI / 3 + rad, PI / 2 + rad); sCtx.moveTo(co.x + r * cos(PI * 2 / 3 + rad) * t, co.y + r * sin(PI * 2 / 3 + rad) * t); sCtx.arc(co.x, co.y, r * t, PI * 2 / 3 + rad, PI * 5 / 6 + rad); sCtx.stroke(); // 画中心圆 sCtx.beginPath(); sCtx.fillStyle = c + "1)"; sCtx.arc(co.x, co.y, 2 * t, 0, PI); sCtx.fill(); sCtx.restore(); }; } // CHECK overview 没有传入 ; _proto.track = function track(fCo, tCo, fade, color, h, overview) { var self = this; var ctx = self.sCtx; var fColor = "rgba(" + (color.fColor || self.config.lineColor.from) + ","; var tColor = "rgba(" + (color.tColor || self.config.lineColor.to) + ","; // (x1, y1) 出发点,(x2, y2) 到达点 var x1 = fCo.x; var y1 = fCo.y; var x2 = tCo.x; var y2 = tCo.y; // 求法线方程 // y = j * x + k var dx = (x1 + x2) / 2; var dy = (y1 + y2) / 2; var j = (x1 - x2) / (y2 - y1); var k = dy - j * dx; // d用来控制弧线的弧度 var d = sqrt(pow2(x1 - x2) + pow2(y1 - y2)) / h; var rad = atan(j); var cx = d * cos(rad); // 渐变 var gradient = ctx.createLinearGradient(x1, y1, x2, y2); var bulletR = this.config.bullet.r; var bulletLen = this.config.bullet.length; var shootDurable = self.config.shootDurable; // 控制点坐标 var x3 = (x1 + x2) / 2 + cx; var y3 = j * x3 + k; if (isNaN(j) || j > 10) { // 水平方向 x3 = dx; y3 = dy - h - dx / 20; } else if (Math.abs(j) < 0.1) { // 竖直方向 x3 = y1 < y2 ? dx + h + dy / 20 : dx - h - dy / 20; y3 = dy; } else if (Math.abs(j) >= 1) { // 之后的两个条件判断请画象限图理解。。。估计明天我也忘记为什么要这么写了 y3 = dy - cx; x3 = (y3 - k) / j; } else if (j > 0) { x3 = (x1 + x2) / 2 - cx; y3 = j * x3 + k; } return function (t) { // 移动点坐标 var x0; var y0; // 贝塞尔曲线切线斜率 var kx; var ky; // 蒙板起始坐标 var rx; var ry; // 蒙板半径 var r; var gradientOpacity = 0.7; if (t > 1 || t < 0) { return; } // TODO:最好加上一个透明度变化的动画 if (!overview) { if (fade) { // 避免出现科学计数法,rgba中的透明值不能设为科学计数法 gradientOpacity = 1 - t < 0.01 ? 0.01 : 1 - t; } else { gradientOpacity = t < 0.01 ? 0.01 : t; } if (shootDurable) { gradientOpacity = 1; // add by kaihong.tkh 线不需要渐变 } } // 贝塞尔曲线方程 x0 = (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * x3 + t * t * x2; y0 = (1 - t) * (1 - t) * y1 + 2 * t * (1 - t) * y3 + t * t * y2; // 贝塞尔曲线切线方程 kx = -2 * x1 * (1 - t) + 2 * x3 * (1 - 2 * t) + 2 * x2 * t; ky = -2 * y1 * (1 - t) + 2 * y3 * (1 - 2 * t) + 2 * y2 * t; rx = (x1 + x0) / 2; ry = (y1 + y0) / 2; r = sqrt(pow2(x1 - x0) + pow2(y1 - y0)) / 2; ctx.save(); gradient.addColorStop(0, fColor + gradientOpacity + ")"); gradient.addColorStop(1, tColor + gradientOpacity + ")"); if (!fade && !overview) { // 创建圆形蒙板 ctx.arc(rx, ry, r, 0, PI); ctx.clip(); } ctx.beginPath(); ctx.globalCompositeOperation = 'lighter'; ctx.strokeStyle = gradient; ctx.lineWidth = self.config.lineWidth; ctx.moveTo(x1, y1); ctx.quadraticCurveTo(x3, y3, x2, y2); ctx.stroke(); ctx.restore(); var a = atan(ky / kx); // 计算旋转角度 if (ky > 0 && kx < 0) { a += PI / 2; } else if (ky < 0 && kx < 0) { a -= PI / 2; } // TODO add by kaihong.tkh if (shootDurable) { self.drawBullet(x0, y0, a, color.bullet, bulletR, bulletLen); } else if (!fade && !overview) { // ky/kx 为切线斜率 self.drawBullet(x0, y0, a, color.bullet, bulletR, bulletLen); } }; }; _proto.drawBullet = function drawBullet(x, y, a, color, r, len) { var self = this; var sCtx = self.sCtx; sCtx.save(); sCtx.translate(x, y); sCtx.rotate(a); sCtx.translate(-x, -y); sCtx.beginPath(); sCtx.globalCompositeOperation = 'lighter'; // shadow sCtx.shadowColor = color || this.config.bullet.shadowColor; sCtx.shadowBlur = 20; sCtx.shadowOffsetX = 0; sCtx.shadowOffsetY = 0; sCtx.fillStyle = color || this.config.bullet.color; sCtx.arc(x, y, r, -PI / 4, PI / 4); sCtx.lineTo(x - len, y); sCtx.closePath(); sCtx.fill(); sCtx.restore(); }; _proto.update = function update(time) { if (this.tween && this.tween.update) { this.tween.update(time); } }; _proto.destroy = function destroy() { this.clear(this.sCtx); this.tween && this.tween.destroy(); }; return Shoot; }(); export default Shoot;