Vue+Openlayer设置闪烁点LjOlWave-基于postrender机制

参考:openlayers6【二十八】批量设置闪烁点,feature要素通过postrender机制批量生成动态点_@必意玲-CSDN博客

效果图:

实现代码:

<template>
  <div class="test">
    <div id="map" ref="map" style="width: 100vw; height: 100vh"></div>
    <div style="position: fixed; top: 100px; left: 100px">
      <el-button @click="showWaveLayer = true">添加gif图层</el-button>
      <el-button @click="showWaveLayer = false">移除gif图层</el-button>
      <el-button @click="addData()">添加数据</el-button>
      <el-button @click="deleteData()">删除数据</el-button>
    </div>

    <LjOlWave
      v-if="showWaveLayer"
      :map="map"
      :speed="0.35"
      :circleRadius="35"
      :color="[255, 0, 0]"
      :geojsonData="geojsonData"
    ></LjOlWave>
  </div>
</template>
 
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjOlWave from "@/components/LjOlWave/index.vue";
export default {
  components: { LjOlWave },

  data() {
    return {
      map: {},
      geojsonData: {
        type: "FeatureCollection",
        features: [
          {
            type: "Feature",
            properties: {
              title: "警报1",
            },
            geometry: {
              type: "Point",
              coordinates: [91.48879670091165, 37.83814884701121],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报2",
            },
            geometry: {
              type: "Point",
              coordinates: [99.19515576149941, 26.713646654711134],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报3",
            },
            geometry: {
              type: "Point",
              coordinates: [123.74363825288785, 44.363694825734726],
            },
          },
        ],
      },
      showWaveLayer: false,
    };
  },
  mounted() {
    this.initMap();
    this.pointerMove();
    this.getAnimateInfo();
  },
  methods: {
    getAnimateInfo() {
      this.map.on("click", (e) => {
        let feature = this.map.forEachFeatureAtPixel(e.pixel, (feature) => {
          return feature;
        });
        this.$message.success(feature.get("title"));
      });
    },
    pointerMove() {
      // 设置鼠标划过矢量要素的样式
      this.map.on("pointermove", (e) => {
        const isHover = this.map.hasFeatureAtPixel(e.pixel);
        this.map.getTargetElement().style.cursor = isHover ? "pointer" : "";
      });
    },
    addData() {
      this.geojsonData = {
        type: "FeatureCollection",
        features: [
          {
            type: "Feature",
            properties: {
              title: "警报1",
            },
            geometry: {
              type: "Point",
              coordinates: [91.48879670091165, 37.83814884701121],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报2",
            },
            geometry: {
              type: "Point",
              coordinates: [99.19515576149941, 26.713646654711134],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报3",
            },
            geometry: {
              type: "Point",
              coordinates: [123.74363825288785, 44.363694825734726],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报4",
            },
            geometry: {
              type: "Point",
              coordinates: [113.74363825288785, 34.363694825734726],
            },
          },
        ],
      };
    },
    deleteData() {
      this.geojsonData = {
        type: "FeatureCollection",
        features: [
          {
            type: "Feature",
            properties: {
              title: "警报1",
            },
            geometry: {
              type: "Point",
              coordinates: [91.48879670091165, 37.83814884701121],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报2",
            },
            geometry: {
              type: "Point",
              coordinates: [99.19515576149941, 26.713646654711134],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报3",
            },
            geometry: {
              type: "Point",
              coordinates: [123.74363825288785, 44.363694825734726],
            },
          },
        ],
      };
    },
    // 初始化地图
    initMap() {
      this.map = new Map({
        target: document.getElementById("map"),
        layers: [
          new TileLayer({
            source: new OSM(),
          }),
        ],
        view: new View({
          projection: "EPSG:4326",
          center: [104.29806, 30.5263],
          zoom: 4,
        }),
      });
    },
  },
};
</script>

LjOlWave/index.vue:

<template>
  <div/>
</template>
<script>
import "ol/ol.css";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Feature } from "ol";
import { Style, Circle, Stroke } from "ol/style";
import { Point } from "ol/geom";
import { getVectorContext } from "ol/render";
import GeoJSON from "ol/format/GeoJSON";

import { unByKey } from "ol/Observable"; //移除事件

