Openlayers4中实现动态线效果

概述:

本文讲述如何结合canvas在Openlayers4中实现动态线的效果。

效果:

代码:

1、move-line扩展

[javascript] view plain copy

扫描二维码关注公众号,回复: 117389 查看本文章
  1. (function (global, factory) {  
  2.     typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :  
  3.         typeof define === 'function' && define.amd ? define(factory) :  
  4.             (global.MoveLine = factory());  
  5. }(this, (function () { 'use strict';  
  6.   
  7.     /** 
  8.      * @author https://github.com/chengquan223 
  9.      * @Date 2017-02-27 
  10.      * */  
  11.     function CanvasLayer(options) {  
  12.         this.options = options || {};  
  13.         this.paneName = this.options.paneName || 'labelPane';  
  14.         this.zIndex = this.options.zIndex || 0;  
  15.         this._map = options.map;  
  16.         this._lastDrawTime = null;  
  17.         this.show();  
  18.     }  
  19.   
  20.     CanvasLayer.prototype.initialize = function () {  
  21.         var canvas = this.canvas = document.createElement('canvas');  
  22.         var ctx = this.ctx = this.canvas.getContext('2d');  
  23.         canvas.style.cssText = 'position:absolute;' + 'left:0;' + 'top:0;' + 'z-index:' + this.zIndex + ';';  
  24.         this.adjustSize();  
  25.         this.adjustRatio(ctx);  
  26.         map.getViewport().appendChild(canvas);  
  27.         var that = this;  
  28.         map.getView().on('propertychange',function(){  
  29.             $(canvas).hide();  
  30.         });  
  31.         map.on("moveend",function(){  
  32.             $(canvas).show();  
  33.             that.adjustSize();  
  34.             that._draw();  
  35.         });  
  36.         return this.canvas;  
  37.     };  
  38.   
  39.     CanvasLayer.prototype.adjustSize = function () {  
  40.         var size = this._map.getSize();  
  41.         var canvas = this.canvas;  
  42.         canvas.width = size[0];  
  43.         canvas.height = size[1];  
  44.         canvas.style.width = canvas.width + 'px';  
  45.         canvas.style.height = canvas.height + 'px';  
  46.     };  
  47.   
  48.     CanvasLayer.prototype.adjustRatio = function (ctx) {  
  49.         var backingStore = ctx.backingStorePixelRatio || ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;  
  50.         var pixelRatio = (window.devicePixelRatio || 1) / backingStore;  
  51.         var canvasWidth = ctx.canvas.width;  
  52.         var canvasHeight = ctx.canvas.height;  
  53.         ctx.canvas.width = canvasWidth * pixelRatio;  
  54.         ctx.canvas.height = canvasHeight * pixelRatio;  
  55.         ctx.canvas.style.width = canvasWidth + 'px';  
  56.         ctx.canvas.style.height = canvasHeight + 'px';  
  57.         ctx.scale(pixelRatio, pixelRatio);  
  58.     };  
  59.   
  60.     CanvasLayer.prototype.draw = function () {  
  61.         var self = this;  
  62.         var args = arguments;  
  63.         clearTimeout(self.timeoutID);  
  64.         self.timeoutID = setTimeout(function () {  
  65.             self._draw();  
  66.         }, 15);  
  67.     };  
  68.   
  69.     CanvasLayer.prototype._draw = function () {  
  70.         var map = this._map;  
  71.         var size = map.getSize();  
  72.         var center = map.getView().getCenter();  
  73.         if (center) {  
  74.             var pixel = map.getPixelFromCoordinate(center);  
  75.             this.canvas.style.left = pixel[0] - size[0] / 2 + 'px';  
  76.             this.canvas.style.top = pixel[1] - size[1] / 2 + 'px';  
  77.             this.options.update && this.options.update.call(this);  
  78.         }  
  79.     };  
  80.   
  81.     CanvasLayer.prototype.getContainer = function () {  
  82.         return this.canvas;  
  83.     };  
  84.   
  85.     CanvasLayer.prototype.show = function () {  
  86.         this.initialize();  
  87.         this.canvas.style.display = 'block';  
  88.     };  
  89.   
  90.     CanvasLayer.prototype.hide = function () {  
  91.         this.canvas.style.display = 'none';  
  92.     };  
  93.   
  94.     CanvasLayer.prototype.setZIndex = function (zIndex) {  
  95.         this.canvas.style.zIndex = zIndex;  
  96.     };  
  97.   
  98.     CanvasLayer.prototype.getZIndex = function () {  
  99.         return this.zIndex;  
  100.     };  
  101.   
  102.     var global = typeof window === 'undefined' ? {} : window;  
  103.   
  104.     var requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || global.msRequestAnimationFrame || function (callback) {  
  105.             return global.setTimeout(callback, 1000 / 60);  
  106.         };  
  107.   
  108.     var MoveLine = function MoveLine(map, userOptions) {  
  109.         var self = this;  
  110.   
  111.         //默认参数  
  112.         var options = {  
  113.             //marker点半径  
  114.             markerRadius: 3,  
  115.             //marker点颜色,为空或null则默认取线条颜色  
  116.             markerColor: '#fff',  
  117.             //线条类型 solid、dashed、dotted  
  118.             lineType: 'solid',  
  119.             //线条宽度  
  120.             lineWidth: 1,  
  121.             //线条颜色  
  122.             colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'],  
  123.             //移动点半径  
  124.             moveRadius: 2,  
  125.             //移动点颜色  
  126.             fillColor: '#fff',  
  127.             //移动点阴影颜色  
  128.             shadowColor: '#fff',  
  129.             //移动点阴影大小  
  130.             shadowBlur: 5  
  131.         };  
  132.   
  133.         //全局变量  
  134.         var baseLayer = null,  
  135.             animationLayer = null,  
  136.             width = map.getSize()[0],  
  137.             height = map.getSize()[1],  
  138.             animationFlag = true,  
  139.             markLines = [];  
  140.   
  141.         //参数合并  
  142.         var merge = function merge(userOptions, options) {  
  143.             Object.keys(userOptions).forEach(function (key) {  
  144.                 options[key] = userOptions[key];  
  145.             });  
  146.         };  
  147.   
  148.         function Marker(opts) {  
  149.             this.city = opts.city;  
  150.             this.location = opts.location;  
  151.             this.color = opts.color;  
  152.         }  
  153.   
  154.         Marker.prototype.draw = function (context) {  
  155.             var pixel = this.pixel = map.getPixelFromCoordinate(this.location);  
  156.   
  157.             context.save();  
  158.             context.beginPath();  
  159.             context.fillStyle = options.markerColor || this.color;  
  160.             context.arc(pixel[0], pixel[1], options.markerRadius, 0, Math.PI * 2, true);  
  161.             context.closePath();  
  162.             context.fill();  
  163.   
  164.             context.textAlign = 'center';  
  165.             context.textBaseline = 'middle';  
  166.             context.font = '12px Microsoft YaHei';  
  167.             context.fillStyle = this.color;  
  168.             context.fillText(this.city, pixel[0], pixel[1] - 10);  
  169.             context.restore();  
  170.         };  
  171.   
  172.         function MarkLine(opts) {  
  173.             this.from = opts.from;  
  174.             this.to = opts.to;  
  175.             this.id = opts.id;  
  176.             this.step = 0;  
  177.         }  
  178.   
  179.         MarkLine.prototype.getPointList = function (from, to) {  
  180.             var points = [[from[0], from[1]], [to[0], to[1]]];  
  181.             var ex = points[1][0];  
  182.             var ey = points[1][1];  
  183.             points[3] = [ex, ey];  
  184.             points[1] = this.getOffsetPoint(points[0], points[3]);  
  185.             points[2] = this.getOffsetPoint(points[3], points[0]);  
  186.             points = this.smoothSpline(points, false);  
  187.             //修正最后一点在插值产生的偏移  
  188.             points[points.length - 1] = [ex, ey];  
  189.             return points;  
  190.         };  
  191.   
  192.         MarkLine.prototype.getOffsetPoint = function (start, end) {  
  193.             var distance = this.getDistance(start, end) / 3; //除以3?  
  194.             var angle, dX, dY;  
  195.             var mp = [start[0], start[1]];  
  196.             var deltaAngle = -0.2; //偏移0.2弧度  
  197.             if (start[0] != end[0] && start[1] != end[1]) {  
  198.                 //斜率存在  
  199.                 var k = (end[1] - start[1]) / (end[0] - start[0]);  
  200.                 angle = Math.atan(k);  
  201.             } else if (start[0] == end[0]) {  
  202.                 //垂直线  
  203.                 angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2;  
  204.             } else {  
  205.                 //水平线  
  206.                 angle = 0;  
  207.             }  
  208.             if (start[0] <= end[0]) {  
  209.                 angle -= deltaAngle;  
  210.                 dX = Math.round(Math.cos(angle) * distance);  
  211.                 dY = Math.round(Math.sin(angle) * distance);  
  212.                 mp[0] += dX;  
  213.                 mp[1] += dY;  
  214.             } else {  
  215.                 angle += deltaAngle;  
  216.                 dX = Math.round(Math.cos(angle) * distance);  
  217.                 dY = Math.round(Math.sin(angle) * distance);  
  218.                 mp[0] -= dX;  
  219.                 mp[1] -= dY;  
  220.             }  
  221.             return mp;  
  222.         };  
  223.   
  224.         MarkLine.prototype.smoothSpline = function (points, isLoop) {  
  225.             var len = points.length;  
  226.             var ret = [];  
  227.             var distance = 0;  
  228.             for (var i = 1; i < len; i++) {  
  229.                 distance += this.getDistance(points[i - 1], points[i]);  
  230.             }  
  231.             var segs = distance / 2;  
  232.             segs = segs < len ? len : segs;  
  233.             for (var i = 0; i < segs; i++) {  
  234.                 var pos = i / (segs - 1) * (isLoop ? len : len - 1);  
  235.                 var idx = Math.floor(pos);  
  236.                 var w = pos - idx;  
  237.                 var p0;  
  238.                 var p1 = points[idx % len];  
  239.                 var p2;  
  240.                 var p3;  
  241.                 if (!isLoop) {  
  242.                     p0 = points[idx === 0 ? idx : idx - 1];  
  243.                     p2 = points[idx > len - 2 ? len - 1 : idx + 1];  
  244.                     p3 = points[idx > len - 3 ? len - 1 : idx + 2];  
  245.                 } else {  
  246.                     p0 = points[(idx - 1 + len) % len];  
  247.                     p2 = points[(idx + 1) % len];  
  248.                     p3 = points[(idx + 2) % len];  
  249.                 }  
  250.                 var w2 = w * w;  
  251.                 var w3 = w * w2;  
  252.   
  253.                 ret.push([this.interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), this.interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)]);  
  254.             }  
  255.             return ret;  
  256.         };  
  257.   
  258.         MarkLine.prototype.interpolate = function (p0, p1, p2, p3, t, t2, t3) {  
  259.             var v0 = (p2 - p0) * 0.5;  
  260.             var v1 = (p3 - p1) * 0.5;  
  261.             return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;  
  262.         };  
  263.   
  264.         MarkLine.prototype.getDistance = function (p1, p2) {  
  265.             return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));  
  266.         };  
  267.   
  268.         MarkLine.prototype.drawMarker = function (context) {  
  269.             this.from.draw(context);  
  270.             this.to.draw(context);  
  271.         };  
  272.   
  273.         MarkLine.prototype.drawLinePath = function (context) {  
  274.             var pointList = this.path = this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));  
  275.             var len = pointList.length;  
  276.             context.save();  
  277.             context.beginPath();  
  278.             context.lineWidth = options.lineWidth;  
  279.             context.strokeStyle = options.colors[this.id];  
  280.   
  281.             if (!options.lineType || options.lineType == 'solid') {  
  282.                 context.moveTo(pointList[0][0], pointList[0][1]);  
  283.                 for (var i = 0; i < len; i++) {  
  284.                     context.lineTo(pointList[i][0], pointList[i][1]);  
  285.                 }  
  286.             } else if (options.lineType == 'dashed' || options.lineType == 'dotted') {  
  287.                 for (var i = 1; i < len; i += 2) {  
  288.                     context.moveTo(pointList[i - 1][0], pointList[i - 1][1]);  
  289.                     context.lineTo(pointList[i][0], pointList[i][1]);  
  290.                 }  
  291.             }  
  292.             context.stroke();  
  293.             context.restore();  
  294.             this.step = 0; //缩放地图时重新绘制动画  
  295.         };  
  296.   
  297.         MarkLine.prototype.drawMoveCircle = function (context) {  
  298.             var pointList = this.path || this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));  
  299.   
  300.             context.save();  
  301.             context.fillStyle = options.fillColor;  
  302.             context.shadowColor = options.shadowColor;  
  303.             context.shadowBlur = options.shadowBlur;  
  304.             context.beginPath();  
  305.             context.arc(pointList[this.step][0], pointList[this.step][1], options.moveRadius, 0, Math.PI * 2, true);  
  306.             context.fill();  
  307.             context.closePath();  
  308.             context.restore();  
  309.             this.step += 1;  
  310.             if (this.step >= pointList.length) {  
  311.                 this.step = 0;  
  312.             }  
  313.         };  
  314.   
  315.         //底层canvas渲染,标注,线条  
  316.         var brush = function brush() {  
  317.             var baseCtx = baseLayer.canvas.getContext('2d');  
  318.             if (!baseCtx) {  
  319.                 return;  
  320.             }  
  321.   
  322.             addMarkLine();  
  323.   
  324.             baseCtx.clearRect(0, 0, width, height);  
  325.   
  326.             markLines.forEach(function (line) {  
  327.                 line.drawMarker(baseCtx);  
  328.                 line.drawLinePath(baseCtx);  
  329.             });  
  330.         };  
  331.   
  332.         //上层canvas渲染,动画效果  
  333.         var render = function render() {  
  334.             var animationCtx = animationLayer.canvas.getContext('2d');  
  335.             if (!animationCtx) {  
  336.                 return;  
  337.             }  
  338.   
  339.             if (!animationFlag) {  
  340.                 animationCtx.clearRect(0, 0, width, height);  
  341.                 return;  
  342.             }  
  343.   
  344.             animationCtx.fillStyle = 'rgba(0,0,0,.93)';  
  345.             var prev = animationCtx.globalCompositeOperation;  
  346.             animationCtx.globalCompositeOperation = 'destination-in';  
  347.             animationCtx.fillRect(0, 0, width, height);  
  348.             animationCtx.globalCompositeOperation = prev;  
  349.   
  350.             for (var i = 0; i < markLines.length; i++) {  
  351.                 var markLine = markLines[i];  
  352.                 markLine.drawMoveCircle(animationCtx); //移动圆点  
  353.             }  
  354.         };  
  355.         var addMarkLine = function addMarkLine() {  
  356.             markLines = [];  
  357.             var dataset = options.data;  
  358.             dataset.forEach(function (line, i) {  
  359.                 markLines.push(new MarkLine({  
  360.                     id: i,  
  361.                     from: new Marker({  
  362.                         city: line.from.city,  
  363.                         location: [line.from.lnglat[0], line.from.lnglat[1]],  
  364.                         color: options.colors[i]  
  365.                     }),  
  366.                     to: new Marker({  
  367.                         city: line.to.city,  
  368.                         location: [line.to.lnglat[0], line.to.lnglat[1]],  
  369.                         color: options.colors[i]  
  370.                     })  
  371.                 }));  
  372.             });  
  373.         };  
  374.   
  375.         //初始化  
  376.         var init = function init(map, options) {  
  377.             merge(userOptions, options);  
  378.   
  379.             baseLayer = new CanvasLayer({  
  380.                 map: map,  
  381.                 update: brush  
  382.             });  
  383.   
  384.             animationLayer = new CanvasLayer({  
  385.                 map: map,  
  386.                 update: render  
  387.             });  
  388.   
  389.             (function drawFrame() {  
  390.                 requestAnimationFrame(drawFrame);  
  391.                 render();  
  392.             })();  
  393.         };  
  394.   
  395.         init(map, options);  
  396.   
  397.         self.options = options;  
  398.     };  
  399.   
  400.     MoveLine.prototype.update = function (resetOpts) {  
  401.         for (var key in resetOpts) {  
  402.             this.options[key] = resetOpts[key];  
  403.         }  
  404.     };  
  405.   
  406.     return MoveLine;  
  407.   
  408. })));  

