首页 > 其他分享 >在vue3中使用openlayers3实现track轨迹动画

在vue3中使用openlayers3实现track轨迹动画

时间:2023-12-08 15:57:24浏览次数:23  
标签:map openlayers3 const track EPSG value passCoordinates vue3 new

网上太多资料代码,抄来抄去,而且版本也是OL2的,部分API已经弃用
基础知识不多说,直接讲重点

  1. 三个关键变量
//  记录开始动画的时间
const startTime = ref(0);
// 轨迹分割的颗粒度,数值越小分的越细
const particle = 20;
// 轨迹动画的速度,数值越大位移越快
const speed = 10;
  1. 根据给定的轨迹线路,确定轨道上每个位移点
  const trackLine = new LineString(coordinates);
  // 轨迹在投影平面上的长度
  const trackLineLen = trackLine.getLength();
  // 当前平面的分辨率
  const resolution = map.value.getView().getResolution();
  // 点有可能是小数,要到终点需要手动添加最后一个点
  const pointCount = trackLineLen / (resolution * particle);
  for (let i = 0; i <= pointCount; i++) {
    passCoordinates.value.push(trackLine.getCoordinateAt(i / pointCount));
  }
  passCoordinates.value.push(coordinates.at(-1)!);
  1. 将起点、终点、轨迹、运动小车样式添加到layer上
 // 设置运动小车样式,并添加ID,供后续逻辑找到此geometry 
  const geoMarker = new Feature({
    type: "geoMarker",
    geometry: new Point(passCoordinates.value[0]!)
  });
  geoMarker.setId("point");

  featureLayer.value = new VectorLayer({
    source: new Vector({
      features: [trackFeature, geoMarker, startMarker, endMarker]
    }),
    style: (feature) => {
      return styles[feature.get("type")];
    }
  });

  map.value.addLayer(featureLayer.value);
  1. 绑定事件render监听事件,记录开始时间
  startTime.value = new Date().getTime();
  map.value.on("postrender", move);
  // 第一次需要手动调用一遍,否则不执行postcompose
  map.value.render();
  1. 动画逻辑
const move = (evt: RenderEvent) => {
  const frameState = evt.frameState;
  // 执行动画已经过了多少时间(秒)
  const timeout = (frameState!.time - startTime.value) / 1000;
  let count = Math.round(speed * timeout);

  if (count >= passCoordinates.value.length - 1) {
    // 确保到达最后一个点位,并停止移动动画
    count = passCoordinates.value.length - 1;
    stopMove();
  }
  const point = featureLayer.value.getSource().getFeatureById("point");
  // point.setGeometry(new Point(passCoordinates.value[count]));
  point.getGeometry().setCoordinates(passCoordinates.value[count]);
  map.value.render();
};

完整代码:

<template>
  <div class="map" id="map" ref="myMap"></div>
  <button @click="startMove">start animate</button>
</template>

<script setup lang="ts">
import { Feature, Map, View } from "ol";
import { Coordinate } from "ol/coordinate";
import { LineString, Point } from "ol/geom";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import { transform } from "ol/proj";
import RenderEvent from "ol/render/Event";
import { Vector, XYZ } from "ol/source";
import { Circle, Fill, Icon, Stroke, Style } from "ol/style";
import { onBeforeUnmount, onMounted, ref } from "vue";
import markIcon from "../assets/mark.png";

const map = ref();
const featureLayer = ref();
const passCoordinates = ref<Coordinate[]>([]);

//  记录开始动画的时间
const startTime = ref(0);
// 轨迹分割的颗粒度,数值越小分的越细
const particle = 20;
// 轨迹动画的速度,数值越大位移越快
const speed = 10;

onMounted(() => {
  initMap();
  addTrack();
});
const initMap = () => {
  const amap = new TileLayer({
    source: new XYZ({
      url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
      wrapX: false
    })
  });
  map.value = new Map({
    target: "map",
    layers: [amap],
    view: new View({
      center: transform([114.3, 30.5], "EPSG:4326", "EPSG:3857"),
      zoom: 10,
      minZoom: 3
    })
  });
};

const coordinates = [
  transform([114.0, 30.0], "EPSG:4326", "EPSG:3857"),
  transform([114.1, 30.1], "EPSG:4326", "EPSG:3857"),
  transform([114.2, 30.1], "EPSG:4326", "EPSG:3857"),
  transform([114.2, 30.4], "EPSG:4326", "EPSG:3857"),
  transform([114.4, 30.4], "EPSG:4326", "EPSG:3857")
];

const addLayer = (trackLine: any) => {
  const trackFeature = new Feature({
    type: "track",
    geometry: trackLine
  });
  const geoMarker = new Feature({
    type: "geoMarker",
    geometry: new Point(passCoordinates.value[0]!)
  });
  geoMarker.setId("point");

  const startMarker = new Feature({
    type: "icon",
    geometry: new Point(passCoordinates.value[0]!)
  });
  const endMarker = new Feature({
    type: "icon",
    geometry: new Point(passCoordinates.value.at(-1)!)
  });

  const styles: { [k in string]: Style } = {
    track: new Style({
      stroke: new Stroke({
        width: 6,
        color: [220, 30, 60, 0.9]
      })
    }),
    icon: new Style({
      image: new Icon({
        anchor: [0.5, 1],
        scale: 0.4,
        src: markIcon
      })
    }),
    geoMarker: new Style({
      image: new Circle({
        radius: 8,
        fill: new Fill({ color: "#333" }),
        stroke: new Stroke({
          color: "#f00",
          width: 2
        })
      })
    })
  };

  featureLayer.value = new VectorLayer({
    source: new Vector({
      features: [trackFeature, geoMarker, startMarker, endMarker]
    }),
    style: (feature) => {
      return styles[feature.get("type")];
    }
  });

  map.value.addLayer(featureLayer.value);
};

