亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue利用openlayers實(shí)現(xiàn)動(dòng)態(tài)軌跡

 更新時(shí)間:2022年11月04日 09:28:15   作者:字節(jié)逆旅  
這篇文章主要為大家介紹了vue利用openlayers實(shí)現(xiàn)動(dòng)態(tài)軌跡,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

實(shí)現(xiàn)效果

今天介紹一個(gè)有趣的gis小功能:動(dòng)態(tài)軌跡播放!效果就像這樣:

這效果看著還很絲滑!別急,接下來教你怎么實(shí)現(xiàn)。代碼示例基于parcel打包工具和es6語法,本文假設(shè)你已經(jīng)掌握相關(guān)知識(shí)和技巧。

gis初學(xué)者可能對(duì)openlayers(后面簡稱ol)不熟悉,這里暫時(shí)不介紹ol了,直接上代碼,先體驗(yàn)下感覺。

創(chuàng)建一個(gè)地圖容器

引入地圖相關(guān)對(duì)象

import Map from 'ol/Map';
import View from 'ol/View';
import XYZ from 'ol/source/XYZ';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';

創(chuàng)建地圖對(duì)象

const center = [-5639523.95, -3501274.52];
const map = new Map({
  target: document.getElementById('map'),
  view: new View({
    center: center,
    zoom: 10,
    minZoom: 2,
    maxZoom: 19,
  }),
  layers: [
    new TileLayer({
      source: new XYZ({
        attributions: attributions,
        url: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=' + key,
        tileSize: 512,
      }),
    }),
  ],
});

創(chuàng)建一條線路

畫一條線路

可以用這個(gè)geojson網(wǎng)站隨意畫一條線,然后把數(shù)據(jù)內(nèi)容復(fù)制下來,保存為json文件格式,作為圖層數(shù)據(jù)添加到地圖容器中。

你可以用異步加載的方式,也可以用require方式,這里都介紹下吧:

