Design sketch:
Explain:
1. This function is based on this blog: Path path playback with ArcGIS JS API But there are some small problems: first of all, the car does not move at a constant speed, but the running time of each section is fixed, so it has been modified on this blog; on the other hand, there is no way to set the rotation angle of the icon in Leaflet, so the Marker class needs to be expanded first.
2. Also refer to Baidu map road book open source library , I wanted to use js files directly, but many of them are bound to Bmap objects and cannot be used directly.
3. Baidu open source library first converts the latitude and longitude coordinates to plane coordinates and then interpolates them, and then converts them to latitude and longitude coordinates. The purpose of doing this is only to calculate the real distance, and then carry out the real simulation according to the set speed. Leaflet does not provide the method of converting the geographical coordinates to plane coordinates, so it directly uses the latitude and longitude to interpolate, so it goes back to The release speed is only a relative speed.
Implementation code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Path path playback</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="./lib/leaflet/leaflet.css" /> <script src="./lib/leaflet/leaflet.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> /** * Add methods to Marker class */ (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: [38.8631169, 2.3708919], 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 //Allow trolley to rotate }; //Index moved to current point this.i = 0; var latlngs = [ [45.51, 2.3708919], [37.77, 8.54235], [34.04, 9.52532], [36.04, 10.52532], [40.04, 14.52532], [47.04, 15.52532] ]; var _path = latlngs; var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); var myIcon = L.icon({ iconUrl: 'car.png', iconSize: [24, 24] }); function start(){ var me = this, len = me._path.length; //It's not the first time to click on the start, and the car hasn't reached the end yet if (me.i && me.i < len - 1) { //Don't press pause and then press start if (!me._fromPause) { return; }else if(!me._fromStop){ //Press the pause button, and then press start to move to the next point //In the process, the stop button is not pressed //Prevent the exception of stop first, then pause, and then start continuously me._moveNext(++me.i); } }else { //Click start for the first time, or click stop to start me._addMarker(); me._moveNext(me.i); } //reset state this._fromPause = false; this._fromStop = false; } function _addMarker(callback) { if (this._marker) { this.stop(); this._marker.remove(); } var marker =new L.Marker(_path[0],{icon: myIcon }).addTo(map) this._marker = marker; } /** * Move to next point */ function _moveNext(index) { var me = this; if (index < this._path.length - 1) { this._move(me._path[index], me._path[index + 1], me._tween); } } /** * Mobile car * @param {Number} poi Current step size * @param {Point} initPos Initial point of latitude and longitude coordinate * @param {Point} targetPos Longitude and latitude coordinate target point * @param {Function} effect Slow motion for interpolation * @return No return value */ function _move(initPos,targetPos,effect) { var me = this, //Current frames currentCount = 0, //step timer = 10, //10 ms in one step step = 0.1, //Total steps count = Math.round(me._getDistance(initPos[0], initPos[1],targetPos[0],targetPos[1]) / step); //If less than 1, move directly to the next point if (count < 1) { this._moveNext(++me.i); return; } //Moving evenly between two points var angle; me._intervalFlag = setInterval(function() { //When the current number of frames between two points is greater than the total number of frames, the move is completed if (currentCount >= count) { clearInterval(me._intervalFlag); //The point moved has exceeded the total length if(me.i > me._path.length){ return; } //Run next point 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); //Set marker if(currentCount == 1){ if(me._opts.enableRotation == true){ //Initpos = [lat, LNG], the format of coordinate pair in leaflet is (latitude, longitude), so to calculate the angle, X corresponds to longitude, that is, initPos[1] angle = me._getAngle(initPos[1], initPos[0],targetPos[1],targetPos[0]); } } //Moving me._marker.remove();//Delete first me._marker.setRotationAngle(angle); me._marker._latlng = pos;//Set icon location me._marker.addTo(map); } },timer); } /** * Slow motion effect * Initial coordinate, target coordinate, current step, total step * @private */ function _tween(initPos, targetPos, currentCount, count) { var b = initPos, c = targetPos - initPos, t = currentCount, d = count; return c * t / d + b; } /** * Calculate the distance between two points */ function _getDistance(pxA,pyA, pxB,pyB) { return Math.sqrt(Math.pow(pxA - pxB, 2) + Math.pow(pyA - pyB, 2)); } /** * Calculating angle * @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; console.log(tan); } if (endx >= startx && endy >= starty)//First quadrant { return -tan; } else if (endx > startx && endy < starty)//Delta Quadrant { return tan; } else if (endx < startx && endy > starty)//Beta Quadrant { return tan - 180; } else { return 180 - tan; //third quadrant } } /** * Stop it */ function stop() { this.i = 0; this._fromStop = true; clearInterval(this._intervalFlag); } /** * suspend */ function pause() { clearInterval(this._intervalFlag); //Mark whether the pause button has been pressed this._fromPause = true; } </script> </body> </html>
File download address: https://download.csdn.net/download/wml00000/10769999