amap繪製1000+飛機不卡頓

高德地圖上繪製飛機標記,相信不少人都使用marker來實現的,但若是這個數據較多的話, 地圖就會有些卡,嚴重影響用戶使用,那麼怎樣才能在大數據的狀況下不卡呢?javascript

  1. 若是需求較少的話,能夠使用MassMarks麻點層來解決這個問題,但其可擴展性較弱
  2. 選擇CustomLayer來實現

這裏咱們主要講的是CustomLayer來實現,相對之前的多個marker實現,那麼使用CustomLayer就好多了。。css

下面先看下效果吧
圖片描述html

使用customLayer主要的工做仍是在於繪製canvashtml5

行看一個簡單的示例(繪製飛機和添加事件)java

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="jquery.min.js"></script>
</head>
<body>
    
    <canvas id="canvas"></canvas>
    
    <script>
    var canvas = document.getElementById('canvas');
    canvas.width = 500;
    canvas.height = 500;
    var ctx = canvas.getContext('2d');
    // 背景
    ctx.fillStyle = '#999';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 飛機
    var coord = [200, 200],
        planeSize = 26,
        planeHalf = planeSize/2;

    var x = coord[0],
        y = coord[1],
        angle = 2*Math.PI*(-60/360);

    // 藍色線
    ctx.save();
    ctx.moveTo(x, y);
    ctx.lineTo(x-200, y);
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'blue';
    ctx.stroke();
    ctx.restore();

    // 黑色飛機
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#000';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 綠色飛機
    ctx.save();
    ctx.translate(0, -planeHalf);
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#690';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 白色飛機
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);
    ctx.translate(-x, -y);
    ctx.beginPath();
    ctx.translate(0, -planeHalf);
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#fff';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 紅色飛機
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);
    ctx.translate(-x, -y);
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#f00';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 點擊點在飛機矩形範圍內[ Math.round(Math.sqrt(planeHalf*planeHalf*2))==18 ] ( 推薦 )
    var modulesPoints = {};
    modulesPoints['plane'] = {
        xMin: x-18,
        xMax: x+18,
        yMin: y-18,
        yMax: y+18
    };
    canvas.addEventListener('click', function(e) {
        var clientX = e.clientX,
            clientY = e.clientY,
            planeRect = modulesPoints['plane'];
        if(planeRect.xMin<clientX && clientX<planeRect.xMax && planeRect.yMin<clientY && clientY<planeRect.yMax) {
            console.log(+new Date);
        }
    });

    // 點擊點只在飛機內
    // var modulesPoints = {};
    // for(var i=0; i<canvas.width; i++) {
    //     for(var j=0; j<canvas.height; j++) {
    //         if(ctx.isPointInPath(i, j)) {
    //             if(!modulesPoints['plane']) {
    //                 modulesPoints['plane'] = {};
    //             }
    //             modulesPoints['plane']['x_'+i] = i;
    //             modulesPoints['plane']['y_'+j] = j;
    //         }
    //     }
    // }
    // canvas.addEventListener('click', function(e) {
    //     if(modulesPoints['plane']['x_'+e.clientX] && modulesPoints['plane']['y_'+e.clientY]) {
    //         console.log(+new Date);
    //     }
    // });
    </script>
</body>
</html>

效果:
圖片描述jquery

基本原理就是上面那樣的,用過canvas的同窗應該都能看懂,固然也能夠先了解下canvas基礎canvas APIweb

接下來就請嚴讀完整代碼嘍~~~
註釋也都很清淅。。ajax

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    *{margin: 0;}
    </style>
    <script src="js/jquery.min.js"></script>
