zoukankan      html  css  js  c++  java
  • leaflet地图和飞线

    gif:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>js地图-leaflet</title>
    <link href="https://cdn.bootcss.com/leaflet/1.3.1/leaflet.css" rel="stylesheet">
    <style type="text/css">
    .m-map { 486px;height: 302px;}
    .leaflet-control{display: none;}
    .leaflet-right{direction: none;}
    </style>
    </head>
    <body>
    <div id="map" class="m-map"></div>
    <script src="https://cdn.bootcss.com/leaflet/1.3.1/leaflet.js"></script>
    <script src="src.js"></script>
    <script type="text/javascript">
        var map = L.map('map').setView([51.505, -0.09], 1);;
        var positron = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        }).addTo(map);
     
        L.marker([51.5, -0.09]).addTo(map)
            .bindPopup('这里是伦敦')
            .openPopup();
    
        var data = [{"from":[-0.09,51.505],"to":[116.3,39.9],"labels":["伦敦","北京"],"color":"#ff3a31"}];
    
        var migrationLayer = new L.migrationLayer({
            map: map,
            data: data,
            pulseRadius:30,
            pulseBorderWidth:3,
            arcWidth:1,
            arcLabel:true,
            arcLabelFont:'10px sans-serif',
            }
        );
        migrationLayer.addTo(map);        
    </script>
    </body>
    </html>

    src.js:

    (function (window) {
        var utils = {
            // color:rgb或rgba格式
            // opacity: 透明度
            calculateColor: function (color, opacity) {
                if (color.indexOf('#') === 0) {
                    var color16 = color.slice(1);
                    var r = parseInt(color16.slice(0, 2), 16);
                    var g = parseInt(color16.slice(2, 4), 16);
                    var b = parseInt(color16.slice(4), 16);
                    return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')';
                } else if (/^rgb(/.test(color)) {
                    return color.replace(/rgb/, 'rgba').replace(')', ",") +
                        opacity + ')';
                } else {
                    return color.split(',').splice(0, 3).join(',') +
                        opacity + ')';
                }
            }
        };
        var arrayUtils = {
            forEach: function (arr, cb, scope) {
                if (typeof Array.prototype.forEach === 'function') {
                    arr.forEach(cb, scope);
                } else {
                    for (var i = 0, len = arr.length; i < len; i++) {
                        cb.apply(scope, [arr[i], i, arr]);
                    }
                }
            },
            map: function (arr, cb, scope) {
                if (typeof Array.prototype.map === 'function') {
                    return arr.map(cb, scope);
                } else {
                    var mapped = [];
                    for (var i = 0, len = arr.length; i < len; i++) {
                        mapped[i] = cb.apply(scope, [arr[i], i, arr]);
                    }
                    return mapped;
                }
            }
        };
    
        var Marker = (function () {
            var M = function (options) {
                this.x = options.x;
                this.y = options.y;
                this.rotation = options.rotation;
                this.style = options.style;
                this.color = options.color;
                this.size = options.size;
                this.borderWidth = options.borderWidth;
                this.borderColor = options.borderColor;
            };
    
            M.prototype.draw = function (context) {
                context.save();
                context.translate(this.x, this.y);
                context.rotate(this.rotation);
    
                context.lineWidth = this.borderWidth || 0;
                context.strokeStyle = this.borderColor || '#000';
                context.fillStyle = this.color || '#000';
                // 目前先只支持圆
                context.beginPath();
                if (this.style === 'circle') {
                    context.arc(0, 0, this.size, 0, Math.PI * 2, false);
                } else if (this.style === 'arrow') {
                    context.moveTo(-this.size, -this.size);
                    context.lineTo(this.size, 0);
                    context.lineTo(-this.size, this.size);
                    context.lineTo(-this.size / 4, 0);
                    context.lineTo(-this.size, -this.size);
                }
                context.closePath();
                context.stroke();
                context.fill();
                context.restore();
            };
    
            return M;
        })();
    
        var Arc = (function () {
            var A = function (options) {
                var startX = options.startX,
                    startY = options.startY,
                    endX = options.endX,
                    endY = options.endY;
                //两点之间的圆有多个,通过两点及半径便可以定出两个圆,根据需要选取其中一个圆
                var L = Math.sqrt(Math.pow(startX - endX, 2) + Math.pow(startY - endY, 2));
                var m = (startX + endX) / 2; // 横轴中点
                var n = (startY + endY) / 2; // 纵轴中点
                var factor = 1.5;
    
                var centerX = (startY - endY) * factor + m;
                var centerY = (endX - startX) * factor + n;
    
                var radius = Math.sqrt(Math.pow(L / 2, 2) + Math.pow(L * factor, 2));
                var startAngle = Math.atan2(startY - centerY, startX - centerX);
                var endAngle = Math.atan2(endY - centerY, endX - centerX);
    
                // this.L = L;
                this.startX = startX;
                this.startY = startY;
                this.endX = endX;
                this.endY = endY;
                this.centerX = centerX;
                this.centerY = centerY;
                this.startAngle = startAngle;
                this.endAngle = endAngle;
                this.startLabel = options && options.labels && options.labels[0],
                    this.endLabel = options && options.labels && options.labels[1],
                    this.radius = radius;
                this.lineWidth = options.width || 1;
                this.strokeStyle = options.color || '#000';
                this.label = options.label;
                this.font = options.font;
                this.shadowBlur = options.shadowBlur;
            };
    
            A.prototype.draw = function (context) {
                context.save();
                context.lineWidth = this.lineWidth;
                context.strokeStyle = this.strokeStyle;
                context.shadowColor = this.strokeStyle;
                context.shadowBlur = this.shadowBlur || 2;
    
                context.beginPath();
                context.arc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle, false);
                context.stroke();
                context.restore();
    
                context.save();
                context.fillStyle = this.strokeStyle;
                if (this.label) {
                    context.font = this.font;
                    if (this.startLabel) {
                        var x = this.startX - 15
                        var y = this.startY + 5
                        context.fillText(this.startLabel, x, y);
                    }
                    if (this.endLabel) {
                        var x = this.endX - 15;
                        var y = this.endY - 5;
                        context.fillText(this.endLabel, x, y);
                    }
                }
                context.restore();
            };
    
            return A;
        })();
    
        var Pulse = (function () {
            function P(options) {
                this.x = options.x;
                this.y = options.y;
                this.maxRadius = options.radius;
                this.color = options.color;
                this.shadowBlur = 5;
                this.lineWidth = options.borderWidth;
                this.r = 0;
                this.factor = 2 / options.radius;
            };
    
            P.prototype.draw = function (context) {
                // var vr = (this.maxRadius - this.r) * this.factor;
                var vr = 0.5;
                this.r += vr;
                // this.shadowBlur = Math.floor(this.r);
    
                context.save();
                context.translate(this.x, this.y);
                var strokeColor = this.color;
                strokeColor = utils.calculateColor(strokeColor, 1 - this.r / this.maxRadius);
                context.strokeStyle = strokeColor;
                context.shadowBlur = this.shadowBlur;
                context.shadowColor = strokeColor;
                context.lineWidth = this.lineWidth;
                context.beginPath();
                context.arc(0, 0, this.r, 0, Math.PI * 2, false);
                context.stroke();
                context.restore();
    
                if (Math.abs(this.maxRadius - this.r) < 0.8) {
                    this.r = 0;
                }
            }
    
            return P;
        })();
    
        var Spark = (function () {
            var S = function (options) {
                var startX = options.startX,
                    startY = options.startY,
                    endX = options.endX,
                    endY = options.endY;
    
                //两点之间的圆有多个,通过两点及半径便可以定出两个圆,根据需要选取其中一个圆
                var L = Math.sqrt(Math.pow(startX - endX, 2) + Math.pow(startY - endY, 2));
                var m = (startX + endX) / 2; // 横轴中点
                var n = (startY + endY) / 2; // 纵轴中点
                var factor = 1.5;
    
                var centerX = (startY - endY) * factor + m;
                var centerY = (endX - startX) * factor + n;
    
                var radius = Math.sqrt(Math.pow(L / 2, 2) + Math.pow(L * factor, 2));
                var startAngle = Math.atan2(startY - centerY, startX - centerX);
                var endAngle = Math.atan2(endY - centerY, endX - centerX);
    
                // 保证Spark的弧度不超过Math.PI
                if (startAngle * endAngle < 0) {
                    if (startAngle < 0) {
                        startAngle += Math.PI * 2;
                        endAngle += Math.PI * 2;
                    } else {
                        endAngle += Math.PI * 2;
                    }
                }
    
                this.tailPointsCount = 50; // 拖尾点数
                this.centerX = centerX;
                this.centerY = centerY;
                this.startAngle = startAngle;
                this.endAngle = endAngle;
                this.radius = radius;
                this.lineWidth = options.width || 1;
                this.strokeStyle = options.color || '#fff';
                this.factor = 2 / this.radius;
                this.deltaAngle = (80 / Math.min(this.radius, 400)) / this.tailPointsCount;
                this.trailAngle = this.startAngle;
                this.arcAngle = this.startAngle;
    
                this.animateBlur = true;
    
                this.marker = new Marker({
                    x: 50,
                    y: 80,
                    rotation: 50 * Math.PI / 180,
                    style: 'arrow',
                    color: 'rgb(255, 255, 255)',
                    size: 3,
                    borderWidth: 0,
                    borderColor: this.strokeStyle
                });
            };
    
            S.prototype.drawArc = function (context, strokeColor, lineWidth, startAngle, endAngle) {
                context.save();
                context.lineWidth = lineWidth;
                // context.lineWidth = 5;
                context.strokeStyle = strokeColor;
                context.shadowColor = this.strokeStyle;
                // context.shadowBlur = 5;
                context.lineCap = "round";
                context.beginPath();
                context.arc(this.centerX, this.centerY, this.radius, startAngle, endAngle, false);
                context.stroke();
                context.restore();
            };
    
            S.prototype.draw = function (context) {
                var endAngle = this.endAngle;
                // 匀速
                var angle = this.trailAngle + this.factor;
                var strokeColor = this.strokeStyle;
                if (this.animateBlur) {
                    this.arcAngle = angle;
                }
                this.trailAngle = angle;
                strokeColor = utils.calculateColor(strokeColor, 0.1);
    
                this.drawArc(context, strokeColor, this.lineWidth, this.startAngle, this.arcAngle);
    
                // 拖尾效果
                var count = this.tailPointsCount;
                for (var i = 0; i < count; i++) {
                    var arcColor = utils.calculateColor(this.strokeStyle, 0.3 - 0.3 / count * i);
                    var tailLineWidth = 5;
                    if (this.trailAngle - this.deltaAngle * i > this.startAngle) {
                        this.drawArc(context, arcColor,
                            tailLineWidth - tailLineWidth / count * i,
                            this.trailAngle - this.deltaAngle * i,
                            this.trailAngle
                        );
                    }
                }
    
                context.save();
                context.translate(this.centerX, this.centerY);
                this.marker.x = Math.cos(this.trailAngle) * this.radius;
                this.marker.y = Math.sin(this.trailAngle) * this.radius;
                this.marker.rotation = this.trailAngle + Math.PI / 2;
                this.marker.draw(context);
                context.restore();
    
                if ((endAngle - this.trailAngle) * 180 / Math.PI < 0.5) {
                    this.trailAngle = this.startAngle;
                    this.animateBlur = false;
                }
            };
    
            return S;
        })();
    
    
        var Migration = (function () {
            var M = function (options) {
                this.data = options.data;
                this.store = {
                    arcs: [],
                    markers: [],
                    pulses: [],
                    sparks: []
                };
                this.playAnimation = true;
                this.started = false;
                this.context = options.context;
                this.style = options.style;
                this.init();
            };
    
            M.prototype.init = function () {
                this.updateData(this.data);
            };
            /*
             * Shape 必须拥有draw方法
            */
            M.prototype.add = function (Shape) {
    
            };
            M.prototype.remove = function () {
    
            };
            M.prototype.clear = function () {
                this.store = {
                    arcs: [],
                    markers: [],
                    pulses: [],
                    sparks: []
                };
                // 更新状态
                this.playAnimation = true;
                this.started = false;
                // 清除绘画实例,如果没有这个方法,多次调用start,相当于存在多个动画队列同时进行
                window.cancelAnimationFrame(this.requestAnimationId);
            };
            /*
             * 更新数据
            */
            M.prototype.updateData = function (data) {
                if (!data || data.length === 0) {
                    return;
                }
                this.clear();
                this.data = data;
                if (this.data && this.data.length > 0) {
                    arrayUtils.forEach(this.data, function (element) {
                        var arc = new Arc({
                            startX: element.from[0],
                            startY: element.from[1],
                            endX: element.to[0],
                            endY: element.to[1],
                            labels: element.labels,
                            label: this.style.arc.label,
                            font: this.style.arc.font,
                             this.style.arc.width,
                            color: element.color
                        });
                        var marker = new Marker({
                            x: element.to[0],
                            y: element.to[1],
                            rotation: arc.endAngle + Math.PI / 2,
                            style: 'arrow',
                            color: element.color,
                            size: 4,
                            borderWidth: 0,
                            borderColor: element.color
                        });
                        var pulse = new Pulse({
                            x: element.to[0],
                            y: element.to[1],
                            radius: this.style.pulse.radius,
                            color: element.color,
                            borderWidth: this.style.pulse.borderWidth
                        });
                        var spark = new Spark({
                            startX: element.from[0],
                            startY: element.from[1],
                            endX: element.to[0],
                            endY: element.to[1],
                             15,
                            color: element.color
                        });
    
                        this.store.arcs.push(arc);
                        this.store.markers.push(marker);
                        this.store.pulses.push(pulse);
                        this.store.sparks.push(spark);
                    }, this);
                }
            };
            /*
            */
            M.prototype.start = function (canvas) {
                var that = this;
                if (!this.started) {
                    (function drawFrame() {
                        that.requestAnimationId = window.requestAnimationFrame(drawFrame, canvas);
    
                        if (that.playAnimation) {
                            canvas.width += 1;
                            canvas.width -= 1;
                            for (var p in that.store) {
                                var shapes = that.store[p];
                                for (var i = 0, len = shapes.length; i < len; i++) {
                                    shapes[i].draw(that.context);
                                }
                            }
                        }
                    })();
                    this.started = true;
                }
            };
            M.prototype.play = function () {
                this.playAnimation = true;
            };
            M.prototype.pause = function () {
                this.playAnimation = false;
            };
            return M;
        })();
    
        L.MigrationLayer = L.Class.extend({
            options: {
                map: {},
                data: {},
                pulseRadius: 25,
                pulseBorderWidth: 3,
                arcWidth: 1,
                arcLabel: true,
                arcLabelFont: '15px sans-serif',
                Marker: {},
                Spark: {}
    
            },
            _setOptions: function (obj, options) {
                if (!obj.hasOwnProperty('options')) {
                    obj.options = obj.options ? L.Util.create(obj.options) : {};
                }
                for (var i in options) {
                    obj.options[i] = options[i];
                }
                return obj.options;
            },
            initialize: function (options) {
                this._setOptions(this, options);
                this._map = this.options.map || {};
                this._data = this.options.data || {};
                this._style = {
                    pulse: {
                        radius: this.options.pulseRadius,
                        borderWidth: this.options.pulseBorderWidth
                    },
                    arc: {
                         this.options.arcWidth,
                        label: this.options.arcLabel,
                        font: this.options.arcLabelFont
                    }
                } || {};
                this._show = true;
                this._init();
            },
            _init: function () {
                var container = L.DomUtil.create('div', 'leaflet-ODLayer-container');
                container.style.position = 'absolute';
                container.style.width = this._map.getSize().x + "px";
                container.style.height = this._map.getSize().y + "px";
    
                this.container = container;
    
                this.canvas = document.createElement('canvas');
                this.context = this.canvas.getContext('2d');
                container.appendChild(this.canvas);
                this._map.getPanes().overlayPane.appendChild(container);
                //this._resize();
                if (!this.migration) {
                    var data = this._convertData();
                    this.migration = new Migration({
                        data: data,
                        context: this.context,
                        style: this._style
                    });
                    //this._draw();
                    //this._bindMapEvents();
                }
            },
            _resize: function () {
                var bounds = this._map.getBounds();
                var topleft = bounds.getNorthWest();
                var topLeftscreen = this._map.latLngToContainerPoint(topleft);
                //当地图缩放或者平移到整个地图的范围小于外层容器的范围的时候,要对this.container进行上下平移操作,反之则回归原位
                if (topLeftscreen.y > 0) {
                    this.container.style.top = -topLeftscreen.y + 'px';
                } else {
                    this.container.style.top = '0px';
                }
    
                var containerStyle = window.getComputedStyle(this._map.getContainer());
                this.canvas.setAttribute('width', parseInt(containerStyle.width, 10));
                this.canvas.setAttribute('height', parseInt(containerStyle.height, 10));
            },
            _convertData: function () {
                var bounds = this._map.getBounds();
    
                if (this._data && bounds) {
                    var data = arrayUtils.map(this._data, function (d) {
    
                        var fromPixel = this._map.latLngToContainerPoint(new L.LatLng(d.from[1], d.from[0]));
                        var toPixel = this._map.latLngToContainerPoint(new L.LatLng(d.to[1], d.to[0]));
                        return {
                            from: [fromPixel.x, fromPixel.y],
                            to: [toPixel.x, toPixel.y],
                            labels: d.labels,
                            value: d.value,
                            color: d.color
                        }
                    }, this);
    
                    return data;
                }
            },
            _bindMapEvents: function () {
                var that = this;
                // window.onresize = function () {
                //     that._resize();
                // };
                // this._map.on('movestart ', function () {
                //     that.migration.pause();
                // });
                this._map.on('moveend', function () {
                    that.migration.play();
                    that._draw();
                });
                this._map.on('zoomstart ', function () { that.container.style.display = 'none' });
                this._map.on('zoomend', function () {
                    if (that._show) {
                        that.container.style.display = ''
                        that._draw();
                    }
                });
            },
            _draw: function () {
                var bounds = this._map.getBounds();
                if (bounds && this.migration.playAnimation) {
                    this._resize();
                    this._transform();
                    var data = this._convertData();
                    this.migration.updateData(data);
                    this.migration.start(this.canvas);
                }
            },
            _transform: function () {
                var bounds = this._map.getBounds();
                var topLeft = this._map.latLngToLayerPoint(bounds.getNorthWest());
                L.DomUtil.setPosition(this.container, topLeft);
            },
            addTo: function () {
                this._bindMapEvents();
                var bounds = this._map.getBounds();
                if (bounds && this.migration.playAnimation) {
                    this._resize();
                    this._transform();
    
                    var data = this._convertData();
                    this.migration.updateData(data);
                    this.migration.start(this.canvas);
                }
            },
            setData: function (data) {
                this._data = data;
                this._draw();
            },
            hide: function () {
                this.container.style.display = 'none';
                this._show = false;
            },
            show: function () {
                this.container.style.display = '';
                this._show = true;
            },
            play: function () {
                this.migration.play();
            },
            pause: function () {
                this.migration.pause();
            },
            destroy: function () {
                this.migration.clear();
                //移除dom
                this.container.parentNode.removeChild(this.container);
                //移除事件监听
                this._map.clearAllEventListeners();
                this.mapHandles = [];
            }
    
        });
        L.migrationLayer = function (options) {
            return new L.MigrationLayer(options)
        }
    })(window)
    
  • 相关阅读:
    python 2
    Python 1 practice
    python 1
    Python多线程_笔记
    背景自适应不会随浏览器界面放大速效而改变
    平行四边形定理
    动态规划3(区间+树形)
    素数快速生成
    设CPU共有16根地址线,8根数据线,,,
    贪心+huffman编码+模拟退火+分治
  • 原文地址:https://www.cnblogs.com/xutongbao/p/11915736.html
Copyright © 2011-2022 走看看