// fetch
fetch('data/route.json').then(function (response) {
  response.json().then(function (result) {
    const polyline = result.routes[0].geometry;
  }),
};
// require
var roadData = require('data/route.json')

后面基本一樣了,就以fetch為準(zhǔn),現(xiàn)在把線路加載的剩余部分補(bǔ)充完整:

fetch('data/route.json').then(function (response) {
  response.json().then(function (result) {
    const polyline = result.routes[0].geometry;
	// 線路數(shù)據(jù)坐標(biāo)系轉(zhuǎn)換
    const route = new Polyline({
      factor: 1e6,
    }).readGeometry(polyline, {
      dataProjection: 'EPSG:4326',
      featureProjection: 'EPSG:3857',
    });
	// 線路圖層要素
    const routeFeature = new Feature({
      type: 'route',
      geometry: route,
    });
    // 起點(diǎn)要素
    const startMarker = new Feature({
      type: 'icon',
      geometry: new Point(route.getFirstCoordinate()),
    });
    // 終點(diǎn)要素
    const endMarker = new Feature({
      type: 'icon',
      geometry: new Point(route.getLastCoordinate()),
    });
    // 取起點(diǎn)值
    const position = startMarker.getGeometry().clone();
    // 游標(biāo)要素
    const geoMarker = new Feature({
      type: 'geoMarker',
      geometry: position,
    });
	// 樣式組合
    const styles = {
        // 路線
      'route': new Style({
        stroke: new Stroke({
          width: 6,
          color: [237, 212, 0, 0.8],
        }),
      }),
      'icon': new Style({
        image: new Icon({
          anchor: [0.5, 1],
          src: 'data/icon.png',
        }),
      }),
      'geoMarker': new Style({
        image: new CircleStyle({
          radius: 7,
          fill: new Fill({color: 'black'}),
          stroke: new Stroke({
            color: 'white',
            width: 2,
          }),
        }),
      }),
    };
	// 創(chuàng)建圖層并添加以上要素集合
    const vectorLayer = new VectorLayer({
      source: new VectorSource({
        features: [routeFeature, geoMarker, startMarker, endMarker],
      }),
      style: function (feature) {
        return styles[feature.get('type')];
      },
    });
	// 在地圖容器中添加圖層
    map.addLayer(vectorLayer);

以上代碼很完整,我加了注釋,整體思路總結(jié)如下:

  • 先加載路線數(shù)據(jù)
  • 構(gòu)造路線、起始點(diǎn)及游標(biāo)對(duì)應(yīng)圖層要素對(duì)象
  • 構(gòu)造圖層并把要素添加進(jìn)去
  • 在地圖容器中添加圖層

添加起、終點(diǎn)

這個(gè)上面的代碼已經(jīng)包括了,我這里列出來是為了讓你更清晰,就是startMarkerendMarker對(duì)應(yīng)的代碼。

添加小車

同樣的,這里的代碼在上面也寫過了,就是geoMarker所對(duì)應(yīng)的代碼。

準(zhǔn)備開車

線路有了,車也有了,現(xiàn)在就到了激動(dòng)人心的開車時(shí)刻了,接下來才是本文最核心的代碼!

const speedInput = document.getElementById('speed');
    const startButton = document.getElementById('start-animation');
    let animating = false;
    let distance = 0;
    let lastTime;
    function moveFeature(event) {
      const speed = Number(speedInput.value);
      // 獲取當(dāng)前渲染幀狀態(tài)時(shí)刻
      const time = event.frameState.time;
      // 渲染時(shí)刻減去開始播放軌跡的時(shí)間
      const elapsedTime = time - lastTime;
      // 求得距離比
      distance = (distance + (speed * elapsedTime) / 1e6) % 2;
      // 刷新上一時(shí)刻
      lastTime = time;
	  // 反減可實(shí)現(xiàn)反向運(yùn)動(dòng),獲取坐標(biāo)點(diǎn)
      const currentCoordinate = route.getCoordinateAt(
        distance > 1 ? 2 - distance : distance
      );
      position.setCoordinates(currentCoordinate);
      // 獲取渲染圖層的畫布
      const vectorContext = getVectorContext(event);
      vectorContext.setStyle(styles.geoMarker);
      vectorContext.drawGeometry(position);
      map.render();
    }
    function startAnimation() {
      animating = true;
      lastTime = Date.now();
      startButton.textContent = 'Stop Animation';
      vectorLayer.on('postrender', moveFeature);
      // 隱藏小車前一刻位置同時(shí)觸發(fā)事件
      geoMarker.setGeometry(null);
    }
    function stopAnimation() {
      animating = false;
      startButton.textContent = '開車了';
      // 將小車固定在當(dāng)前位置
      geoMarker.setGeometry(position);
      vectorLayer.un('postrender', moveFeature);
    }
    startButton.addEventListener('click', function () {
      if (animating) {
        stopAnimation();
      } else {
        startAnimation();
      }
    });

簡單說下它的原理就是利用postrender事件觸發(fā)一個(gè)函數(shù),這個(gè)事件本來是地圖渲染結(jié)束事件,但是它的回調(diào)函數(shù)中,小車的坐標(biāo)位置一直在變,那就會(huì)不停地觸發(fā)地圖渲染,當(dāng)然最終也會(huì)觸發(fā)postrender。這樣就實(shí)現(xiàn)的小車沿著軌跡的動(dòng)畫效果了。這段代碼有點(diǎn)難理解,最好自己嘗試體驗(yàn)下,比較難理解部分我都加上了注釋。

好了,ol動(dòng)態(tài)巡查已經(jīng)介紹完了,動(dòng)手試下吧!看你的車能否開起來?

完整代碼

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Marker Animation</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <label for="speed">
      speed: 
      <input id="speed" type="range" min="10" max="999" step="10" value="60">
    </label>
    <button id="start-animation">Start Animation</button>
    <script src="main.js"></script>
  </body>
</html>

main.js

import 'ol/ol.css';
import Feature from 'ol/Feature';
import Map from 'ol/Map';
import Point from 'ol/geom/Point';
import Polyline from 'ol/format/Polyline';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import XYZ from 'ol/source/XYZ';
import {
  Circle as CircleStyle,
  Fill,
  Icon,
  Stroke,
  Style,
} from 'ol/style';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {getVectorContext} from 'ol/render';
const key = 'Get your own API key at https://www.maptiler.com/cloud/';
const attributions =
  '<a  rel="external nofollow"  target="_blank">&copy; MapTiler</a> ' +
  '<a  rel="external nofollow"  target="_blank">&copy; OpenStreetMap contributors</a>';
const center = [-5639523.95, -3501274.52];
const map = new Map({
  target: document.getElementById('map'),
  view: new View({
    center: center,
    zoom: 10,
    minZoom: 2,
    maxZoom: 19,
  }),
  layers: [
    new TileLayer({
      source: new XYZ({
        attributions: attributions,
        url: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=' + key,
        tileSize: 512,
      }),
    }),
  ],
});
// The polyline string is read from a JSON similiar to those returned
// by directions APIs such as Openrouteservice and Mapbox.
fetch('data/polyline/route.json').then(function (response) {
  response.json().then(function (result) {
    const polyline = result.routes[0].geometry;
    const route = new Polyline({
      factor: 1e6,
    }).readGeometry(polyline, {
      dataProjection: 'EPSG:4326',
      featureProjection: 'EPSG:3857',
    });
    const routeFeature = new Feature({
      type: 'route',
      geometry: route,
    });
    const startMarker = new Feature({
      type: 'icon',
      geometry: new Point(route.getFirstCoordinate()),
    });
    const endMarker = new Feature({
      type: 'icon',
      geometry: new Point(route.getLastCoordinate()),
    });
    const position = startMarker.getGeometry().clone();
    const geoMarker = new Feature({
      type: 'geoMarker',
      geometry: position,
    });
    const styles = {
      'route': new Style({
        stroke: new Stroke({
          width: 6,
          color: [237, 212, 0, 0.8],
        }),
      }),
      'icon': new Style({
        image: new Icon({
          anchor: [0.5, 1],
          src: 'data/icon.png',
        }),
      }),
      'geoMarker': new Style({
        image: new CircleStyle({
          radius: 7,
          fill: new Fill({color: 'black'}),
          stroke: new Stroke({
            color: 'white',
            width: 2,
          }),
        }),
      }),
    };
    const vectorLayer = new VectorLayer({
      source: new VectorSource({
        features: [routeFeature, geoMarker, startMarker, endMarker],
      }),
      style: function (feature) {
        return styles[feature.get('type')];
      },
    });
    map.addLayer(vectorLayer);
    const speedInput = document.getElementById('speed');
    const startButton = document.getElementById('start-animation');
    let animating = false;
    let distance = 0;
    let lastTime;
    function moveFeature(event) {
      const speed = Number(speedInput.value);
      const time = event.frameState.time;
      const elapsedTime = time - lastTime;
      distance = (distance + (speed * elapsedTime) / 1e6) % 2;
      lastTime = time;
      const currentCoordinate = route.getCoordinateAt(
        distance > 1 ? 2 - distance : distance
      );
      position.setCoordinates(currentCoordinate);
      const vectorContext = getVectorContext(event);
      vectorContext.setStyle(styles.geoMarker);
      vectorContext.drawGeometry(position);
      // tell OpenLayers to continue the postrender animation
      map.render();
    }
    function startAnimation() {
      animating = true;
      lastTime = Date.now();
      startButton.textContent = 'Stop Animation';
      vectorLayer.on('postrender', moveFeature);
      geoMarker.setGeometry(null);
    }
    function stopAnimation() {
      animating = false;
      startButton.textContent = '開車了';
      geoMarker.setGeometry(position);
      vectorLayer.un('postrender', moveFeature);
    }
    startButton.addEventListener('click', function () {
      if (animating) {
        stopAnimation();
      } else {
        startAnimation();
      }
    });
  });
});

package.json

{
  "name": "feature-move-animation",
  "dependencies": {
    "ol": "6.9.0"
  },
  "devDependencies": {
    "parcel": "^2.0.0-beta.1"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  }
}

參考資源:

https://openlayers.org/en/latest/examples/feature-move-animation.html

以上就是vue利用openlayers實(shí)現(xiàn)動(dòng)態(tài)軌跡的詳細(xì)內(nèi)容,更多關(guān)于vue openlayers動(dòng)態(tài)軌跡的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue異步更新dom的實(shí)現(xiàn)淺析

    vue異步更新dom的實(shí)現(xiàn)淺析

    Vue中的數(shù)據(jù)更新是異步的,意味著我們?cè)谛薷耐闐ata之后并不能立刻獲取修改后的DOM元素。本文介紹了vue異步更新dom的實(shí)現(xiàn),感興趣的小伙伴們可以參考一下
    2021-07-07
  • Vue中使用video.js實(shí)現(xiàn)截圖和視頻錄制與下載

    Vue中使用video.js實(shí)現(xiàn)截圖和視頻錄制與下載

    這篇文章主要為大家詳細(xì)介紹了Vue中如何使用video.js實(shí)現(xiàn)截圖和視頻錄制與下載,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • VuePress 靜態(tài)網(wǎng)站生成方法步驟

    VuePress 靜態(tài)網(wǎng)站生成方法步驟

    這篇文章主要介紹了VuePress 靜態(tài)網(wǎng)站生成方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • vue實(shí)現(xiàn)tab切換的放大鏡效果

    vue實(shí)現(xiàn)tab切換的放大鏡效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)tab切換的放大鏡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 如何使用yarn創(chuàng)建vite項(xiàng)目+vue3

    如何使用yarn創(chuàng)建vite項(xiàng)目+vue3

    這篇文章主要介紹了如何使用yarn創(chuàng)建vite項(xiàng)目+vue3,詳細(xì)介紹了使用vite創(chuàng)建vue3過程,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • vite+vue3+ts項(xiàng)目新建以及解決遇到的問題

    vite+vue3+ts項(xiàng)目新建以及解決遇到的問題

    vite是一個(gè)基于Vue3單文件組件的非打包開發(fā)服務(wù)器,它具有快速的冷啟動(dòng),不需要等待打包操作,下面這篇文章主要給大家介紹了關(guān)于vite+vue3+ts項(xiàng)目新建以及解決遇到的問題的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • vue同一個(gè)瀏覽器登錄不同賬號(hào)數(shù)據(jù)覆蓋問題解決方案

    vue同一個(gè)瀏覽器登錄不同賬號(hào)數(shù)據(jù)覆蓋問題解決方案

    同一個(gè)瀏覽器登錄不同賬號(hào)session一致,這就導(dǎo)致后面登錄的用戶數(shù)據(jù)會(huì)把前面登錄的用戶數(shù)據(jù)覆蓋掉,這個(gè)問題很常見,當(dāng)前我這邊解決的就是同一個(gè)瀏覽器不同窗口只能登錄一個(gè)用戶,對(duì)vue同一個(gè)瀏覽器登錄不同賬號(hào)數(shù)據(jù)覆蓋問題解決方法感興趣的朋友一起看看吧
    2024-01-01
  • Vue組件之間的通信你知道多少

    Vue組件之間的通信你知道多少

    這篇文章主要為大家詳細(xì)介紹了Vue組件之間的通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 沒有搭建腳手架時(shí)vue組件的使用方式

    沒有搭建腳手架時(shí)vue組件的使用方式

    這篇文章主要介紹了沒有搭建腳手架時(shí)vue組件的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 解決父組件將子組件作為彈窗調(diào)用只執(zhí)行一次created的問題

    解決父組件將子組件作為彈窗調(diào)用只執(zhí)行一次created的問題

    這篇文章主要介紹了解決父組件將子組件作為彈窗調(diào)用只執(zhí)行一次created的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07

最新評(píng)論