这几年一直在做网页端地图相关的功能开发、设计,最近有个新的展示功能,需要在地图上根据经纬度显示一个会动的雷达扫描图,简单研究了一下,具体代码如下:
(所有我认为需要解释的都在代码中注释了,大家可以直接查看代码,有问题可以在文末留言~)
Openlayers 4
<!-- 代码片作者:Carl Zhang,转载请注明。 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OpenLayers 4 Radar Animation</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <style>
        .map {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<div id="map" class="map"></div>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
<script>
    // 雷达中心点的经纬度坐标
    var radarCenter = [116.42022722759157, 39.819625317442345];
    var radarCenterProj = ol.proj.fromLonLat(radarCenter); // 将经纬度坐标转换为投影坐标
    // 初始化地图
    var map = new ol.Map({
        target: 'map',
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM()
            })
        ],
        view: new ol.View({
            center: ol.proj.fromLonLat(radarCenter), // 中心点经纬度
            zoom: 15
        })
    });
    // 创建一个Canvas图层
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    // 获取150米半径对应的像素值
    function getRadiusInPixels() {
        var view = map.getView();
        var resolution = view.getResolution();
        var center = ol.proj.toLonLat(view.getCenter(), 'EPSG:3857');
        var metersPerUnit = ol.proj.METERS_PER_UNIT[map.getView().getProjection().getUnits()];
        // 150表示150米半径
        return 150 / (resolution * metersPerUnit);
    }
    // 绘制雷达动画的函数
    function drawRadar(context, centerX, centerY, radius, angle) {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.beginPath();
        context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
        context.fillStyle = 'rgba(0, 0, 255, 0.1)';
        context.fill();
        context.beginPath();
        context.moveTo(centerX, centerY);
        context.arc(centerX, centerY, radius, angle, angle + Math.PI / 6, false);
        context.closePath();
        context.fillStyle = 'rgba(0, 0, 255, 0.5)';
        context.fill();
    }
    // 更新雷达动画的函数
    var angle = 0;
    function updateRadar() {
        angle += Math.PI / 60;
        radarLayer.getSource().changed();
    }
    // 创建一个图层来显示雷达动画
    var radarLayer = new ol.layer.Image({
        source: new ol.source.ImageCanvas({
            canvasFunction: function(extent, resolution, pixelRatio, size, projection) {
                canvas.width = size[0];
                canvas.height = size[1];
                context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
                var center = map.getPixelFromCoordinate(radarCenterProj);
                var radius = getRadiusInPixels();
                drawRadar(context, center[0], center[1], radius, angle);
                return canvas;
            },
            projection: 'EPSG:3857',
            ratio: 1
        })
    });
    map.addLayer(radarLayer);
    // 每秒钟更新雷达动画
    setInterval(updateRadar, 1000 / 30);
    // 确保在地图视图变化时也重新绘制雷达
    map.getView().on('change:center', updateRadar);
    map.getView().on('change:resolution', updateRadar);
    map.on('moveend', updateRadar);
    map.on('zoomend', updateRadar);
</script>
</body>
</html>
    Openlayers 8