</head>
<body>
    <div id="zh_map"></div>
    
    <script type="text/javascript" src="http://webapi.amap.com/maps?v=1.3&key=b705b0ffe322148bbf5c1febdf47fe95&plugin=AMap.Geocoder"></script>
    <script type="text/javascript" src="js/socketio.js"></script>
    <script type="text/javascript" src="js/wgs2mars.min.js"></script>
    <script type="text/javascript" src="js/protobuf/protobuf.js"></script>

    <script>
    // 初始化
    var initSet = (function set() {
        document.getElementById('zh_map').style.cssText = 'height: '+window.innerHeight+'px;';
        return set;
    })();
    window.onresize = function() {
        initSet();
    }
    // 變量
    var planeMarkers = {},
        planeLayer = null,
        planeSize = 26,
        planePoints = {},
        selectedPlaneAnum = null;
    // 打印log
    function log() {
        var args = Array.prototype.slice.call(arguments);
        if(args.length == 1) {
            console.log(args[0]);
        } else {
            console.log('%c'+args[0], 'color: '+(args[1]?args[1]:'#333'));
        }
    }
    // 地圖
    var map = new AMap.Map('zh_map', {
        zoom: 10,
        center: [102.926672, 25.09155],
        doubleClickZoom: false,
        mapStyle: 'blue_night',
        features: ['bg', 'point']
    });

    // 數據結構
    var locProto,
        pathProto;
        protobuf.load('js/protobuf/flightloc.json', function(err, root) {
            locProto = root.lookupType("Loc.FlightLoc");
            pathProto = root.lookupType("Loc.FlightPath");
        });

    // 請求appid、token
    function reqToken() {
        $.ajax({
            type: 'get',
            url: 'http://***/***/get_client_token',
            success: function(res) {
                res = JSON.parse(res);
                if(res.status) {
                    planeFn(res.appid, res.token);
                }
            }
        });
    }
    reqToken();

    function planeFn(appid, token) {
        socket = null;
        // 可視範圍
        var curBounds = map.getBounds();
        var clientBounds = [curBounds.northeast.lat,curBounds.southwest.lng,curBounds.southwest.lat,curBounds.northeast.lng]; // 左上右下

        // 鏈接socket
        socket = io('https://***.***.com/***', {path: "/**/socket.io", transports: ['websocket'], reconnection: true});
        // 鏈接
        socket.on('connect', function() {
            log('connect success.', 'green');
        });
        // 驗證
        socket.on('validate', function(ack) {
            log('validate...', 'blue');
            ack(appid, token, '{}');
            socket.emit('sub', clientBounds, -1, '');
        });
        // 驗證成功
        socket.on('validateSuccess', function() {
            log('socket validate success.', 'green');
        });
        // 驗證失敗
        socket.on('validateFailed', function() {
            log('socket validate failed.', 'red');
            reqToken();
        });
        socket.on('disconnect', function() {
            log('socket disconnect.', 'red');
            reqToken();
        });
        // 心跳
        socket.on('heartbeat', function(ack) {
            ack();
        });
        // 監聽adsb數據
        socket.on('~', function(buffer) {
            if(locProto) {
                var item = locProto.decode(new Uint8Array(buffer)), // 數據解碼
                    Anum = item.Anum,         // 飛機編號
                    Lon = item.Lon,         // 經度
                    Lat = item.Lat,         // 緯度
                    Alt = item.Alt,         // 高度
                    Ang = item.Ang,         // 角度
                    Spd = item.Spd;         // 速度
                var coord = transformFromWGSToGCJ(Lon, Lat); // WGS(World Geodetic System) 世界大地座標系 轉 高德地圖GCJ(國測局座標)使用的火星座標系
                    Lon = coord.lng;
                    Lat = coord.lat;
                planeMarkers[Anum] = item;
            } else {
                log('監控飛機位置失敗~');
            }
        });
    }

    // 從新訂閱
    function resub(socket) {
        // 可視範圍
        var curBounds = map.getBounds();
        var clientBounds = [curBounds.northeast.lat,curBounds.southwest.lng,curBounds.southwest.lat,curBounds.northeast.lng]; // 左上右下
        // 訂閱
        socket.emit("sub", clientBounds, -1, '');
    }

    // 拖動結束
    map.on('dragend', function() {
        if(socket) resub(socket);
    });

    // 縮放結束
    map.on('zoomend', function() {
        if(socket) resub(socket);
    });

    // 繪製
    function drawPlane(anum) {
        if(planeLayer) {
            map.remove(planeLayer);
            planeLayer = null
            planePoints = {};
        }
        map.plugin(['AMap.CustomLayer'], function() {
            var canvas = document.createElement('canvas');
                canvas.id = 'plane_layer';
                canvasWth = map.getSize().width;
                canvasHgt = map.getSize().height;
                canvas.width = canvasWth;
                canvas.height = canvasHgt;
            planeLayer = new AMap.CustomLayer(canvas, {
                zIndex: 110
            });
            planeLayer.render = function() {
                var ctx = canvas.getContext('2d');
                    ctx.clearRect(0, 0, canvasWth, canvasHgt);
                $.each(planeMarkers, function(k, v) {
                    var Anum = v.Anum,         // 飛機編號
                        Fnum = v.Fnum,         // 航班號
                        Org = v.Org,         // 出發地
                        Dst = v.Dst,         // 目的地
                        Squawk = v.Squawk,   // 應答碼
                        Lon = v.Lon,          // 經度
                        Lat = v.Lat,          // 緯度
                        Spd = v.Spd,         // 速度
                        Ang = v.Ang,         // 角度
                        Alt = v.Alt;          // 高度
                    var coord = transformFromWGSToGCJ(Lon, Lat);
                        Lon = coord.lng;
                        Lat = coord.lat;
                    var containerCoord = map.lngLatToContainer([coord.lng, coord.lat]),
                        x = containerCoord.x-planeSize/2,
                        y = containerCoord.y,
                        angle = 2*Math.PI*(Ang/360);

                    // 繪製飛機
                    ctx.save();
                    ctx.translate(x, y);
                    ctx.rotate(angle);
                    ctx.translate(-x, -y);
                    ctx.beginPath();
                    ctx.translate(0, -planeHalf);
                    ctx.moveTo(x, y);
                    ctx.lineTo(x+2, y+3);
                    ctx.lineTo(x+2, y+12);
                    ctx.lineTo(x+13, y+20);
                    ctx.lineTo(x+2, y+16);
                    ctx.lineTo(x+2, y+23);
                    ctx.lineTo(x+5, y+26);
                    ctx.lineTo(x, y+25);
                    ctx.lineTo(x-5, y+26);
                    ctx.lineTo(x-2, y+23);
                    ctx.lineTo(x-2, y+16);
                    ctx.lineTo(x-13, y+20);
                    ctx.lineTo(x-2, y+12);
                    ctx.lineTo(x-2, y+3);
                    ctx.fillStyle = (selectedPlaneAnum===Anum?'#FF5F3A':'#1282F7');
                    ctx.fill();
                    ctx.closePath();
                    ctx.restore();

                    // 保存座標點
                    planePoints[Anum] = {
                        xMin: x-18,
                        xMax: x+18,
                        yMin: y-18,
                        yMax: y+18
                    };

                    // 繪製矩形
                    x += planeHalf;
                    y -= planeHalf;
                    ctx.fillStyle = 'rgba(0,0,0,0.5)';
                    ctx.fillRect(x, y, 100, 150);
                    ctx.restore();

                    // 繪製文字
                    x += 10;
                    ctx.fillStyle = '#fff';
                    ctx.fillText(Anum, x, y+20);
                    ctx.fillText(Fnum, x, y+40);
                    ctx.fillText(Org+'-'+Dst, x, y+60);
                    ctx.fillText(Squawk||'--', x, y+80);
                    ctx.fillText(Ang, x, y+100);
                    ctx.fillText(Spd.toFixed(2), x, y+120);
                    ctx.fillText(Alt.toFixed(2), x, y+140);
                });
            }
            planeLayer.setMap(map);
        });
    }

    // 1秒鐘繪製一次
    setInterval(function() {
        drawPlane(selectedPlaneAnum);
    }, 1000);

    // 事件
    $('#zh_map').on('click', 'canvas',function(e) {
        var clientX = e.clientX,
            clientY = e.clientY;
        $.each(planePoints, function(k, v) {
            if(v.xMin<clientX && clientX<v.xMax && v.yMin<clientY && clientY<v.yMax) {
                selectedPlaneAnum = k;
            }
        });
        drawPlane(selectedPlaneAnum);
    });
    </script>
</body>
</html>

其實整個過程也不難,就是考驗你們想不想作了。。json

相關文章
相關標籤/搜索