const move = (evt: RenderEvent) => {
  const frameState = evt.frameState;
  // 执行动画已经过了多少时间(秒)
  const timeout = (frameState!.time - startTime.value) / 1000;
  let count = Math.round(speed * timeout);

  if (count >= passCoordinates.value.length - 1) {
    // 确保到达最后一个点位,并停止移动动画
    count = passCoordinates.value.length - 1;
    stopMove();
  }
  const point = featureLayer.value.getSource().getFeatureById("point");
  // point.setGeometry(new Point(passCoordinates.value[count]));
  point.getGeometry().setCoordinates(passCoordinates.value[count]);
  map.value.render();
};

const startMove = () => {
  startTime.value = new Date().getTime();
  map.value.on("postrender", move);
  // 第一次需要手动调用一遍,否则不执行postcompose
  map.value.render();
};

const stopMove = () => {
  map.value.un("postrender", move);
};

const addTrack = () => {
  const trackLine = new LineString(coordinates);
  // 轨迹在投影平面上的长度
  const trackLineLen = trackLine.getLength();
  // 当前平面的分辨率
  const resolution = map.value.getView().getResolution();

  // 点有可能是小数,要到终点需要手动添加最后一个点
  const pointCount = trackLineLen / (resolution * particle);
  for (let i = 0; i <= pointCount; i++) {
    passCoordinates.value.push(trackLine.getCoordinateAt(i / pointCount));
  }
  passCoordinates.value.push(coordinates.at(-1)!);

  addLayer(trackLine);
};

onBeforeUnmount(stopMove);
</script>

<style lang="scss" scoped>
.map {
  width: 100vw;
  height: 80vh;
}
</style>
Ï

标签:map,openlayers3,const,track,EPSG,value,passCoordinates,vue3,new
From: https://www.cnblogs.com/rion1234567/p/17888295.html

相关文章

  • vue2、vue3适配大屏。分辨率变化,样式不变
    一、vue3适配大屏的,创建一个叫useDraw.jsexportdefaultfunction(){constscale={width:'1',height:'1',}constbaseWidth=1920constbaseHeight=1080constbaseProportion=parseFloat((baseWidth/baseHeight).toFixed(5......
  • vue3组件通信
    子传父$emit在vue框架中事件分为两种:一种是原生的DOM事件,另外一种自定义事件。原生DOM事件可以让用户与网页进行交互,比如click、change、mouseenter、mouseleave...自定义事件可以实现子组件给父组件传递数据。vue2中的@click绑定的是自定义事件,可以通过.native修饰符变为原生DOM......
  • vue3视频播放器组件vue-video-player
    1、安装npmivue3-video-play--save2、全局注册importvue3videoPlayfrom'vue3-video-play'//引入组件import'vue3-video-play/dist/style.css'//引入cssapp.use(vue3videoPlay)3、使用<vue3VideoPlaywidth="1210px"......
  • vue3组件通信Props()
    vue3组件通信父传子defineProps()在使用 <scriptsetup> 的单文件组件中,props可以使用 defineProps() 宏来声明://父<HelloWorldmsg="Youdidit!"/><!--根据一个变量的值动态传入-->//<BlogPost:title="post.title"/>//子<scriptsetup>//写法1......
  • uni-app 基础架构搭建 ts+vue3 命令行
    1. 安装全局degitnpminstall-gdegit2.创建工程my-vue3-ts-project#创建以javascript开发的工程npxdegitdcloudio/uni-preset-vue#vitemy-vue3-ts-project#创建以ts开发的工程npxdegitdcloudio/uni-preset-vue#vite-tsmy-vue3-ts-project3进入目录cdmy-v......
  • VUE3引入pinia配置使用
    文档:https://pinia.vuejs.org/zh/introduction.html1.引入pinnanpminstallpinia-S2.在src文件里面创建store文件article.js在main.js中引用pinnaimport{defineStore}from'pinia'//你可以对`defineStore()`的返回值进行任意命名,但最好使用store的名字,同时以......
  • Vue3+Vite+ElementPlus管理系统常见问题
     本文本记录了使用Vue3+Vite+ElementPlus从0开始搭建一个前端工程会面临的常见问题,没有技术深度,但全都是解决实际问题的干货,可以当作是问题手册以备后用。本人日常工作偏后端开发,因此,文中的一些前端术语描述可能不严谨,敬请谅解。重点是:这里记录的解决方案都是行之有效果的,拿来......
  • Vue3+Vite+ElementPlus管理系统常见问题
    本文本记录了使用Vue3+Vite+ElementPlus从0开始搭建一个前端工程会面临的常见问题,没有技术深度,但全都是解决实际问题的干货,可以当作是问题手册以备后用。本人日常工作偏后端开发,因此,文中的一些前端术语描述可能不严谨,敬请谅解。重点是:这里记录的解决方案都是行之有效果的,拿来即......
  • vue3引入mitt(eventBus)
    版本"mitt":"^3.0.1"1、npminstallmitt2、项目下创建文件夹eventBus建myEventBus.jsimportmittfrom'mitt'exportdefaultmitt() 3、组件里监听组件AimportmyEventBusfrom"../eventBus/myEventBus";myEventBus.on('closeVisit�......
  • vue3 之 封装hooks
    注意:使用Hooks来做的话,需要封装一个以use开头的函数,自定义Hooks有一个潜规则,就是要use开头一、相关链接①已经封装好可直接使用的:https://vueuse.org/core/useMounted/② 为什么要在Vue3中多使用Hooks?好处是啥?: https://zhua......