Openlayers4中實現動態線效果
概述:
本文講述如何結合canvas在Openlayers4中實現動態線的效果。
效果:
程式碼:
1、move-line擴充套件
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.MoveLine = factory()); }(this, (function () { 'use strict'; /** * @author https://github.com/chengquan223 * @Date 2017-02-27 * */ function CanvasLayer(options) { this.options = options || {}; this.paneName = this.options.paneName || 'labelPane'; this.zIndex = this.options.zIndex || 0; this._map = options.map; this._lastDrawTime = null; this.show(); } CanvasLayer.prototype.initialize = function () { var canvas = this.canvas = document.createElement('canvas'); var ctx = this.ctx = this.canvas.getContext('2d'); canvas.style.cssText = 'position:absolute;' + 'left:0;' + 'top:0;' + 'z-index:' + this.zIndex + ';'; this.adjustSize(); this.adjustRatio(ctx); map.getViewport().appendChild(canvas); var that = this; map.getView().on('propertychange',function(){ $(canvas).hide(); }); map.on("moveend",function(){ $(canvas).show(); that.adjustSize(); that._draw(); }); return this.canvas; }; CanvasLayer.prototype.adjustSize = function () { var size = this._map.getSize(); var canvas = this.canvas; canvas.width = size[0]; canvas.height = size[1]; canvas.style.width = canvas.width + 'px'; canvas.style.height = canvas.height + 'px'; }; CanvasLayer.prototype.adjustRatio = function (ctx) { var backingStore = ctx.backingStorePixelRatio || ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; var pixelRatio = (window.devicePixelRatio || 1) / backingStore; var canvasWidth = ctx.canvas.width; var canvasHeight = ctx.canvas.height; ctx.canvas.width = canvasWidth * pixelRatio; ctx.canvas.height = canvasHeight * pixelRatio; ctx.canvas.style.width = canvasWidth + 'px'; ctx.canvas.style.height = canvasHeight + 'px'; ctx.scale(pixelRatio, pixelRatio); }; CanvasLayer.prototype.draw = function () { var self = this; var args = arguments; clearTimeout(self.timeoutID); self.timeoutID = setTimeout(function () { self._draw(); }, 15); }; CanvasLayer.prototype._draw = function () { var map = this._map; var size = map.getSize(); var center = map.getView().getCenter(); if (center) { var pixel = map.getPixelFromCoordinate(center); this.canvas.style.left = pixel[0] - size[0] / 2 + 'px'; this.canvas.style.top = pixel[1] - size[1] / 2 + 'px'; this.options.update && this.options.update.call(this); } }; CanvasLayer.prototype.getContainer = function () { return this.canvas; }; CanvasLayer.prototype.show = function () { this.initialize(); this.canvas.style.display = 'block'; }; CanvasLayer.prototype.hide = function () { this.canvas.style.display = 'none'; }; CanvasLayer.prototype.setZIndex = function (zIndex) { this.canvas.style.zIndex = zIndex; }; CanvasLayer.prototype.getZIndex = function () { return this.zIndex; }; 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 MoveLine = function MoveLine(map, userOptions) { var self = this; //預設引數 var options = { //marker點半徑 markerRadius: 3, //marker點顏色,為空或null則預設取線條顏色 markerColor: '#fff', //線條型別 solid、dashed、dotted lineType: 'solid', //線條寬度 lineWidth: 1, //線條顏色 colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'], //移動點半徑 moveRadius: 2, //移動點顏色 fillColor: '#fff', //移動點陰影顏色 shadowColor: '#fff', //移動點陰影大小 shadowBlur: 5 }; //全域性變數 var baseLayer = null, animationLayer = null, width = map.getSize()[0], height = map.getSize()[1], animationFlag = true, markLines = []; //引數合併 var merge = function merge(userOptions, options) { Object.keys(userOptions).forEach(function (key) { options[key] = userOptions[key]; }); }; function Marker(opts) { this.city = opts.city; this.location = opts.location; this.color = opts.color; } Marker.prototype.draw = function (context) { var pixel = this.pixel = map.getPixelFromCoordinate(this.location); context.save(); context.beginPath(); context.fillStyle = options.markerColor || this.color; context.arc(pixel[0], pixel[1], options.markerRadius, 0, Math.PI * 2, true); context.closePath(); context.fill(); context.textAlign = 'center'; context.textBaseline = 'middle'; context.font = '12px Microsoft YaHei'; context.fillStyle = this.color; context.fillText(this.city, pixel[0], pixel[1] - 10); context.restore(); }; function MarkLine(opts) { this.from = opts.from; this.to = opts.to; this.id = opts.id; this.step = 0; } MarkLine.prototype.getPointList = function (from, to) { var points = [[from[0], from[1]], [to[0], to[1]]]; var ex = points[1][0]; var ey = points[1][1]; points[3] = [ex, ey]; points[1] = this.getOffsetPoint(points[0], points[3]); points[2] = this.getOffsetPoint(points[3], points[0]); points = this.smoothSpline(points, false); //修正最後一點在插值產生的偏移 points[points.length - 1] = [ex, ey]; return points; }; MarkLine.prototype.getOffsetPoint = function (start, end) { var distance = this.getDistance(start, end) / 3; //除以3? var angle, dX, dY; var mp = [start[0], start[1]]; var deltaAngle = -0.2; //偏移0.2弧度 if (start[0] != end[0] && start[1] != end[1]) { //斜率存在 var k = (end[1] - start[1]) / (end[0] - start[0]); angle = Math.atan(k); } else if (start[0] == end[0]) { //垂直線 angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2; } else { //水平線 angle = 0; } if (start[0] <= end[0]) { angle -= deltaAngle; dX = Math.round(Math.cos(angle) * distance); dY = Math.round(Math.sin(angle) * distance); mp[0] += dX; mp[1] += dY; } else { angle += deltaAngle; dX = Math.round(Math.cos(angle) * distance); dY = Math.round(Math.sin(angle) * distance); mp[0] -= dX; mp[1] -= dY; } return mp; }; MarkLine.prototype.smoothSpline = function (points, isLoop) { var len = points.length; var ret = []; var distance = 0; for (var i = 1; i < len; i++) { distance += this.getDistance(points[i - 1], points[i]); } var segs = distance / 2; segs = segs < len ? len : segs; for (var i = 0; i < segs; i++) { var pos = i / (segs - 1) * (isLoop ? len : len - 1); var idx = Math.floor(pos); var w = pos - idx; var p0; var p1 = points[idx % len]; var p2; var p3; if (!isLoop) { p0 = points[idx === 0 ? idx : idx - 1]; p2 = points[idx > len - 2 ? len - 1 : idx + 1]; p3 = points[idx > len - 3 ? len - 1 : idx + 2]; } else { p0 = points[(idx - 1 + len) % len]; p2 = points[(idx + 1) % len]; p3 = points[(idx + 2) % len]; } var w2 = w * w; var w3 = w * w2; 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)]); } return ret; }; MarkLine.prototype.interpolate = function (p0, p1, p2, p3, t, t2, t3) { var v0 = (p2 - p0) * 0.5; var v1 = (p3 - p1) * 0.5; return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1; }; MarkLine.prototype.getDistance = function (p1, p2) { return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1])); }; MarkLine.prototype.drawMarker = function (context) { this.from.draw(context); this.to.draw(context); }; MarkLine.prototype.drawLinePath = function (context) { var pointList = this.path = this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location)); var len = pointList.length; context.save(); context.beginPath(); context.lineWidth = options.lineWidth; context.strokeStyle = options.colors[this.id]; if (!options.lineType || options.lineType == 'solid') { context.moveTo(pointList[0][0], pointList[0][1]); for (var i = 0; i < len; i++) { context.lineTo(pointList[i][0], pointList[i][1]); } } else if (options.lineType == 'dashed' || options.lineType == 'dotted') { for (var i = 1; i < len; i += 2) { context.moveTo(pointList[i - 1][0], pointList[i - 1][1]); context.lineTo(pointList[i][0], pointList[i][1]); } } context.stroke(); context.restore(); this.step = 0; //縮放地圖時重新繪製動畫 }; MarkLine.prototype.drawMoveCircle = function (context) { var pointList = this.path || this.getPointList(map.getPixelFromCoordinate(this.from.location), map.getPixelFromCoordinate(this.to.location)); context.save(); context.fillStyle = options.fillColor; context.shadowColor = options.shadowColor; context.shadowBlur = options.shadowBlur; context.beginPath(); context.arc(pointList[this.step][0], pointList[this.step][1], options.moveRadius, 0, Math.PI * 2, true); context.fill(); context.closePath(); context.restore(); this.step += 1; if (this.step >= pointList.length) { this.step = 0; } }; //底層canvas渲染,標註,線條 var brush = function brush() { var baseCtx = baseLayer.canvas.getContext('2d'); if (!baseCtx) { return; } addMarkLine(); baseCtx.clearRect(0, 0, width, height); markLines.forEach(function (line) { line.drawMarker(baseCtx); line.drawLinePath(baseCtx); }); }; //上層canvas渲染,動畫效果 var render = function render() { var animationCtx = animationLayer.canvas.getContext('2d'); if (!animationCtx) { return; } if (!animationFlag) { animationCtx.clearRect(0, 0, width, height); return; } animationCtx.fillStyle = 'rgba(0,0,0,.93)'; var prev = animationCtx.globalCompositeOperation; animationCtx.globalCompositeOperation = 'destination-in'; animationCtx.fillRect(0, 0, width, height); animationCtx.globalCompositeOperation = prev; for (var i = 0; i < markLines.length; i++) { var markLine = markLines[i]; markLine.drawMoveCircle(animationCtx); //移動圓點 } }; var addMarkLine = function addMarkLine() { markLines = []; var dataset = options.data; dataset.forEach(function (line, i) { markLines.push(new MarkLine({ id: i, from: new Marker({ city: line.from.city, location: [line.from.lnglat[0], line.from.lnglat[1]], color: options.colors[i] }), to: new Marker({ city: line.to.city, location: [line.to.lnglat[0], line.to.lnglat[1]], color: options.colors[i] }) })); }); }; //初始化 var init = function init(map, options) { merge(userOptions, options); baseLayer = new CanvasLayer({ map: map, update: brush }); animationLayer = new CanvasLayer({ map: map, update: render }); (function drawFrame() { requestAnimationFrame(drawFrame); render(); })(); }; init(map, options); self.options = options; }; MoveLine.prototype.update = function (resetOpts) { for (var key in resetOpts) { this.options[key] = resetOpts[key]; } }; return MoveLine; })));
2、前段呼叫
a、資料格式
{ from: { city: '廣州', lnglat: [113.270793, 23.135308] }, to: { city: '衡山', lnglat: [112.612787, 27.317599] } }
b、前段呼叫
new MoveLine(map, { //marker點半徑 markerRadius: 2, //marker點顏色,為空或null則預設取線條顏色 markerColor: null, //線條型別 solid、dashed、dotted lineType: 'solid', //線條寬度 lineWidth: 2, //線條顏色 colors: ['#F9815C', '#F8AB60', '#EDCC72', '#E2F194', '#94E08A', '#4ECDA5'], //移動點半徑 moveRadius: 3, //移動點顏色 fillColor: '#fff', //移動點陰影顏色 shadowColor: '#fff', //移動點陰影大小 shadowBlur: 6, data: data });
----------------------------------------------------------------------------------------------
技術部落格
CSDN:http://blog.csdn.NET/gisshixisheng
部落格園:http://www.cnblogs.com/lzugis/
線上教程
http://edu.csdn.Net/course/detail/799
Github
https://github.com/lzugis/
聯絡方式
q q:1004740957
e-mail:[email protected]
公眾號:lzugis15
Q Q 群:452117357(webgis)
337469080(Android)
相關推薦
Openlayers4中實現動態線效果
概述:本文講述如何結合canvas在Openlayers4中實現動態線的效果。效果:程式碼:1、move-line擴充套件(function (global, factory) { typeof exports === 'object' && type
Shine.js實現動態陰影效果
b2c ava .net fun text 動態 cti element gravity Shine.js 是一個用於實現美麗陰影的 JS 庫。 特性 1、可動態旋轉光的位置,投影出不同的陰影效果 2、可定制的陰影, 3、沒有庫依賴關系,AMD兼容使
關於Unity中實現繩索物理效果 Obi - Advanced Rope Simulation插件解析
阻尼 旋轉 eval mil 抖動 amp ola all 動作 Obi - Advanced Rope Simulation 繩索插件學習文檔 插件分享: 鏈接:https://pan.baidu.com/s/1eTwZOrg 密碼:p8wa //插件導入有錯誤產生,將
Java中實現多線程的兩種方式
窗口 -- his 面向對象 new thread 資源 pub string /** * 使用Thread類模擬4個售票窗口共同賣100張火車票的程序 * * 沒有共享數據,每個線程各賣100張火車票 * * @author jiqinlin * */pu
UWP中實現大爆炸效果(一)
ID eight 爆炸效果 foo 更新 The 選中 wid 重寫 自從老羅搞出大爆炸之後,各家安卓都內置了類似功能。UWP怎麽能落下呢,在這裏我們就一起擼一個簡單的大爆炸實現。 閑話不說,先上效果: 因為代碼太多,所以我打算寫成一個系列,下面是第一篇的正文: 首先
UWP中實現大爆炸效果(二)
cti setter val sele osi enume rail += ddd 上一回實現了一個寬度不均勻的Panel,這次我們編寫一個簡單的BigbangView主體。 首先創建一個模板化控件,刪掉Themes/Generic.xaml中的<Style Targ
jq實現動態粒子效果
tex ret padding 粒子 proto scrip rip [] 3D 在線上看到別人做的動態粒子,哇,太炫了,我也來試一試。 <!DOCTYPE html><html> <head> <meta charset="UT
Java中實現多線程繼承Thread類與實現Runnable接口的區別
不同 static oid 實現 nbsp 運行 我們 增強 ner Java中線程的創建有兩種方式: 1. 通過繼承Thread類,重寫Thread的run()方法,將線程運行的邏輯放在其中 2. 通過實現Runnable接口,實例化Thread類
MyBatis中實現動態的SQL語句,分頁以及mybatis的常用的配置
<select id="getListByPage" resultType="com.gxa.bj.modle.UserInfoPage" parameterType="com.gxa.bj.modle.UserInfoPage">
在retina屏中實現1px border效果
街景wap官網中有在視網膜螢幕中實現1px border的需求. 首先,來看下面視覺給的輸出圖中的border: 從上面的視覺圖可以看到,border是一根非常細的線。這篇文章將說明如何使用border-image實現在視網膜屏中1px的border效果
在 Web 應用中實現全屏效果
隨著HTML5技術和瀏覽器的發展,Web應用程式也能像本地應用一樣實現全屏,而且現在大部分瀏覽器都支援全屏。Fullscreen JavaScript API讓這一切變得簡單,本文來探討一下如何讓Web應用程式實現全屏效果。 啟動全屏模式 可以通過Fullscreen A
Android--ListView中item中實現跑馬燈效果
1.要實現跑馬燈的TextView寫法 <TextView android:layout_width="wrap_content"
robotframework在測試中實現動態的增加測試內容,使用純robotframework自身語法實現
自動化測試有種場景是:用例跑起來了,想增加新的檢查項或則配置,一般常見可能的操作是: 1、通過動態定期檢查一個指令碼,或檔案,執行檔案裡面的內容 2、使用reload library方法,迴圈更新庫,執行時,更新庫的內容,變能實現執行用例時動態增加修改測試內容 以上方法都需
Axure RP 7原型工具通過中繼器實現動態表格效果
一、新增控制元件 1、新增兩個文字控制元件分別為“姓名”、“Email” 2、新增兩個文字框控制元件分別命名為"inputTextName"、“inputEmailText” 3、新增一個按鈕控制元件“提交” 4、新增一個表格空間,只要頭,將頭下面的單元格刪掉 5、新增一箇
mapbox 與 openlayers 實現晨昏線效果
轉自https://blog.csdn.net/u014529917/article/details/77506542 替換mapbox access_token <!DOCTYPE html> <html> <head> <title>Map
Qt 之 QQ系統表情—實現動態顯示效果
簡述 在Qt 之 QQ系統表情(五) 中 我們實現了自定義系統表情視窗,這一篇將簡單介紹如何實現QQ聊天介面中小表情視窗切換至正常表情視窗的動畫效果。 先看看QQ的效果: 當滑鼠懸浮在表情按鈕之上顯示小表情視窗,點選動態顯示正常表情視窗,再點選隱
在網頁中實現細線邊框的兩種方法
很多朋友都有過製作網頁的經歷,如今,眾多網頁的設計都用到了表格。這樣不僅有利於網頁的維護,同時,提高了網頁的觀賞性。在眾多網頁製作風格中,細邊框這個製作方法是必不可少的。這裡,我將簡單地談一下細邊框的製作方法。 談到細邊框,本人認為大致有兩種製作方法,第一種是用表格
JSP中實現動態顯示系統時間
JSP中實現動態顯示系統時間可以使用js來編寫,如下程式碼: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-
HTML中實現圖片滾動效果
HTML中實現滾動效果網頁中多的不能再說了,一般的網站都有圖片滾動效果,突然滾動出一個美女讓你點選,這樣也是一個網站獲取流量的方式,今天我也做了個測試,實現簡單的圖片滾動效果 <marquee onmouseover=stop() onmouseout=star
android xml bitmap 實現波浪線效果
我們要實現的效果如下: 找了一些關於實現波浪線的方法,總感覺不大滿意,常見的方法有 1、直接搞一個這樣的波浪線的切圖 這種方式最簡單,但是劣勢也非常明顯,如果view的寬度過大,則會出現