2、前段调用

a、数据格式

[javascript] view plain copy

  1. {  
  2.                     from: {  
  3.                         city: '广州',  
  4.                         lnglat: [113.270793, 23.135308]  
  5.                     },  
  6.                     to: {  
  7.                         city: '衡山',  
  8.                         lnglat: [112.612787, 27.317599]  
  9.                     }  
  10.                 }  


b、前段调用

[javascript] view plain copy

  1. new MoveLine(map, {  
  2.                     //marker点半径  
  3.                     markerRadius: 2,  
  4.                     //marker点颜色,为空或null则默认取线条颜色  
  5.                     markerColor: null,  
  6.                     //线条类型 solid、dashed、dotted  
  7.                     lineType: 'solid',  
  8.                     //线条宽度  
  9.                     lineWidth: 2,  
  10.                     //线条颜色  
  11.                     colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'],  
  12.                     //移动点半径  
  13.                     moveRadius: 3,  
  14.                     //移动点颜色  
  15.                     fillColor: '#fff',  
  16.                     //移动点阴影颜色  
  17.                     shadowColor: '#fff',  
  18.                     //移动点阴影大小  
  19.                     shadowBlur: 6,  
  20.                     data: data  
  21.                 });  

------------------------------------------------------------------------------https://blog.csdn.net/GISShiXiSheng/article/details/78148576?locationNum=11&fps=1

猜你喜欢

转载自my.oschina.net/u/1464512/blog/1801155