// 边界json数据
export default {
  name: "LjOlWave",
  props: {
    map: {
      type: Object,
      default: () => {},
    },
    speed: {
      type: Number,
      default: 0.35,
    },
    circleRadius: {
      type: Number,
      default: 35,
    },
    geojsonData: {
      type: Object,
      default: () => {},
    },
    color: {
      type: Array,
      default: () => [255, 0, 0],
    },
  },
  data() {
    return {
      pointLayer: {},
      eventRender: {},
    };
  },
  mounted() {
    this.addLayer();
    this.renderAnimate();
  },
  methods: {
    addLayer() {
      if (Object.keys(this.pointLayer).length !== 0) return;

      // 设置图层
      this.pointLayer = new VectorLayer({
        source: new VectorSource({
          features: new GeoJSON().readFeatures(this.geojsonData),
        }),
      });
      // 添加图层
      this.map.addLayer(this.pointLayer);
    },
    removeLayer() {
      if (Object.keys(this.pointLayer).length == 0) return;

      this.pointLayer.getSource().clear();
      this.map.removeLayer(this.pointLayer);
      this.pointLayer = {};
    },
    renderAnimate() {
      if (Object.keys(this.pointLayer).length !== 0) {
        this.pointLayer.getSource().clear();
      }
      // 循环添加feature
      let pointFeatures = [];

      for (let i in this.geojsonData.features) {
        const feature = new Feature({
          geometry: new Point(
            this.geojsonData.features[i].geometry.coordinates
          ),
        });
        pointFeatures = [...pointFeatures, feature];
        feature.setProperties(this.geojsonData.features[i].properties);
      }
      this.pointLayer.getSource().addFeatures(pointFeatures);

      // 关键的地方在此:监听postrender事件,在里面重新设置circle的样式
      let radius = 0;
      this.eventRender = this.pointLayer.on("postrender", (e) => {
        if (radius >= this.circleRadius) radius = 0;
        let opacity = 1 - radius / this.circleRadius; //不透明度

        let pointStyle = new Style({
          image: new Circle({
            radius: radius,
            stroke: new Stroke({
              color: `rgba(${this.color[0]},${this.color[1]},${this.color[2]},${opacity})`,

              width: (this.circleRadius - radius) / 10, //设置宽度
            }),
          }),
        });
        // 获取矢量要素上下文
        let vectorContext = getVectorContext(e);
        vectorContext.setStyle(pointStyle);
        pointFeatures.forEach((feature) => {
          vectorContext.drawGeometry(feature.getGeometry());
        });
        radius = radius + this.speed; //调整闪烁速度
        //请求地图渲染(在下一个动画帧处)
        this.map.render();
      });
    },
  },
  destroyed() {
    unByKey(this.eventRender);
    this.removeLayer();
  },
  watch: {
    geojsonData: {
      handler() {
        unByKey(this.eventRender);
        this.renderAnimate();
      },
      deep: true,
    },
  },
};
</script>

高级封装:

更新数据时,只需传入更新后的geojson即可

实现代码:

<template>
  <div>
    <div id="map" ref="map" style="width: 100vw; height: 100vh"></div>
    <!-- 警报按钮、警报面板 -->
    <div
      style="
        position: fixed;
        top: 40vh;
        left: 23vw;
        z-index: 999;
        width: 50px;
        height: 50px;
      "
    >
      <!-- 警报按钮 -->
      <div
        @click="showWavePanel = !showWavePanel"
        style="
          display: inline-block;
          width: 100%;
          height: 100%;
          display: flex;
          flex-flow: column nowrap;
          align-items: center;
          justify-content: space-around;
          background-color: #08305d;
          cursor: pointer;
          color: white;
        "
      >
        <i class="el-icon-bell" style="font-size: 24px"></i>
        <span style="font-size: 12px">警报</span>
      </div>
      <!-- 警报面板 -->
      <LjOlWave
        v-show="showWavePanel"
        style="position: absolute; top: 0px; left: 60px"
        :map="map"
        :geojsonData="geojsonData"
      ></LjOlWave>
    </div>
  </div>
</template>
 
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjOlWave from "@/components/LjOlWave/index.vue";
export default {
  components: { LjOlWave },

  data() {
    return {
      map: {},
      showWavePanel: false, //显示警报面板
      geojsonData: {
        type: "FeatureCollection",
        features: [
          {
            type: "Feature",
            properties: {
              title: "警报1",
            },
            geometry: {
              type: "Point",
              coordinates: [104.2979, 30.528],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报2",
            },
            geometry: {
              type: "Point",
              coordinates: [104.2987, 30.527],
            },
          },
          {
            type: "Feature",
            properties: {
              title: "警报3",
            },
            geometry: {
              type: "Point",
              coordinates: [104.2974, 30.526],
            },
          },
        ],
      },
    };
  },
  mounted() {
    this.initMap();
    this.pointerMove();
    this.getAnimateInfo();
  },
  methods: {
    getAnimateInfo() {
      this.map.on("click", (e) => {
        let feature = this.map.forEachFeatureAtPixel(e.pixel, (feature) => {
          return feature;
        });
        this.$message.success(feature.get("title"));
      });
    },
    pointerMove() {
      // 设置鼠标划过矢量要素的样式
      this.map.on("pointermove", (e) => {
        const isHover = this.map.hasFeatureAtPixel(e.pixel);
        this.map.getTargetElement().style.cursor = isHover ? "pointer" : "";
      });
    },

    // 初始化地图
    initMap() {
      this.map = new Map({
        target: document.getElementById("map"),
        layers: [
          new TileLayer({
            source: new OSM(),
          }),
        ],
        view: new View({
          projection: "EPSG:4326",
          center: [104.29806, 30.5263],
          zoom: 18,
        }),
      });
    },
  },
};
</script>

