<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路径轨迹回放</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<!-- <link rel="stylesheet" href="./node_modules/leafvar/dist/leafvar.css" /> -->
<!-- <script src="./node_modules/leafvar/dist/leafvar.js"></script> -->
</head>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
#mapid {
width: 100%;
height: 100%;
}
.input-card {
z-index: 50;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: .25rem;
width: 8rem;
border-width: 0;
border-radius: 0.4rem;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
position: fixed;
bottom: 1rem;
right: 1rem;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
padding: 0.75rem 1.25rem;
}
</style>
<body>
<div id="mapid" style="z-index: 10"></div>
<div class="input-card">
<button id="run" onclick="start()">run</button>
<button id="stop" onclick="stop()">stop</button>
<button id="pause" onclick="pause()">pause</button>
</div>
<script>
/**
* 为Marker类添加方法
*/
(function() {
// save these original methods before they are overwritten
var proto_initIcon = L.Marker.prototype._initIcon;
var proto_setPos = L.Marker.prototype._setPos;
var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
L.Marker.addInitHook(function() {
var iconOptions = this.options.icon && this.options.icon.options;
var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
if (iconAnchor) {
iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
}
this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center center';
this.options.rotationAngle = this.options.rotationAngle || 0;
// Ensure marker keeps rotated during dragging
this.on('drag', function(e) {
e.target._applyRotation();
});
});
L.Marker.include({
_initIcon: function() {
proto_initIcon.call(this);
},
_setPos: function(pos) {
proto_setPos.call(this, pos);
this._applyRotation();
},
_applyRotation: function() {
if (this.options.rotationAngle) {
this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin;
if (oldIE) {
// for IE 9, use the 2D rotation
this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
} else {
// for modern browsers, prefer the 3D accelerated version
this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
}
}
},
setRotationAngle: function(angle) {
this.options.rotationAngle = angle;
this.update();
return this;
},
setRotationOrigin: function(origin) {
this.options.rotationOrigin = origin;
this.update();
return this;
}
});
})();
var map = L.map('mapid', {
center: [35.952, 94.82],
zoom: 5,
crs: L.CRS.EPSG3857,
layers: [
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
})
]
});
var _opts = {
icon: null,
enableRotation: true //允许小车旋转
};
//移动到当前点的索引
this.i = 0;
var latlngs = [
[35.952, 94.82],
[36.13, 94.77],
[36.5, 94.79],
[36.13, 94.82],
[36.24, 94.78],
[36.17, 94.73],
[35.969, 94.81],
[36.27, 94.79],
[36.96, 94.81],
[36.13, 94.79],
];
var _path = latlngs;
/* 运行轨迹 */
var polyline = L.polyline([], {
color: 'red'
}).addTo(map);
_initPolyline(_path[i], _path[i + 1], _tween)
var latlngsPass = polyline.getLatLngs
var polylinePass = L.polyline([], {
color: 'green'
}).addTo(map);
function start() {
var me = this,
len = me._path.length;
//不是第一次点击开始,并且小车还没到达终点
if (me.i && me.i < len - 1) {
//没按pause再按start不做处理
if (!me._fromPause) {
return;
} else if (!me._fromStop) {
//按了pause按钮,并且再按start,直接移动到下一点
//并且此过程中,没有按stop按钮
//防止先stop,再pause,然后连续不停的start的异常
me._moveNext(++me.i);
}
} else {
//第一次点击开始,或者点了stop之后点开始
polylinePass.setLatLngs([])
me._addMarker();
me._moveNext(me.i);
}
//重置状态
this._fromPause = false;
this._fromStop = false;
}
function _addMarker(callback) {
if (this._marker) {
this.stop();
this._marker.remove();
}
var marker = new L.Marker(_path[0]).addTo(map)
this._marker = marker;
}
/**
* 移动到下一个点
*/
function _moveNext(index) {
var me = this;
if (index < this._path.length - 1) {
this._move(me._path[index], me._path[index + 1], me._tween);
}
}
/**
* 画路线
*/
function _initPolyline(initPos, targetPos, effect) {
var me = this,
//当前的帧数
currentCount = -1,
//步长
step = 0.01,
//总步数
count = Math.round(me._getDistance(initPos[0], initPos[1], targetPos[0], targetPos[1]) / step);
//如果小于1直接移动到下一点
if (count < 1) {
++me.i
if (me.i < me._path.length - 1) {
_initPolyline(me._path[me.i], me._path[me.i + 1], me._tween);
}
return;
}
//两点之间当前帧数大于总帧数的时候,则说明已经完成移动
var loop = true
while (loop) {
if (currentCount >= count) {
loop = false
//移动的点已经超过总的长度
if (me.i > me._path.length) {
me.i = 0
return;
}
//运行下一个点
++me.i;
if (me.i < me._path.length - 1) {
_initPolyline(me._path[me.i], me._path[me.i + 1], me._tween);
me.i = 0
}
} else {
currentCount++;
var x = effect(initPos[0], targetPos[0], currentCount, count),
y = effect(initPos[1], targetPos[1], currentCount, count);
var pos = L.latLng(x, y);
polyline.addLatLng(pos)
}
}
}
/**
* 移动小车
* @param {Number} poi 当前的步长.
* @param {Point} initPos 经纬度坐标初始点.
* @param {Point} targetPos 经纬度坐标目标点.
* @param {Function} effect 缓动效果,实现插值
* @return 无返回值.
*/
function _move(initPos, targetPos, effect) {
var me = this,
//当前的帧数
currentCount = -1,
//步长
timer = 10, //10毫秒为一步
step = 0.01,
//总步数
count = Math.round(me._getDistance(initPos[0], initPos[1], targetPos[0], targetPos[1]) / step);
//如果小于1直接移动到下一点
if (count < 1) {
this._moveNext(++me.i);
return;
}
//两点之间匀速移动
var angle;
me._intervalFlag = setInterval(function() {
//两点之间当前帧数大于总帧数的时候,则说明已经完成移动
if (currentCount >= count) {
clearInterval(me._intervalFlag);
//移动的点已经超过总的长度
++me.i
if (me.i >= me._path.length - 1) {
console.log('move done')
return;
}
//运行下一个点
me._moveNext(me.i);
} else {
currentCount++;
var x = effect(initPos[0], targetPos[0], currentCount, count),
y = effect(initPos[1], targetPos[1], currentCount, count);
var pos = L.latLng(x, y);
//设置marker
if (currentCount == 1) {
if (me._opts.enableRotation == true) {
//initPos=[lat,lng],leafvar中坐标对的格式为(纬度,经度),因此要计算角度的话,X对应经度,即initPos[1]
// angle = me._getAngle(initPos[1], initPos[0], targetPos[1], targetPos[0]);
}
}
//正在移动
me._marker.remove(); //先删除
me._marker.setRotationAngle(angle);
me._marker._latlng = pos; //设置图标位置
me._marker.addTo(map);
polylinePass.addLatLng(pos)
}
}, timer);
}
/**
* 缓动效果
* 初始坐标,目标坐标,当前的步长,总的步长
* @private
*/
function _tween(initPos, targetPos, currentCount, count) {
var b = initPos,
c = targetPos - initPos,
t = currentCount,
d = count;
return c * t / d + b;
}
/**
* 计算两点间的距离
*/
function _getDistance(pxA, pyA, pxB, pyB) {
return Math.sqrt(Math.pow(pxA - pxB, 2) + Math.pow(pyA - pyB, 2));
}
/**
* 计算角度
* @param startx
* @param starty
* @param endx
* @param endy
* @returns {number}
*/
function _getAngle(startx, starty, endx, endy) {
var tan = 0
if (endx == startx) {
tan = 90;
} else {
tan = Math.atan(Math.abs((endy - starty) / (endx - startx))) * 180 / Math.PI;
}
if (endx >= startx && endy >= starty) //第一象限
{
return -tan;
} else if (endx > startx && endy < starty) //第四象限
{
return tan;
} else if (endx < startx && endy > starty) //第二象限
{
return tan - 180;
} else {
return 180 - tan; //第三象限
}
}
/**
* 停止
*/
function stop() {
this.i = 0;
this._fromStop = true;
clearInterval(this._intervalFlag);
}
/**
* 暂停
*/
function pause() {
clearInterval(this._intervalFlag);
//标识是否是按过pause按钮
this._fromPause = true;
}
</script>
</body>
</html>