384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
(function (global, factory) {
|
||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||
typeof define === 'function' && define.amd ? define(factory) :
|
||
(global.LineGradient = 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;
|
||
},
|
||
|
||
//是否在矩形内
|
||
isPointInRect: function isPointInRect(point, bound) {
|
||
var wn = bound.wn; //西北
|
||
var es = bound.es; //东南
|
||
return point.x >= wn.x && point.x <= es.x && point.y >= wn.y && point.y <= es.y;
|
||
},
|
||
|
||
//是否在圆内
|
||
isPointInCircle: function isPointInCircle(point, center, radius) {
|
||
var dis = this.getDistanceNew(point, center);
|
||
return dis <= radius;
|
||
},
|
||
|
||
//两点间距离
|
||
getDistanceNew: function getDistanceNew(point1, point2) {
|
||
return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 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 requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || global.msRequestAnimationFrame || function (callback) {
|
||
return global.setTimeout(callback, 1000 / 60);
|
||
};
|
||
|
||
var cancelAnimationFrame = global.cancelAnimationFrame || global.mozCancelAnimationFrame || global.webkitCancelAnimationFrame || global.msCancelAnimationFrame || function (id) {
|
||
clearTimeout(id);
|
||
};
|
||
|
||
var LineGradient = function LineGradient(map, userOptions) {
|
||
var self = this;
|
||
|
||
self.map = map;
|
||
self.lines = [];
|
||
self.pixelList = [];
|
||
|
||
//默认参数
|
||
var options = {
|
||
//线条宽度
|
||
lineWidth: 1
|
||
};
|
||
|
||
self.init(userOptions, options);
|
||
|
||
//全局变量
|
||
this.baseCtx = self.options.baseCanvas.getContext("2d");
|
||
this.animateCtx = self.options.animateCanvas.getContext("2d");
|
||
this.width = map.width;
|
||
this.height = map.height;
|
||
|
||
this.clickEvent = this.clickEvent.bind(this);
|
||
|
||
this.bindEvent();
|
||
};
|
||
|
||
LineGradient.prototype.start = function () {
|
||
var self = this;
|
||
self.stop();
|
||
self.adjustSize();
|
||
|
||
self.renderBaselayer(); //底层canvas渲染
|
||
|
||
(function drawFrame() {
|
||
self.timer = setTimeout(function () {
|
||
self.animationId = requestAnimationFrame(drawFrame);
|
||
self.renderAnimatelayer(); //动画层canvas渲染
|
||
}, 1000 / 10);
|
||
})();
|
||
};
|
||
|
||
LineGradient.prototype.stop = function () {
|
||
var self = this;
|
||
if (self.animationId) {
|
||
cancelAnimationFrame(self.animationId);
|
||
}
|
||
if (self.timer) {
|
||
clearTimeout(self.timer);
|
||
}
|
||
};
|
||
|
||
LineGradient.prototype.adjustSize = function () {
|
||
var width = this.width;
|
||
var height = this.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);
|
||
};
|
||
|
||
LineGradient.prototype.addLine = function () {
|
||
var self = this,
|
||
options = this.options;
|
||
if (self.lines && self.lines.length > 0) return;
|
||
var dataset = options.data;
|
||
var legend = new Legend();
|
||
|
||
dataset.forEach(function (item, i) {
|
||
var line = new Line({
|
||
name: item.name,
|
||
label: item.label,
|
||
labelColor: item.labelColor,
|
||
path: []
|
||
});
|
||
|
||
item.data.forEach(function (point, j) {
|
||
point.color = legend.getColor(point.value).color;
|
||
line.path.push(point);
|
||
});
|
||
|
||
self.lines.push(line);
|
||
});
|
||
};
|
||
|
||
LineGradient.prototype.init = function (settings, defaults) {
|
||
//合并参数
|
||
tool.merge(settings, defaults);
|
||
this.options = defaults;
|
||
|
||
//初始化线条数据
|
||
this.addLine();
|
||
};
|
||
|
||
LineGradient.prototype.renderBaselayer = function () {
|
||
var self = this;
|
||
var context = self.baseCtx;
|
||
if (!context) return;
|
||
|
||
context.clearRect(0, 0, self.width, self.height);
|
||
|
||
self.pixelList = [];
|
||
self.lines.forEach(function (line) {
|
||
self.pixelList.push({
|
||
name: line.name,
|
||
label: line.label,
|
||
labelColor: line.labelColor,
|
||
data: line.getPointList(self.map)
|
||
});
|
||
line.draw(context, self.map, self.options);
|
||
});
|
||
};
|
||
|
||
LineGradient.prototype.renderAnimatelayer = function () {
|
||
var context = this.animateCtx;
|
||
if (!context) return;
|
||
|
||
context.fillStyle = 'rgba(0,0,0,.2)';
|
||
var prev = context.globalCompositeOperation;
|
||
context.globalCompositeOperation = 'destination-in';
|
||
context.fillRect(0, 0, this.width, this.height);
|
||
context.globalCompositeOperation = prev;
|
||
|
||
var lines = this.lines;
|
||
for (var i = 0; i < lines.length; i++) {
|
||
lines[i].drawArrow(context, this.map); //画箭头
|
||
}
|
||
};
|
||
|
||
LineGradient.prototype.bindEvent = function (e) {
|
||
var map = this.map;
|
||
if (this.options.methods) {
|
||
if (this.options.methods.mousemove) {
|
||
map.on('mouse-move', this.clickEvent);
|
||
}
|
||
}
|
||
};
|
||
|
||
LineGradient.prototype.clickEvent = function (e) {
|
||
var self = this,
|
||
flag = false,
|
||
lines = self.pixelList;
|
||
|
||
if (lines.length > 0) {
|
||
lines.forEach(function (line, i) {
|
||
for (var j = 0; j < line.data.length; j++) {
|
||
var beginPt = line.data[j].pixel;
|
||
if (line.data[j + 1] == undefined) return;
|
||
var curPt = e;
|
||
var inCircle = tool.isPointInCircle(curPt, beginPt, self.options.lineWidth);
|
||
if (inCircle) {
|
||
self.options.methods.mousemove(e, line.data[j]);
|
||
flag = true;
|
||
return;
|
||
}
|
||
}
|
||
});
|
||
if (!flag) {
|
||
document.getElementById('tooltips').style.visibility = 'hidden';
|
||
}
|
||
}
|
||
};
|
||
|
||
function Line(opts) {
|
||
this.name = opts.name;
|
||
this.label = opts.label;
|
||
this.labelColor = opts.labelColor;
|
||
this.path = opts.path;
|
||
this.step = 0;
|
||
}
|
||
|
||
Line.prototype.getPointList = function (map) {
|
||
var points = this.path;
|
||
if (points && points.length > 0) {
|
||
points.forEach(function (point) {
|
||
point.pixel = map.toScreen(point.lonlat);
|
||
});
|
||
}
|
||
return points;
|
||
};
|
||
|
||
Line.prototype.draw = function (context, map, options) {
|
||
var pointList = this.pixelList || this.getPointList(map);
|
||
|
||
for (var i = 0, len = pointList.length; i < len - 1; i++) {
|
||
context.save();
|
||
context.beginPath();
|
||
context.lineWidth = options.lineWidth;
|
||
context.strokeStyle = pointList[i].color;
|
||
context.moveTo(pointList[i].pixel.x, pointList[i].pixel.y);
|
||
context.lineTo(pointList[i + 1].pixel.x, pointList[i + 1].pixel.y);
|
||
context.stroke();
|
||
context.closePath();
|
||
context.restore();
|
||
}
|
||
|
||
var lastpoint = pointList[pointList.length - 1];
|
||
context.font = 'bold 14px Arial';
|
||
context.textAlign = 'left';
|
||
context.textBaseline = 'middle';
|
||
context.fillStyle = this.labelColor;
|
||
context.fillText(this.label, lastpoint.pixel.x, lastpoint.pixel.y);
|
||
};
|
||
|
||
Line.prototype.drawArrow = function (context, map, options) {
|
||
var pointList = this.pixelList || this.getPointList(map);
|
||
if (this.step >= pointList.length - 1) {
|
||
this.step = 0;
|
||
}
|
||
context.beginPath();
|
||
// context.lineWidth = options.animateLineWidth;
|
||
context.lineWidth = 5;
|
||
context.strokeStyle = '#fff';
|
||
context.moveTo(pointList[this.step].pixel.x, pointList[this.step].pixel.y);
|
||
context.lineTo(pointList[this.step + 1].pixel.x, pointList[this.step + 1].pixel.y);
|
||
context.stroke();
|
||
|
||
context.save();
|
||
context.translate(pointList[this.step + 1].pixel.x, pointList[this.step + 1].pixel.y);
|
||
//我的箭头本垂直向下,算出直线偏离Y的角,然后旋转 ,rotate是顺时针旋转的,所以加个负号
|
||
var ang = (pointList[this.step + 1].pixel.x - pointList[this.step].pixel.x) / (pointList[this.step + 1].pixel.y - pointList[this.step].pixel.y);
|
||
ang = Math.atan(ang);
|
||
pointList[this.step + 1].pixel.y - pointList[this.step].pixel.y >= 0 ? context.rotate(-ang) : context.rotate(Math.PI - ang); //加个180度,反过来
|
||
context.lineTo(-6, -6);
|
||
context.lineTo(0, 6);
|
||
context.lineTo(6, -6);
|
||
context.lineTo(0, 0);
|
||
context.fillStyle = '#fff';
|
||
context.fill(); //箭头是个封闭图形
|
||
context.restore(); //用来恢复Canvas之前保存的状态,否则会影响后续绘制
|
||
|
||
this.step += 1;
|
||
};
|
||
|
||
function Legend() {
|
||
var options = this.options = {
|
||
width: 400,
|
||
height: 15,
|
||
range: [700, 750, 800, 850, 900, 950, 1000, 1050, 1100],
|
||
gradient: {
|
||
0.1: '#fe0000',
|
||
0.4: '#ffaa01',
|
||
0.7: '#b0e000',
|
||
1.0: '#38a702'
|
||
}
|
||
};
|
||
|
||
this.init();
|
||
}
|
||
|
||
Legend.prototype.init = function () {
|
||
var options = this.options;
|
||
var canvas = this.canvas = document.createElement('canvas');
|
||
var context = canvas.getContext('2d');
|
||
canvas.width = options.width;
|
||
canvas.height = options.height;
|
||
var grad = context.createLinearGradient(0, 0, canvas.width, canvas.height);
|
||
for (var gradient in options.gradient) {
|
||
grad.addColorStop(gradient, options.gradient[gradient]);
|
||
}
|
||
context.fillStyle = grad;
|
||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||
this.imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||
};
|
||
|
||
Legend.prototype.d2Hex = function (d) {
|
||
var hex = Number(d).toString(16);
|
||
while (hex.length < 2) {
|
||
hex = "0" + hex;
|
||
}
|
||
return hex.toUpperCase();
|
||
};
|
||
|
||
Legend.prototype.getRgbColor = function (point) {
|
||
var imageData = this.imageData;
|
||
var data = imageData.data;
|
||
var i = (point.y * this.canvas.width + point.x) * 4;
|
||
var rgb = [],
|
||
color = '#',
|
||
objRgbColor = {
|
||
"rgb": rgb,
|
||
"color": color
|
||
};
|
||
for (var j = 0; j < 3; j++) {
|
||
rgb.push(data[i + j]);
|
||
color += this.d2Hex(data[i + j]);
|
||
}
|
||
objRgbColor.color = color;
|
||
return objRgbColor;
|
||
};
|
||
|
||
Legend.prototype.getColor = function (value) {
|
||
var options = this.options;
|
||
var colorValue = value - options.range[0];
|
||
var point = {
|
||
x: Math.round(colorValue * this.canvas.width / (options.range[options.range.length - 1] - options.range[0])),
|
||
y: 1
|
||
};
|
||
return this.getRgbColor(point);
|
||
};
|
||
|
||
return LineGradient;
|
||
|
||
})));
|