LjOlWave/index.vue:

<template>
  <div
    style="z-index: 99999; background-color: rgba(0, 0, 0, 0.48); padding: 10px"
  >
    <div>
      <el-button size="mini" @click="addWaveLayer()">添加警报图层</el-button
      >&nbsp;
      <el-button size="mini" @click="removeWaveLayer()">移除警报图层</el-button>
    </div>
    <br />
    <div style="color: white; font-size: 16px">
      <div
        style="
          display: flex;
          justify-content: space-between;
          align-items: center;
        "
      >
        <span style="width: 60px">半径:</span
        ><el-slider
          v-model="option_wave.radius"
          style="width: 200px"
        ></el-slider>
      </div>
      <div
        style="
          display: flex;
          justify-content: space-between;
          align-items: center;
        "
      >
        速度:<el-slider
          style="width: 200px"
          v-model="option_wave.speed"
          :min="0"
          :max="1"
          :step="0.01"
        ></el-slider>
      </div>
      <div
        style="
          display: flex;
          justify-content: space-between;
          align-items: center;
        "
      >
        颜色:<el-color-picker v-model="colorPick" show-alpha></el-color-picker>
      </div>
    </div>
  </div>
</template>
<script>
import "ol/ol.css";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Feature } from "ol";
import { Style, Circle, Stroke } from "ol/style";
import { Point } from "ol/geom";
import { getVectorContext } from "ol/render";
import GeoJSON from "ol/format/GeoJSON";

import { unByKey } from "ol/Observable"; //移除事件

// 边界json数据
export default {
  name: "LjOlWave",
  props: {
    map: {
      type: Object,
      default: () => {},
    },
    geojsonData: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      pointLayer: {},
      eventRender: {},

      option_wave: {
        radius: 55,
        speed: 0.65,
      },
      colorPick: "rgba(255,0,0,1)",
    };
  },
  computed: {
    colorWave() {
      let r = this.rgbaNum(this.colorPick, 0);
      let g = this.rgbaNum(this.colorPick, 1);
      let b = this.rgbaNum(this.colorPick, 2);

      return [r, g, b];
    },
  },
  methods: {
    addWaveLayer() {
      this.addLayer();
      this.renderAnimate();
    },
    removeWaveLayer() {
      unByKey(this.eventRender);
      this.removeLayer();
    },
    //从rgba字符串中解析出各个值
    rgbaNum(rgba, index) {
      return rgba.match(/(\d(\.\d+)?)+/g)[index];
    },
    addLayer() {
      if (Object.keys(this.pointLayer).length !== 0) return;

      // 设置图层
      this.pointLayer = new VectorLayer({
        source: new VectorSource({
          features: new GeoJSON().readFeatures(this.geojsonData),
        }),
        // zIndex: 9999999,
      });
      // 添加图层
      this.map.addLayer(this.pointLayer);
    },
    removeLayer() {
      if (Object.keys(this.pointLayer).length == 0) return;

      this.pointLayer.getSource().clear();
      this.map.removeLayer(this.pointLayer);
      this.pointLayer = {};
    },
    renderAnimate() {
      if (Object.keys(this.pointLayer).length !== 0) {
        this.pointLayer.getSource().clear();
      }
      // 循环添加feature
      let pointFeatures = [];

      for (let i in this.geojsonData.features) {
        const feature = new Feature({
          geometry: new Point(
            this.geojsonData.features[i].geometry.coordinates
          ),
        });
        pointFeatures = [...pointFeatures, feature];
        feature.setProperties(this.geojsonData.features[i].properties);
      }
      this.pointLayer.getSource().addFeatures(pointFeatures);

      // 关键的地方在此:监听postrender事件,在里面重新设置circle的样式
      let radius = 0;
      this.eventRender = this.pointLayer.on("postrender", (e) => {
        if (radius >= this.option_wave.radius) radius = 0;
        let opacity = 1 - radius / this.option_wave.radius; //不透明度

        let pointStyle = new Style({
          image: new Circle({
            radius: radius,
            stroke: new Stroke({
              color: `rgba(${this.colorWave[0]},${this.colorWave[1]},${this.colorWave[2]},${opacity})`,

              width: (this.option_wave.radius - radius) / 10, //设置宽度
            }),
          }),
        });
        // 获取矢量要素上下文
        let vectorContext = getVectorContext(e);
        vectorContext.setStyle(pointStyle);
        pointFeatures.forEach((feature) => {
          vectorContext.drawGeometry(feature.getGeometry());
        });
        radius += this.option_wave.speed; //调整闪烁速度

        //请求地图渲染(在下一个动画帧处)
        this.map.render();
      });
    },
  },
  watch: {
    geojsonData: {
      handler() {
        unByKey(this.eventRender);
        this.renderAnimate();
      },
      deep: true,
    },
  },
};
</script>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值