<!-- 代码片作者:Carl Zhang,转载请注明。 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OpenLayers 8 Radar Animation</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@latest/ol.css" type="text/css">
    <style>
        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            width: 100%;
        }
        .map {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<div id="map" class="map"></div>
<script type="module">
    import Map from 'https://cdn.jsdelivr.net/npm/[email protected]/Map.js';
    import View from 'https://cdn.jsdelivr.net/npm/[email protected]/View.js';
    import TileLayer from 'https://cdn.jsdelivr.net/npm/[email protected]/layer/Tile.js';
    import ImageLayer from 'https://cdn.jsdelivr.net/npm/[email protected]/layer/Image.js';
    import OSM from 'https://cdn.jsdelivr.net/npm/[email protected]/source/OSM.js';
    import ImageCanvasSource from 'https://cdn.jsdelivr.net/npm/[email protected]/source/ImageCanvas.js';
    import {fromLonLat} from 'https://cdn.jsdelivr.net/npm/[email protected]/proj.js';
    // 雷达中心点的经纬度坐标数组、颜色、底色和速度
    const radarCenters = [
        {coord: [116.42622722759157, 39.816625317442345], color: 'rgba(255, 0, 0, 0.5)', backgroundColor: 'rgba(255, 0, 0, 0.1)', speed: Math.PI / 90},
        {coord: [116.42322722759157, 39.819625317442345], color: 'rgba(0, 255, 0, 0.5)', backgroundColor: 'rgba(0, 255, 0, 0.1)', speed: Math.PI / 90},
        {coord: [116.42022722759157, 39.822625317442345], color: 'rgba(0, 0, 255, 0.5)', backgroundColor: 'rgba(0, 0, 255, 0.1)', speed: Math.PI / 90}
    ];
    // 将经纬度坐标转换为投影坐标
    const radarCentersProj = radarCenters.map(radar => ({
        coord: fromLonLat(radar.coord),
        color: radar.color,
        backgroundColor: radar.backgroundColor,
        speed: radar.speed
    }));
    // 初始化地图
    const map = new Map({
        target: 'map',
        layers: [
            new TileLayer({
                source: new OSM()
            })
        ],
        view: new View({
            center: fromLonLat(radarCenters[0].coord), // 中心点经纬度
            zoom: 15
        })
    });
    // 创建一个Canvas图层
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    // 获取150米半径对应的像素值
    function getRadiusInPixels() {
        const view = map.getView();
        const resolution = view.getResolution();
        const metersPerUnit = view.getProjection().getMetersPerUnit();
        return 150 / (resolution * metersPerUnit); // 150米半径
    }
    // 绘制雷达动画的函数
    function drawRadar(context, centerX, centerY, radius, angle, color, backgroundColor) {
        context.beginPath();
        context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
        context.fillStyle = backgroundColor; // 底色
        context.fill();
        context.beginPath();
        context.moveTo(centerX, centerY);
        context.arc(centerX, centerY, radius, angle, angle + Math.PI / 6, false);
        context.closePath();
        context.fillStyle = color; // 扫描颜色
        context.fill();
    }
    // 更新雷达动画的函数
    const angles = radarCenters.map(() => 0);
    function updateRadar() {
        radarCentersProj.forEach((radar, index) => {
            angles[index] += radar.speed;
        });
        radarLayer.getSource().changed();
    }
    // 创建一个图层来显示雷达动画
    const radarLayer = new ImageLayer({
        source: new ImageCanvasSource({
            canvasFunction: function(extent, resolution, pixelRatio, size, projection) {
                canvas.width = size[0];
                canvas.height = size[1];
                context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
                context.clearRect(0, 0, canvas.width, canvas.height);
                const radius = getRadiusInPixels();
                radarCentersProj.forEach((radar, index) => {
                    const center = map.getPixelFromCoordinate(radar.coord);
                    drawRadar(context, center[0], center[1], radius, angles[index], radar.color, radar.backgroundColor);
                });
                return canvas;
            },
            projection: 'EPSG:3857',
            ratio: 1
        })
    });
    map.addLayer(radarLayer);
    // 使用requestAnimationFrame来实现平滑动画
    function animate() {
        updateRadar();
        requestAnimationFrame(animate);
    }
    animate();
    // 确保在地图视图变化时也重新绘制雷达
    map.getView().on('change:center', () => radarLayer.getSource().changed());
    map.getView().on('change:resolution', () => radarLayer.getSource().changed());
    map.on('moveend', () => radarLayer.getSource().changed());
    map.on('zoomend', () => radarLayer.getSource().changed());
</script>
</body>
</html>
    具体效果:
        
    
(本文为作者原创。转载请注明:转自carlzhang.xyz)