- 绘制组件 MapTrackDraw.vue
<template>
<div></div>
</template>
<script lang='ts' setup>
import { ref, reactive,onMounted, nextTick } from 'vue'
import "ol/ol.css";
import { Map, View, Feature, Overlay } from "ol";
import { Vector as VectorLayer } from "ol/layer";
import { Vector as VectorSource } from "ol/source";
import { Point, LineString } from "ol/geom.js";
import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
import { getVectorContext } from "ol/render";
import { ElMessage } from 'element-plus';
interface Props {
map:any,
view:any
}
const { map,view } = defineProps<Props>()
let geometryMove:any = {}
let featureMove:any = {}
let vectorLayer:any = ref(null)
let distance:number = 0
let lastTime:number = 0
let speed:number = 0.1
let styles:any = {
route: new Style({
stroke: new Stroke({
width: 8,
color: "green",
}),
}),
iconStart: new Style({
image: new Icon({
anchor: [0.5, 1],
src: require('@/assets/images/start_point.png'),
scale: 1, //设置大小
}),
}),
iconEnd: new Style({
image: new Icon({
anchor: [0.5, 1],
src: require('@/assets/images/end_point.png'),
scale: 1, //设置大小
}),
}),
featureMove: new Style({
image: new Icon({
anchor: [0.5, 1],
src: require('@/assets/images/walk-icon.png'),
scale: 1, //设置大小
}),
// image: new Circle({
// radius: 7,
// fill: new Fill({ color: "black" }),
// stroke: new Stroke({
// color: "white",
// width: 2,
// }),
// }),
}),
}
let route:any = null
const drawHandle = (coordinateArr:any[]) => {
clearDrawHandle()
view.setCenter(coordinateArr[0])
route = new LineString(coordinateArr)
geometryMove = new Point(route.getFirstCoordinate())
featureMove = new Feature({
type: "featureMove",
geometry: geometryMove,
})
vectorLayer.value = new VectorLayer({
source: new VectorSource({
features: [
new Feature({
type: "route",
geometry: route,
}),
featureMove,
new Feature({
type: "iconStart",
geometry: new Point(route.getFirstCoordinate()),
}),
new Feature({
type: "iconEnd",
geometry: new Point(route.getLastCoordinate()),
}),
],
}),
style: (feature:any) => {
if(feature.get("type") == 'route') {
feature.setStyle(arrowLineStyles)
return
}
return styles[feature.get("type")];
},
})
map.addLayer(vectorLayer.value)
}
// 清除绘制
const clearDrawHandle = () => {
if(vectorLayer.value) {
// vectorLayer.value.getSource().clear()
map.removeLayer(vectorLayer.value)
vectorLayer.value = null
}
}
// 移动
const moveFeature = (e:any) => {
let time = e.frameState.time;
distance =
(distance + (speed * (time - lastTime)) / 1000) % 1; //%2表示:起止止起;%1表示:起止起止
lastTime = time;
const currentCoordinate = route.getCoordinateAt(
distance > 1 ? 2 - distance : distance
);
geometryMove.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(e);
vectorContext.setStyle(styles.featureMove);
vectorContext.drawGeometry(geometryMove);
map.render();
}
// 动画开始
const startAnimation = () => {
if(vectorLayer.value) {
lastTime = Date.now();
vectorLayer.value.on("postrender", moveFeature);
featureMove.setGeometry(null); //必须用null,不能用{}
}else {
ElMessage.warning('请先绘制路线!')
}
}
// 动画结束
const stopAnimation = () => {
featureMove.setGeometry(geometryMove);
vectorLayer.value.un("postrender", moveFeature);
}
// 箭头样式
const arrowLineStyles = (feature:any, resolution:any) => {
let styles = [];
// 线条样式
let backgroundLineStyle = new Style({
stroke: new Stroke({
width: 10,
color: "green",
}),
});
styles.push(backgroundLineStyle);
let geometry = feature.getGeometry();
// 获取线段长度
const length = geometry.getLength();
// 箭头间隔距离(像素)
const step = 50;
// 将间隔像素距离转换成地图的真实距离
const StepLength = step * resolution;
// 得到一共需要绘制多少个 箭头
const arrowNum = Math.floor(length / StepLength);
const rotations:any = [];
const distances = [0];
geometry.forEachSegment(function (start:any, end:any) {
let dx = end[0] - start[0];
let dy = end[1] - start[1];
let rotation = Math.atan2(dy, dx);
distances.unshift(Math.sqrt(dx ** 2 + dy ** 2) + distances[0]);
rotations.push(rotation);
});
// 利用之前计算得到的线段矢量信息,生成对应的点样式塞入默认样式中
// 从而绘制内部箭头
for (let i = 1; i < arrowNum; ++i) {
const arrowCoord = geometry.getCoordinateAt(i / arrowNum);
const d = i * StepLength;
const grid = distances.findIndex((x) => x <= d);
styles.push(
new Style({
geometry: new Point(arrowCoord),
image: new Icon({
src: require('@/assets/images/right-icon.png'),
opacity: 1,
anchor: [0.5, 0.5],
rotateWithView: false,
// 读取 rotations 中计算存放的方向信息
rotation: -rotations[distances.length - grid - 1],
scale: 0.8,
}),
})
);
}
return styles;
}
defineExpose({
drawHandle,
clearDrawHandle,
startAnimation,
stopAnimation
})
</script>
<style scoped lang='less'>
</style>
- 使用组件
<template>
<div class="map-box" id="map" :key="keyMap">
<MapTrackDraw ref="mapTrackDrawRef" :map="myMap" :view="myView" :key="myMap" />
<el-button type="primary" @click="drawHandle">绘制</el-button>
<el-button type="primary" @click="clearDrawHandle">清除绘制</el-button>
<el-button type="primary" @click="startAnimation()">路线动画开始</el-button>
<el-button type="primary" @click="stopAnimation()">路线动画暂停</el-button>
</div>
</template>
<script lang='ts' setup>
//引入组件
import MapTrackDraw from '@/views/components/MapTrackDraw.vue'
import { ref, reactive, onMounted, nextTick,watch } from 'vue'
//导入相关配置信息
import 'ol/css';
import { Map, View, Feature } from 'ol'
import { Style, Icon } from 'ol/style'
import { Point } from 'ol/geom';
import { Vector as SourceVec, XYZ, } from 'ol/source'
import { Vector as LayerVec } from 'ol/layer'
import TileLayer from 'ol/layer/Tile'
import { defaults as defaultControls, MousePosition, } from "ol/control"
let trackData = [
[120.13422983645752, 30.331830188982277],
[120.13446587085083, 30.330585643999367],
[120.13459461688355, 30.33000628685215],
[120.13508814334229, 30.329941913835793],
[120.13598936557129, 30.33002774452427],
[120.1382424211438, 30.33025305008152],
[120.13861793040589, 30.329394743196755],
[120.13873594760254, 30.328557893984108],
[120.13883250712708, 30.327688858263283],
[120.1377918100293, 30.327624485246925],
[120.13711619158958, 30.32857207778663]
]
const mapTrackDrawRef = ref()
const drawHandle = () => {
mapTrackDrawRef.value.drawHandle(trackData)
}
const clearDrawHandle = () => {
mapTrackDrawRef.value.clearDrawHandle()
}
const startAnimation = () => {
mapTrackDrawRef.value.startAnimation()
}
const stopAnimation = () => {
mapTrackDrawRef.value.stopAnimation()
}
let myMap: any = ref(12)
let myView: any = ref(null)
let keyMap: any = ref(Math.random())
let vectorLayer: any = null
onMounted(() => {
// console.log('initMap')
nextTick(()=>{
myMap.value = new Map({
target: 'map',
//图层数组 layers
layers: [
new TileLayer({
source: new XYZ({
crossOrigin: 'anonymous',
url: 'https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=37c72a79fe4c6a1b3fa6b1435214b378'
})
}),
new TileLayer({
source: new XYZ({
crossOrigin: 'anonymous',
url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=37c72a79fe4c6a1b3fa6b1435214b378'
})
})
],
//视图 View
view: new View({
projection: "EPSG:4326",
center: [120.15373797456354, 30.291315691648734],
zoom: 15,
maxZoom: 17,
minZoom: 3,
}),
//默认控件
controls: defaultControls({
zoom: false,
rotate: false,
attribution: false,
}).extend([
//添加新控件
// new MousePosition(),
])
})
// 获取地图视图
myView.value = myMap.value.getView()
// myMap.on('singleclick', function (e: any) {
// setMarker(e.coordinate)
// })
})
})
</script>
<style scoped lang='less'>
.map-box {
width: 100%;
height: 600px;
position: relative;
}
</style>
-
效果
效果
网友评论