Openlayers4中实现动态线效果

Stella981
• 阅读 661

概述:

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

效果:

Openlayers4中实现动态线效果

代码:

1、move-line扩展

[javascript] view plain copy

  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.      * @author https://github.com/chengquan223 

  8.      * @Date 2017-02-27 

  9.      * */  

  10.     function CanvasLayer(options) {  

  11.         this.options = options || {};  

  12.         this.paneName = this.options.paneName || 'labelPane';  

  13.         this.zIndex = this.options.zIndex || 0;  

  14.         this._map = options.map;  

  15.         this._lastDrawTime = null;  

  16.         this.show();  

  17.     }  

  18.     CanvasLayer.prototype.initialize = function () {  

  19.         var canvas = this.canvas = document.createElement('canvas');  

  20.         var ctx = this.ctx = this.canvas.getContext('2d');  

  21.         canvas.style.cssText = 'position:absolute;' + 'left:0;' + 'top:0;' + 'z-index:' + this.zIndex + ';';  

  22.         this.adjustSize();  

  23.         this.adjustRatio(ctx);  

  24.         map.getViewport().appendChild(canvas);  

  25.         var that = this;  

  26.         map.getView().on('propertychange',function(){  

  27.             $(canvas).hide();  

  28.         });  

  29.         map.on("moveend",function(){  

  30.             $(canvas).show();  

  31.             that.adjustSize();  

  32.             that._draw();  

  33.         });  

  34.         return this.canvas;  

  35.     };  

  36.     CanvasLayer.prototype.adjustSize = function () {  

  37.         var size = this._map.getSize();  

  38.         var canvas = this.canvas;  

  39.         canvas.width = size[0];  

  40.         canvas.height = size[1];  

  41.         canvas.style.width = canvas.width + 'px';  

  42.         canvas.style.height = canvas.height + 'px';  

  43.     };  

  44.     CanvasLayer.prototype.adjustRatio = function (ctx) {  

  45.         var backingStore = ctx.backingStorePixelRatio || ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;  

  46.         var pixelRatio = (window.devicePixelRatio || 1) / backingStore;  

  47.         var canvasWidth = ctx.canvas.width;  

  48.         var canvasHeight = ctx.canvas.height;  

  49.         ctx.canvas.width = canvasWidth * pixelRatio;  

  50.         ctx.canvas.height = canvasHeight * pixelRatio;  

  51.         ctx.canvas.style.width = canvasWidth + 'px';  

  52.         ctx.canvas.style.height = canvasHeight + 'px';  

  53.         ctx.scale(pixelRatio, pixelRatio);  

  54.     };  

  55.     CanvasLayer.prototype.draw = function () {  

  56.         var self = this;  

  57.         var args = arguments;  

  58.         clearTimeout(self.timeoutID);  

  59.         self.timeoutID = setTimeout(function () {  

  60.             self._draw();  

  61.         }, 15);  

  62.     };  

  63.     CanvasLayer.prototype._draw = function () {  

  64.         var map = this._map;  

  65.         var size = map.getSize();  

  66.         var center = map.getView().getCenter();  

  67.         if (center) {  

  68.             var pixel = map.getPixelFromCoordinate(center);  

  69.             this.canvas.style.left = pixel[0] - size[0] / 2 + 'px';  

  70.             this.canvas.style.top = pixel[1] - size[1] / 2 + 'px';  

  71.             this.options.update && this.options.update.call(this);  

  72.         }  

  73.     };  

  74.     CanvasLayer.prototype.getContainer = function () {  

  75.         return this.canvas;  

  76.     };  

  77.     CanvasLayer.prototype.show = function () {  

  78.         this.initialize();  

  79.         this.canvas.style.display = 'block';  

  80.     };  

  81.     CanvasLayer.prototype.hide = function () {  

  82.         this.canvas.style.display = 'none';  

  83.     };  

  84.     CanvasLayer.prototype.setZIndex = function (zIndex) {  

  85.         this.canvas.style.zIndex = zIndex;  

  86.     };  

  87.     CanvasLayer.prototype.getZIndex = function () {  

  88.         return this.zIndex;  

  89.     };  

  90.     var global = typeof window === 'undefined' ? {} : window;  

  91.     var requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || global.msRequestAnimationFrame || function (callback) {  

  92.             return global.setTimeout(callback, 1000 / 60);  

  93.         };  

  94.     var MoveLine = function MoveLine(map, userOptions) {  

  95.         var self = this;  

  96.         //默认参数  

  97.         var options = {  

  98.             //marker点半径  

  99.             markerRadius: 3,  

  100.             //marker点颜色,为空或null则默认取线条颜色  

  101.             markerColor: '#fff',  

  102.             //线条类型 solid、dashed、dotted  

  103.             lineType: 'solid',  

  104.             //线条宽度  

  105.             lineWidth: 1,  

  106.             //线条颜色  

  107.             colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'],  

  108.             //移动点半径  

  109.             moveRadius: 2,  

  110.             //移动点颜色  

  111.             fillColor: '#fff',  

  112.             //移动点阴影颜色  

  113.             shadowColor: '#fff',  

  114.             //移动点阴影大小  

  115.             shadowBlur: 5  

  116.         };  

  117.         //全局变量  

  118.         var baseLayer = null,  

  119.             animationLayer = null,  

  120.             width = map.getSize()[0],  

  121.             height = map.getSize()[1],  

  122.             animationFlag = true,  

  123.             markLines = [];  

  124.         //参数合并  

  125.         var merge = function merge(userOptions, options) {  

  126.             Object.keys(userOptions).forEach(function (key) {  

  127.                 options[key] = userOptions[key];  

  128.             });  

  129.         };  

  130.         function Marker(opts) {  

  131.             this.city = opts.city;  

  132.             this.location = opts.location;  

  133.             this.color = opts.color;  

  134.         }  

  135.         Marker.prototype.draw = function (context) {  

  136.             var pixel = this.pixel = map.getPixelFromCoordinate(this.location);  

  137.             context.save();  

  138.             context.beginPath();  

  139.             context.fillStyle = options.markerColor || this.color;  

  140.             context.arc(pixel[0], pixel[1], options.markerRadius, 0, Math.PI * 2, true);  

  141.             context.closePath();  

  142.             context.fill();  

  143.             context.textAlign = 'center';  

  144.             context.textBaseline = 'middle';  

  145.             context.font = '12px Microsoft YaHei';  

  146.             context.fillStyle = this.color;  

  147.             context.fillText(this.city, pixel[0], pixel[1] - 10);  

  148.             context.restore();  

  149.         };  

  150.         function MarkLine(opts) {  

  151.             this.from = opts.from;  

  152.             this.to = opts.to;  

  153.             this.id = opts.id;  

  154.             this.step = 0;  

  155.         }  

  156.         MarkLine.prototype.getPointList = function (from, to) {  

  157.             var points = [[from[0], from[1]], [to[0], to[1]]];  

  158.             var ex = points[1][0];  

  159.             var ey = points[1][1];  

  160.             points[3] = [ex, ey];  

  161.             points[1] = this.getOffsetPoint(points[0], points[3]);  

  162.             points[2] = this.getOffsetPoint(points[3], points[0]);  

  163.             points = this.smoothSpline(points, false);  

  164.             //修正最后一点在插值产生的偏移  

  165.             points[points.length - 1] = [ex, ey];  

  166.             return points;  

  167.         };  

  168.         MarkLine.prototype.getOffsetPoint = function (start, end) {  

  169.             var distance = this.getDistance(start, end) / 3; //除以3?  

  170.             var angle, dX, dY;  

  171.             var mp = [start[0], start[1]];  

  172.             var deltaAngle = -0.2; //偏移0.2弧度  

  173.             if (start[0] != end[0] && start[1] != end[1]) {  

  174.                 //斜率存在  

  175.                 var k = (end[1] - start[1]) / (end[0] - start[0]);  

  176.                 angle = Math.atan(k);  

  177.             } else if (start[0] == end[0]) {  

  178.                 //垂直线  

  179.                 angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2;  

  180.             } else {  

  181.                 //水平线  

  182.                 angle = 0;  

  183.             }  

  184.             if (start[0] <= end[0]) {  

  185.                 angle -= deltaAngle;  

  186.                 dX = Math.round(Math.cos(angle) * distance);  

  187.                 dY = Math.round(Math.sin(angle) * distance);  

  188.                 mp[0] += dX;  

  189.                 mp[1] += dY;  

  190.             } else {  

  191.                 angle += deltaAngle;  

  192.                 dX = Math.round(Math.cos(angle) * distance);  

  193.                 dY = Math.round(Math.sin(angle) * distance);  

  194.                 mp[0] -= dX;  

  195.                 mp[1] -= dY;  

  196.             }  

  197.             return mp;  

  198.         };  

  199.         MarkLine.prototype.smoothSpline = function (points, isLoop) {  

  200.             var len = points.length;  

  201.             var ret = [];  

  202.             var distance = 0;  

  203.             for (var i = 1; i < len; i++) {  

  204.                 distance += this.getDistance(points[i - 1], points[i]);  

  205.             }  

  206.             var segs = distance / 2;  

  207.             segs = segs < len ? len : segs;  

  208.             for (var i = 0; i < segs; i++) {  

  209.                 var pos = i / (segs - 1) * (isLoop ? len : len - 1);  

  210.                 var idx = Math.floor(pos);  

  211.                 var w = pos - idx;  

  212.                 var p0;  

  213.                 var p1 = points[idx % len];  

  214.                 var p2;  

  215.                 var p3;  

  216.                 if (!isLoop) {  

  217.                     p0 = points[idx === 0 ? idx : idx - 1];  

  218.                     p2 = points[idx > len - 2 ? len - 1 : idx + 1];  

  219.                     p3 = points[idx > len - 3 ? len - 1 : idx + 2];  

  220.                 } else {  

  221.                     p0 = points[(idx - 1 + len) % len];  

  222.                     p2 = points[(idx + 1) % len];  

  223.                     p3 = points[(idx + 2) % len];  

  224.                 }  

  225.                 var w2 = w * w;  

  226.                 var w3 = w * w2;  

  227.                 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)]);  

  228.             }  

  229.             return ret;  

  230.         };  

  231.         MarkLine.prototype.interpolate = function (p0, p1, p2, p3, t, t2, t3) {  

  232.             var v0 = (p2 - p0) * 0.5;  

  233.             var v1 = (p3 - p1) * 0.5;  

  234.             return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;  

  235.         };  

  236.         MarkLine.prototype.getDistance = function (p1, p2) {  

  237.             return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));  

  238.         };  

  239.         MarkLine.prototype.drawMarker = function (context) {  

  240.             this.from.draw(context);  

  241.             this.to.draw(context);  

  242.         };  

  243.         MarkLine.prototype.drawLinePath = function (context) {  

  244.             var pointList = this.path = this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));  

  245.             var len = pointList.length;  

  246.             context.save();  

  247.             context.beginPath();  

  248.             context.lineWidth = options.lineWidth;  

  249.             context.strokeStyle = options.colors[this.id];  

  250.             if (!options.lineType || options.lineType == 'solid') {  

  251.                 context.moveTo(pointList[0][0], pointList[0][1]);  

  252.                 for (var i = 0; i < len; i++) {  

  253.                     context.lineTo(pointList[i][0], pointList[i][1]);  

  254.                 }  

  255.             } else if (options.lineType == 'dashed' || options.lineType == 'dotted') {  

  256.                 for (var i = 1; i < len; i += 2) {  

  257.                     context.moveTo(pointList[i - 1][0], pointList[i - 1][1]);  

  258.                     context.lineTo(pointList[i][0], pointList[i][1]);  

  259.                 }  

  260.             }  

  261.             context.stroke();  

  262.             context.restore();  

  263.             this.step = 0; //缩放地图时重新绘制动画  

  264.         };  

  265.         MarkLine.prototype.drawMoveCircle = function (context) {  

  266.             var pointList = this.path || this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location));  

  267.             context.save();  

  268.             context.fillStyle = options.fillColor;  

  269.             context.shadowColor = options.shadowColor;  

  270.             context.shadowBlur = options.shadowBlur;  

  271.             context.beginPath();  

  272.             context.arc(pointList[this.step][0], pointList[this.step][1], options.moveRadius, 0, Math.PI * 2, true);  

  273.             context.fill();  

  274.             context.closePath();  

  275.             context.restore();  

  276.             this.step += 1;  

  277.             if (this.step >= pointList.length) {  

  278.                 this.step = 0;  

  279.             }  

  280.         };  

  281.         //底层canvas渲染,标注,线条  

  282.         var brush = function brush() {  

  283.             var baseCtx = baseLayer.canvas.getContext('2d');  

  284.             if (!baseCtx) {  

  285.                 return;  

  286.             }  

  287.             addMarkLine();  

  288.             baseCtx.clearRect(0, 0, width, height);  

  289.             markLines.forEach(function (line) {  

  290.                 line.drawMarker(baseCtx);  

  291.                 line.drawLinePath(baseCtx);  

  292.             });  

  293.         };  

  294.         //上层canvas渲染,动画效果  

  295.         var render = function render() {  

  296.             var animationCtx = animationLayer.canvas.getContext('2d');  

  297.             if (!animationCtx) {  

  298.                 return;  

  299.             }  

  300.             if (!animationFlag) {  

  301.                 animationCtx.clearRect(0, 0, width, height);  

  302.                 return;  

  303.             }  

  304.             animationCtx.fillStyle = 'rgba(0,0,0,.93)';  

  305.             var prev = animationCtx.globalCompositeOperation;  

  306.             animationCtx.globalCompositeOperation = 'destination-in';  

  307.             animationCtx.fillRect(0, 0, width, height);  

  308.             animationCtx.globalCompositeOperation = prev;  

  309.             for (var i = 0; i < markLines.length; i++) {  

  310.                 var markLine = markLines[i];  

  311.                 markLine.drawMoveCircle(animationCtx); //移动圆点  

  312.             }  

  313.         };  

  314.         var addMarkLine = function addMarkLine() {  

  315.             markLines = [];  

  316.             var dataset = options.data;  

  317.             dataset.forEach(function (line, i) {  

  318.                 markLines.push(new MarkLine({  

  319.                     id: i,  

  320.                     from: new Marker({  

  321.                         city: line.from.city,  

  322.                         location: [line.from.lnglat[0], line.from.lnglat[1]],  

  323.                         color: options.colors[i]  

  324.                     }),  

  325.                     to: new Marker({  

  326.                         city: line.to.city,  

  327.                         location: [line.to.lnglat[0], line.to.lnglat[1]],  

  328.                         color: options.colors[i]  

  329.                     })  

  330.                 }));  

  331.             });  

  332.         };  

  333.         //初始化  

  334.         var init = function init(map, options) {  

  335.             merge(userOptions, options);  

  336.             baseLayer = new CanvasLayer({  

  337.                 map: map,  

  338.                 update: brush  

  339.             });  

  340.             animationLayer = new CanvasLayer({  

  341.                 map: map,  

  342.                 update: render  

  343.             });  

  344.             (function drawFrame() {  

  345.                 requestAnimationFrame(drawFrame);  

  346.                 render();  

  347.             })();  

  348.         };  

  349.         init(map, options);  

  350.         self.options = options;  

  351.     };  

  352.     MoveLine.prototype.update = function (resetOpts) {  

  353.         for (var key in resetOpts) {  

  354.             this.options[key] = resetOpts[key];  

  355.         }  

  356.     };  

  357.     return MoveLine;  

  358. })));

  

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这