270 lines
8.9 KiB
JavaScript
270 lines
8.9 KiB
JavaScript
(function (global, factory) {
|
||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||
typeof define === 'function' && define.amd ? define(factory) :
|
||
(global.MoveRiver = factory());
|
||
}(this, (function () { 'use strict';
|
||
|
||
var tool = {
|
||
merge: function merge(settings, defaults) {
|
||
Object.keys(settings).forEach(function (key) {
|
||
defaults[key] = settings[key];
|
||
});
|
||
},
|
||
//计算两点间距离
|
||
getDistance: function getDistance(p1, p2) {
|
||
return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));
|
||
},
|
||
//判断点是否在线段上
|
||
containStroke: function containStroke(x0, y0, x1, y1, lineWidth, x, y) {
|
||
if (lineWidth === 0) {
|
||
return false;
|
||
}
|
||
var _l = lineWidth;
|
||
var _a = 0;
|
||
var _b = x0;
|
||
// Quick reject
|
||
if (y > y0 + _l && y > y1 + _l || y < y0 - _l && y < y1 - _l || x > x0 + _l && x > x1 + _l || x < x0 - _l && x < x1 - _l) {
|
||
return false;
|
||
}
|
||
|
||
if (x0 !== x1) {
|
||
_a = (y0 - y1) / (x0 - x1);
|
||
_b = (x0 * y1 - x1 * y0) / (x0 - x1);
|
||
} else {
|
||
return Math.abs(x - x0) <= _l / 2;
|
||
}
|
||
var tmp = _a * x - y + _b;
|
||
var _s = tmp * tmp / (_a * _a + 1);
|
||
return _s <= _l / 2 * _l / 2;
|
||
}
|
||
};
|
||
|
||
var resolutionScale = function (context) {
|
||
var devicePixelRatio = window.devicePixelRatio || 1;
|
||
context.canvas.width = context.canvas.width * devicePixelRatio;
|
||
context.canvas.height = context.canvas.height * devicePixelRatio;
|
||
context.canvas.style.width = context.canvas.width / devicePixelRatio + 'px';
|
||
context.canvas.style.height = context.canvas.height / devicePixelRatio + 'px';
|
||
context.scale(devicePixelRatio, devicePixelRatio);
|
||
};
|
||
|
||
var global = typeof window === 'undefined' ? {} : window;
|
||
|
||
var cancelAnimationFrame = global.cancelAnimationFrame || global.mozCancelAnimationFrame || global.webkitCancelAnimationFrame || global.msCancelAnimationFrame || function (id) {
|
||
clearTimeout(id);
|
||
};
|
||
|
||
var MoveRiver = function MoveRiver(map, userOptions) {
|
||
var self = this;
|
||
self.map = map;
|
||
|
||
//默认参数
|
||
var options = {
|
||
lineWidth: 0.5, //线条宽度
|
||
lineStyle: '#C82800', //线条颜色
|
||
animateLineWidth: 1, //动画线条宽度
|
||
animateLineStyle: '#ffff00', //动画线条颜色
|
||
// colors: ["#516b91", "#59c4e6", "#edafda", "#93b7e3", "#a5e7f0", "#cbb0e3"]
|
||
colors: ["#c1232b", "#27727b", "#fcce10", "#e87c25", "#b5c334", "#fe8463", "#9bca63", "#fad860", "#f3a43b", "#60c0dd", "#d7504b", "#c6e579", "#f4e001", "#f0805a", "#26c0c0"]
|
||
};
|
||
|
||
self.init(userOptions, options);
|
||
|
||
//全局变量
|
||
var baseCtx = self.baseCtx = self.options.canvas.getContext("2d");
|
||
var animateCtx = self.animateCtx = self.options.animateCanvas.getContext("2d");
|
||
baseCtx.lineWidth = options.lineWidth;
|
||
};
|
||
|
||
MoveRiver.prototype.init = function (setting, defaults) {
|
||
//合并参数
|
||
tool.merge(setting, defaults);
|
||
this.options = defaults;
|
||
};
|
||
|
||
MoveRiver.prototype.render = function () {
|
||
var self = this;
|
||
var baseCtx = self.baseCtx;
|
||
if (!baseCtx) {
|
||
return;
|
||
}
|
||
var roadLines = self.roadLines;
|
||
roadLines.forEach(function (line) {
|
||
line.drawPath(baseCtx, self.map, self.options);
|
||
});
|
||
};
|
||
|
||
MoveRiver.prototype.animate = function () {
|
||
var self = this;
|
||
var animateCtx = self.animateCtx;
|
||
if (!animateCtx) {
|
||
return;
|
||
}
|
||
// animateCtx.clearRect(0, 0, self.map.width, self.map.height);
|
||
animateCtx.fillStyle = "rgba(0,0,0,0.9)";
|
||
var prev = animateCtx.globalCompositeOperation;
|
||
animateCtx.globalCompositeOperation = "destination-in";
|
||
animateCtx.fillRect(0, 0, self.map.width, self.map.height);
|
||
animateCtx.globalCompositeOperation = prev;
|
||
|
||
var roadLines = self.roadLines;
|
||
roadLines.forEach(function (line) {
|
||
line.draw(animateCtx, self.map, self.options);
|
||
// line.drawCircle(animateCtx, self.map, self.options);
|
||
});
|
||
};
|
||
|
||
MoveRiver.prototype.adjustSize = function () {
|
||
var width = this.map.width;
|
||
var height = this.map.height;
|
||
this.baseCtx.canvas.width = width;
|
||
this.baseCtx.canvas.height = height;
|
||
this.animateCtx.canvas.width = width;
|
||
this.animateCtx.canvas.height = height;
|
||
resolutionScale(this.baseCtx);
|
||
resolutionScale(this.animateCtx);
|
||
};
|
||
|
||
MoveRiver.prototype.start = function () {
|
||
var self = this;
|
||
self.stop();
|
||
self.adjustSize();
|
||
self.addLine();
|
||
self.render();
|
||
// (function drawFrame() {
|
||
// self.timer = setTimeout(function () {
|
||
// self.animationId = requestAnimationFrame(drawFrame);
|
||
// self.animate();
|
||
// }, 1000 / 10);
|
||
// })();
|
||
|
||
// (function drawFrame() {
|
||
// requestAnimationFrame(drawFrame);
|
||
// self.animate();
|
||
// })();
|
||
};
|
||
|
||
MoveRiver.prototype.stop = function () {
|
||
var self = this;
|
||
cancelAnimationFrame(self.animationId);
|
||
if (self.timer) {
|
||
clearTimeout(self.timer);
|
||
}
|
||
};
|
||
|
||
MoveRiver.prototype.addLine = function () {
|
||
var options = this.options;
|
||
var roadLines = this.roadLines = [],
|
||
dataset = this.options.data;
|
||
dataset.forEach(function (line, i) {
|
||
roadLines.push(new Line({
|
||
points: line,
|
||
color: options.colors[Math.floor(Math.random() * options.colors.length)]
|
||
}));
|
||
});
|
||
};
|
||
|
||
function Line(options) {
|
||
this.points = options.points || [];
|
||
this.age = options.age || 0;
|
||
this.maxAge = options.maxAge || 0;
|
||
this.color = options.color || '#ffff00';
|
||
}
|
||
|
||
Line.prototype.getPointList = function (map) {
|
||
var path = this.path = [],
|
||
points = this.points;
|
||
if (points && points.length > 0) {
|
||
points.forEach(function (p) {
|
||
path.push({
|
||
pixel: map.toScreen(p)
|
||
});
|
||
});
|
||
this.maxAge = path.length;
|
||
}
|
||
return path;
|
||
};
|
||
|
||
Line.prototype.drawPath = function (context, map, options) {
|
||
var pointList = this.path || this.getPointList(map);
|
||
context.beginPath();
|
||
context.lineWidth = options.lineWidth;
|
||
context.strokeStyle = this.color;
|
||
// context.strokeStyle = options.lineStyle;
|
||
context.moveTo(pointList[0].pixel.x, pointList[0].pixel.y);
|
||
for (var i = 0, len = pointList.length; i < len; i++) {
|
||
context.lineTo(pointList[i].pixel.x, pointList[i].pixel.y);
|
||
}
|
||
context.stroke();
|
||
};
|
||
|
||
Line.prototype.draw = function (context, map, options) {
|
||
var pointList = this.path || this.getPointList(map);
|
||
var movePoints = this.movePoints;
|
||
if (movePoints && movePoints.length > 0) {
|
||
var moveLen = movePoints.length;
|
||
for (var i = 0; i < moveLen; i++) {
|
||
if (movePoints[i] >= this.maxAge - 1) {
|
||
movePoints[i] = Math.floor(Math.random() * (pointList.length - 1));
|
||
}
|
||
var currentPoint = pointList[movePoints[i]];
|
||
context.beginPath();
|
||
context.lineWidth = options.animateLineWidth;
|
||
context.strokeStyle = this.color;
|
||
context.lineCap = "round";
|
||
context.moveTo(currentPoint.pixel.x, currentPoint.pixel.y);
|
||
context.lineTo(pointList[movePoints[i] + 1].pixel.x, pointList[movePoints[i] + 1].pixel.y);
|
||
context.stroke();
|
||
this.movePoints[i]++;
|
||
}
|
||
} else {
|
||
this.random(map);
|
||
}
|
||
};
|
||
|
||
Line.prototype.drawCircle = function (context, map, options) {
|
||
var pointList = this.path || this.getPointList(map);
|
||
if (this.movePoints && this.movePoints.length > 0) {
|
||
var moveLen = this.movePoints.length;
|
||
for (var i = 0; i < moveLen; i++) {
|
||
if (this.movePoints[i] >= this.maxAge - 1) {
|
||
this.movePoints[i] = Math.floor(Math.random() * pointList.length);
|
||
}
|
||
var currentPoint = pointList[this.movePoints[i]];
|
||
context.beginPath();
|
||
context.arc(currentPoint.pixel.x, currentPoint.pixel.y, 1, 0, Math.PI * 2);
|
||
context.fillStyle = this.color;
|
||
context.fill();
|
||
this.movePoints[i]++;
|
||
}
|
||
} else {
|
||
this.random(map);
|
||
}
|
||
};
|
||
|
||
Line.prototype.random = function (map) {
|
||
var pointList = this.path || this.getPointList(map);
|
||
var arr = [];
|
||
var maxNum = Math.floor(pointList.length / 9);
|
||
while (arr.length < maxNum) {
|
||
//原数组长度为0,每次成功添加一个元素后长度加1,则当数组添加最后一个数字之前长度为9即可
|
||
var num = Math.floor(Math.random() * pointList.length); //生成一个0-100的随机整数
|
||
if (arr.length === 0) {
|
||
//如果数组长度为0则直接添加到arr数组
|
||
arr.push(num);
|
||
} else {
|
||
for (var i = 0; i < arr.length; i++) {
|
||
//当新生成的数字与数组中的元素不重合时则添加到arr数组
|
||
if (arr.join(',').indexOf(num) < 0) {
|
||
arr.push(num);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
this.movePoints = arr;
|
||
};
|
||
|
||
return MoveRiver;
|
||
|
||
})));
|