1.效果图
2.注册账号,获取Key,安装依赖
pnpm add @amap/amap-jsapi-loader --registry=https://registry.npmmirror.com
pnpm add tdesign-vue-next --registry=https://registry.npmmirror.com
3.代码实现
<template>
<div id="container" ref="mapRef" class="map-container h-full"></div>
<div class="absolute handler-bar flex items-center px-30 justify-between">
<img
v-if="animationStatus"
src="@/assets/images/push.png"
class="action-icon cursor-pointer"
@click="changeStatus"
/>
<img
v-else
src="@/assets/images/start.png"
class="action-icon cursor-pointer"
@click="changeStatus"
/>
<div
v-for="(item, index) in speedList"
:key="`speed-${index}`"
class="speed mr-10 cursor-pointer"
:class="speed === item.value ? 'active' : ''"
@click="changeSpeed(item.value)"
>
{{ item.label }}
</div>
<Slider
class="t-slider"
v-model="sliderValue"
:disabled="!initPassed.length"
:tooltip-props="false"
@mousedown="changeMouseStatus('down')"
@mouseup="changeMouseStatus('up')"
@change="sliderChange"
@click="sliderClick"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, shallowRef } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';
import { MessagePlugin, Slider } from 'tdesign-vue-next';
const map = shallowRef(null);
const marker = ref();
const speedList = [
{
label: '1.0x',
value: 1,
},
{
label: '2.0x',
value: 2,
},
{
label: '3.0x',
value: 3,
},
];
const mouseDown = ref(false);
const clickMove = ref(false);
const visible = ref(false);
const sliderValue = ref(0);
const speed = ref(1);
const animationStatus = ref(false);
const initPassed = ref([
[116.478935, 39.997761],
[116.478939, 39.997825],
[116.478912, 39.998549],
[116.478912, 39.998549],
[116.478998, 39.998555],
[116.478998, 39.998555],
[116.479282, 39.99856],
[116.479658, 39.998528],
[116.480151, 39.998453],
[116.480784, 39.998302],
[116.480784, 39.998302],
[116.481149, 39.998184],
[116.481573, 39.997997],
[116.481863, 39.997846],
[116.482072, 39.997718],
[116.482362, 39.997718],
[116.483633, 39.998935],
[116.48367, 39.998968],
[116.484648, 39.999861],
]);
const stopValue = ref(0);
const passedArr = ref([]);
const changeMouseStatus = (mouseStatus) => {
if (mouseStatus === 'down') {
mouseDown.value = true;
pauseAnimation();
}
if (mouseStatus === 'up') {
mouseDown.value = false;
if (animationStatus.value) {
changeStatus('continue');
}
}
};
const changeStatus = (type) => {
if (!initPassed.value || !initPassed.value.length) {
MessagePlugin.warning('实际巡检轨迹为空!');
return;
}
if (type !== 'continue') {
animationStatus.value = !animationStatus.value;
}
if (sliderValue.value === 100) {
sliderValue.value = 0;
}
stopValue.value = sliderValue.value;
const index = Math.ceil(initPassed.value.length * (sliderValue.value / 100));
if (index === 0) {
passedArr.value = [...initPassed.value];
} else {
passedArr.value = initPassed.value.slice(index);
}
if (animationStatus.value) {
setTimeout(() => {
startAnimation();
});
} else {
pauseAnimation();
}
};
const moveend = (info) => {
const { index } = info;
clickMove.value = false;
if (index === passedArr.value.length - 1) {
sliderValue.value = 100;
animationStatus.value = false;
}
};
const movingInfo = (info) => {
if (!mouseDown.value && !clickMove.value) {
const { index, progress } = info;
sliderValue.value = stopValue.value + ((index + progress) / initPassed.value.length) * 100;
if (sliderValue.value === 100) {
animationStatus.value = false;
}
}
};
const sliderClick = () => {
const index = Math.ceil(initPassed.value.length * (sliderValue.value / 100));
const lnglat = initPassed.value[index];
clickMove.value = true;
animationTo(lnglat);
setTimeout(() => {
if (animationStatus.value) {
changeStatus('continue');
}
});
};
const sliderChange = (val) => {
if (mouseDown.value) {
const index = Math.ceil(initPassed.value.length * (val / 100));
const lnglat = initPassed.value[index];
sliderValue.value = val;
animationTo(lnglat);
}
};
const onCancel = () => {
stopAnimation();
animationStatus.value = false;
};
const stopAnimation = () => {
marker.value.stopMove();
};
const changeSpeed = (val) => {
speed.value = val;
pauseAnimation();
setTimeout(() => {
if (animationStatus.value) {
changeStatus('continue');
}
});
};
const open = () => {
visible.value = true;
};
const initMap = () => {
AMapLoader.load({
key: 'YOUR_KEY',
version: '2.0',
plugins: ['AMap.MoveAnimation'],
})
.then((AMap) => {
map.value = new AMap.Map('container', {
pitch: 50,
viewMode: '3D',
terrain: true,
resizeEnable: true,
center: initPassed.value[0],
zoom: 17,
mapStyle: 'amap://styles/normal',
});
marker.value = new AMap.Marker({
map: map.value,
position: initPassed.value[0],
icon: 'https://webapi.amap.com/images/car.png',
offset: new AMap.Pixel(-26, -13),
autoRotation: true,
angle: -90,
});
marker.value.setMap(map.value);
const passedPolyline = new AMap.Polyline({
map: map.value,
path: initPassed.value,
strokeColor: '#AF5',
strokeOpacity: 1,
strokeWeight: 6,
});
const passedPolylineInit = new AMap.Polyline({
map: map.value,
path: initPassed.value,
showDir: true,
strokeColor: '#28F',
strokeWeight: 6,
zIndex: 97,
});
passedPolylineInit.setMap(map.value);
marker.value.on('moving', (e) => {
movingInfo(e);
passedPolyline.setPath(e.passedPath);
map.value.setCenter(e.target.getPosition(), true);
});
marker.value.on('moveend', (e) => {
moveend(e);
});
})
.catch((e) => {
console.log(e);
});
};
const startAnimation = () => {
marker.value.moveAlong(passedArr.value, {
duration: 1000 / speed.value,
autoRotation: true,
});
};
const animationTo = (target) => {
marker.value.moveTo(target, {
duration: 0,
autoRotation: true,
});
};
const pauseAnimation = () => {
marker.value.pauseMove();
};
onMounted(() => {
initMap();
});
defineExpose({ open });
</script>
<style lang="less" scoped>
.h600 {
height: calc(100vh - 80px);
}
.handler-bar {
right: 30px;
bottom: 20px;
z-index: 1;
width: 690px;
background: rgba(0, 0, 0, 0.6);
border-radius: 30px;
height: 53px;
.action-icon {
width: 32px;
height: 32px;
margin-right: 40px;
}
.speed {
background: #dcdcdc;
border-radius: 4px;
height: 32px;
line-height: 32px;
width: 52px;
text-align: center;
color: rgba(0, 0, 0, 0.6);
font-size: 18px;
}
.active {
background: #0052d9;
color: #fff;
}
}
.color-bray {
color: rgba(0, 0, 0, 0.6);
}
.f12 {
font-size: 12px;
}
.color-check {
color: #0052d9;
}
</style>
特别感谢:@亦非余