箭头动画
TIP
通过不断改变箭头的位置和方向和不断的渲染地图实现箭头动画。
代码如下:
点我查看代码
vue
<template>
<div id="map"></div>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from "vue";
import { Map, View } from "ol";
import { XYZ } from "ol/source";
import TileLayer from "ol/layer/Tile";
import LineString from "ol/geom/LineString";
import Point from "ol/geom/Point";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import Feature from "ol/Feature";
import GeoJSON from "ol/format/GeoJSON";
import { getVectorContext } from "ol/render";
import { Style, Icon } from "ol/style";
import { ATTRIBUTIONS, MAPURL } from "/constants";
import {
textStyle,
buttomPathStyle,
upperPathStyle,
outStyle,
midStyle,
innerDot,
foutrStyle,
fmidStyle,
finnerStyle,
} from "./styles";
import soul from "/public/json/soul.json";
import arrow from "/image/arrow.png";
let map = null;
const initMap = () => {
const tileLayer = new TileLayer({
source: new XYZ({
attributions: ATTRIBUTIONS,
url: MAPURL,
maxZoom: 20,
}),
});
map = new Map({
target: "map",
layers: [tileLayer],
view: new View({
center: [11936406.337013, 3786384.633134],
zoom: 5,
constrainResolution: true,
}),
});
const vSource = new VectorSource();
const vLayer = new VectorLayer({
source: vSource,
});
const geojsonFormat = new GeoJSON();
const features = geojsonFormat.readFeatures(soul, {
dataProjection: "EPSG:4326",
featureProjection: "EPSG:3857",
});
const street = features[0];
map.addLayer(vLayer);
map.getView().fit(street.getGeometry());
street.setStyle(textStyle);
vSource.addFeature(street);
let offset = 0.01;
tileLayer.on("postrender", (evt) => {
const vct = getVectorContext(evt);
vct.drawFeature(street, buttomPathStyle);
vct.drawFeature(street, upperPathStyle);
const numArr = Math.ceil(
street.getGeometry().getLength() / map.getView().getResolution() / 100
);
const points = [];
for (let i = 0; i <= numArr; i++) {
let fracPos = i / numArr + offset;
if (fracPos > 1) fracPos -= 1;
const pf = new Feature(
new Point(street.getGeometry().getCoordinateAt(fracPos))
);
points.push(pf);
}
//确定方向并绘制
street.getGeometry().forEachSegment((start, end) => {
points.forEach((item) => {
const line = new LineString([start, end]);
const coord = item.getGeometry().getFirstCoordinate();
const cPoint = line.getClosestPoint(coord);
if (
Math.abs(cPoint[0] - coord[0]) < 1 &&
Math.abs(cPoint[1] - coord[1]) < 1
) {
const myImage = new Image(120, 120);
myImage.src = arrow;
const dx = end[0] - start[0];
const dy = end[1] - start[1];
let rotation = Math.atan(dx / dy);
rotation = dy > 0 ? rotation : Math.PI + rotation;
vct.setStyle(
new Style({
image: new Icon({
img: myImage,
imgSize: [120, 120],
scale: 0.4,
rotation: rotation,
offset: [0, 0],
}),
})
);
vct.drawGeometry(item.getGeometry());
}
});
vct.setStyle(outStyle);
vct.drawGeometry(new Point(street.getGeometry().getFirstCoordinate()));
vct.setStyle(midStyle);
vct.drawGeometry(new Point(street.getGeometry().getFirstCoordinate()));
vct.setStyle(innerDot);
vct.drawGeometry(new Point(street.getGeometry().getFirstCoordinate()));
vct.setStyle(foutrStyle);
vct.drawGeometry(new Point(street.getGeometry().getLastCoordinate()));
vct.setStyle(fmidStyle);
vct.drawGeometry(new Point(street.getGeometry().getLastCoordinate()));
vct.setStyle(finnerStyle);
vct.drawGeometry(new Point(street.getGeometry().getLastCoordinate()));
});
offset = offset + 0.003;
//复位
if (offset >= 1) offset = 0.001;
map.render();
});
};
onMounted(() => {
initMap();
});
onBeforeUnmount(() => {
if (map) {
map.dispose();
map = null;
}
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
height: 650px;
}
</style>
styles 代码如下:
点我查看代码
ts
import { Fill, Stroke, Circle, Style, Text } from 'ol/style'
import { AUTHOR_INFO } from '../../../constants'
export const textStyle = new Style({
text: new Text({
font: 'bold 26px Mirosoft Yahei',
placement: 'line',
text: AUTHOR_INFO.NAME.split('').join(' '),
fill: new Fill({
color: '#000',
}),
offsetY: 3,
stroke: new Stroke({
color: '#FFF',
width: 2,
}),
}),
})
export const buttomPathStyle = new Style({
stroke: new Stroke({
color: [4, 110, 74],
width: 28,
}),
})
export const upperPathStyle = new Style({
stroke: new Stroke({
color: [0, 186, 107],
width: 20,
}),
})
export const outStyle = new Style({
image: new Circle({
radius: 18,
fill: new Fill({
color: [4, 110, 74],
}),
}),
})
export const midStyle = new Style({
image: new Circle({
radius: 15,
fill: new Fill({
color: [0, 186, 107],
}),
}),
})
export const innerDot = new Style({
image: new Circle({
radius: 6,
fill: new Fill({
color: [255, 255, 255],
}),
}),
})
export const foutrStyle = new Style({
image: new Circle({
radius: 18,
fill: new Fill({
color: '#000',
}),
}),
})
export const fmidStyle = new Style({
image: new Circle({
radius: 15,
fill: new Fill({
color: '#FFF',
}),
}),
})
export const finnerStyle = new Style({
image: new Circle({
radius: 6,
fill: new Fill({
color: '#000',
}),
}),
})