leaflet 点击按钮播放轨迹

<!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 {
         100%;
        height: 100%;
    }
    
    .input-card {
        z-index: 50;
        display: flex;
        flex-direction: column;
        min- 0;
        word-wrap: break-word;
        background-color: #fff;
        background-clip: border-box;
        border-radius: .25rem;
         8rem;
        border- 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: '&copy; <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>
原文地址:https://www.cnblogs.com/houBlogs/p/15718315.html