<!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>