【前端】【高德地图WebJs】【知识体系搭建】线要素知识点——>折线, 贝塞尔曲线,轨迹回放

发布于:2025-09-15 ⋅ 阅读:(23) ⋅ 点赞:(0)

高德地图WebJS - 线要素知识点

1. AMap.Polyline 折线

1.1 基础用法

折线是由多个坐标点连接形成的线条,常用于绘制路径、边界等。

// 创建折线
var polyline = new AMap.Polyline({
    path: [
        [116.368904, 39.913423],
        [116.382122, 39.901176], 
        [116.387271, 39.912501],
        [116.398258, 39.904600]
    ],
    strokeColor: "#3366FF",     // 线颜色
    strokeOpacity: 1,           // 线透明度
    strokeWeight: 5,            // 线宽
    strokeStyle: "solid",       // 线样式
    strokeDasharray: [10, 5]    // 补充线样式
});

// 将折线添加到地图
map.add(polyline);

1.2 折线属性配置

属性 类型 说明
path Array 折线的节点坐标数组
strokeColor String 线条颜色,支持十六进制、RGB、RGBA、HSL等格式
strokeOpacity Number 线条透明度,取值范围[0,1]
strokeWeight Number 线条宽度,单位:像素
strokeStyle String 线条样式:solid、dashed
strokeDasharray Array 虚线配置,如[10,5]表示10像素实线,5像素空白
zIndex Number 折线的叠加顺序
geodesic Boolean 是否绘制大地线

1.3 动态折线

// 创建可编辑的折线
var polyline = new AMap.Polyline({
    path: [
        [116.368904, 39.913423],
        [116.382122, 39.901176],
        [116.387271, 39.912501]
    ],
    strokeColor: "#FF0000",
    strokeWeight: 6,
    draggable: true // 允许拖拽
});

map.add(polyline);

// 动态添加点
function addPoint(lnglat) {
    var path = polyline.getPath();
    path.push(lnglat);
    polyline.setPath(path);
}

// 动态删除最后一个点
function removeLastPoint() {
    var path = polyline.getPath();
    if (path.length > 0) {
        path.pop();
        polyline.setPath(path);
    }
}

// 地图点击添加点
map.on('click', function(e) {
    addPoint([e.lnglat.lng, e.lnglat.lat]);
});

1.4 折线事件处理

// 点击事件
polyline.on('click', function(e) {
    console.log('折线被点击', e.lnglat);
});

// 鼠标悬停事件
polyline.on('mouseover', function(e) {
    polyline.setOptions({
        strokeColor: '#FF0000',
        strokeWeight: 8
    });
});

polyline.on('mouseout', function(e) {
    polyline.setOptions({
        strokeColor: '#3366FF',
        strokeWeight: 5
    });
});

// 拖拽事件
polyline.on('dragging', function(e) {
    console.log('折线正在拖拽');
});

polyline.on('dragend', function(e) {
    console.log('拖拽结束,新路径:', polyline.getPath());
});

2. AMap.BezierCurve 贝塞尔曲线

2.1 基础用法

贝塞尔曲线用于绘制平滑的曲线路径。

// 创建贝塞尔曲线
var bezierCurve = new AMap.BezierCurve({
    path: [
        [116.39, 39.91, 116.37, 39.91],  // 起点和控制点1
        [116.40, 39.90, 116.38, 39.90]   // 终点和控制点2
    ],
    strokeColor: "#28F",
    strokeOpacity: 1,
    strokeWeight: 6,
    strokeStyle: "solid"
});

map.add(bezierCurve);

2.2 复杂贝塞尔曲线

// 创建多段贝塞尔曲线
var complexBezier = new AMap.BezierCurve({
    path: [
        // 第一段:起点[116.39, 39.91],控制点1[116.37, 39.91],控制点2[116.38, 39.90],终点[116.40, 39.90]
        [116.39, 39.91, 116.37, 39.91, 116.38, 39.90, 116.40, 39.90],
        // 第二段:起点继承上一段终点,新的控制点和终点
        [116.41, 39.89, 116.42, 39.91, 116.44, 39.90]
    ],
    strokeColor: "#FF6600",
    strokeWeight: 4
});

map.add(complexBezier);

3. 轨迹回放和动画

3.1 基础轨迹回放

// 轨迹回放功能
function createTrackPlayback(trackPoints) {
    // 创建轨迹线
    var trackLine = new AMap.Polyline({
        path: [],
        strokeColor: '#28F',
        strokeWeight: 6,
        strokeOpacity: 0.8
    });
    
    // 创建移动标记点
    var movingMarker = new AMap.Marker({
        position: trackPoints[0],
        icon: new AMap.Icon({
            image: 'https://example.com/car.png',
            size: new AMap.Size(32, 32),
            imageSize: new AMap.Size(32, 32)
        }),
        angle: 0
    });
    
    map.add([trackLine, movingMarker]);
    
    var currentIndex = 0;
    var playbackPath = [];
    
    function playNext() {
        if (currentIndex < trackPoints.length) {
            var currentPoint = trackPoints[currentIndex];
            
            // 更新标记点位置
            movingMarker.setPosition(currentPoint);
            
            // 计算角度(如果有下一个点)
            if (currentIndex < trackPoints.length - 1) {
                var nextPoint = trackPoints[currentIndex + 1];
                var angle = calculateAngle(currentPoint, nextPoint);
                movingMarker.setAngle(angle);
            }
            
            // 更新轨迹线
            playbackPath.push(currentPoint);
            trackLine.setPath([...playbackPath]);
            
            currentIndex++;
            
            // 继续播放下一个点
            setTimeout(playNext, 500); // 500ms间隔
        }
    }
    
    // 计算两点间角度
    function calculateAngle(start, end) {
        var dx = end[0] - start[0];
        var dy = end[1] - start[1];
        var angle = Math.atan2(dy, dx) * 180 / Math.PI;
        return angle;
    }
    
    return {
        start: playNext,
        pause: function() {
            // 暂停逻辑
        },
        reset: function() {
            currentIndex = 0;
            playbackPath = [];
            trackLine.setPath([]);
            movingMarker.setPosition(trackPoints[0]);
        }
    };
}

// 使用示例
var trackPoints = [
    [116.397428, 39.90923],
    [116.398428, 39.90823],
    [116.399428, 39.90723],
    [116.400428, 39.90623]
];

var playback = createTrackPlayback(trackPoints);
playback.start();

3.2 平滑轨迹动画

// 平滑轨迹动画
function createSmoothTrackAnimation(trackPoints, duration = 5000) {
    var polyline = new AMap.Polyline({
        path: trackPoints,
        strokeColor: '#FF6600',
        strokeWeight: 6,
        strokeOpacity: 0.8
    });
    
    var marker = new AMap.Marker({
        position: trackPoints[0],
        icon: new AMap.Icon({
            image: 'https://example.com/moving-icon.png',
            size: new AMap.Size(24, 24)
        })
    });
    
    map.add([polyline, marker]);
    
    // 使用AMap的moveAlong方法实现平滑移动
    marker.moveAlong(trackPoints, {
        duration: duration, // 动画持续时间
        JSAPI: true, // 是否延道路自动设置角度在 JSAPI 中设置
        // 每一段的走完所需要的时间
        easing: 'linear', // 动画缓动效果
        circlable: false, // 是否循环
        delay: 1000, // 动画延迟开始时间
        aniType: 0 // 动画类型,0为沿路径移动,1为飞行移动
    });
    
    // 监听动画事件
    marker.on('moving', function(e) {
        console.log('移动中', e.passedPath);
    });
    
    marker.on('moveend', function() {
        console.log('动画结束');
    });
    
    return {
        pause: function() {
            marker.pauseMove();
        },
        resume: function() {
            marker.resumeMove();
        },
        stop: function() {
            marker.stopMove();
        }
    };
}

3.3 实时轨迹绘制

// 实时轨迹绘制
class RealTimeTrack {
    constructor(map, options = {}) {
        this.map = map;
        this.options = {
            strokeColor: '#FF0000',
            strokeWeight: 4,
            maxPoints: 100, // 最大保留点数
            ...options
        };
        
        this.polyline = new AMap.Polyline({
            path: [],
            strokeColor: this.options.strokeColor,
            strokeWeight: this.options.strokeWeight,
            strokeOpacity: 0.8
        });
        
        this.marker = new AMap.Marker({
            icon: new AMap.Icon({
                image: 'https://example.com/location.png',
                size: new AMap.Size(20, 20)
            })
        });
        
        this.map.add([this.polyline, this.marker]);
        this.path = [];
    }
    
    // 添加新的位置点
    addPoint(lnglat, timestamp) {
        this.path.push({
            position: lnglat,
            timestamp: timestamp || Date.now()
        });
        
        // 限制路径点数量
        if (this.path.length > this.options.maxPoints) {
            this.path.shift();
        }
        
        // 更新折线路径
        var positions = this.path.map(point => point.position);
        this.polyline.setPath(positions);
        
        // 更新标记点位置
        this.marker.setPosition(lnglat);
        
        // 自动调整地图视野
        if (this.path.length > 1) {
            var bounds = new AMap.Bounds();
            positions.forEach(pos => bounds.extend(pos));
            this.map.setBounds(bounds);
        }
    }
    
    // 清除轨迹
    clear() {
        this.path = [];
        this.polyline.setPath([]);
    }
    
    // 获取轨迹统计信息
    getStats() {
        if (this.path.length < 2) return null;
        
        var totalDistance = 0;
        var totalTime = this.path[this.path.length - 1].timestamp - this.path[0].timestamp;
        
        for (var i = 1; i < this.path.length; i++) {
            var distance = AMap.GeometryUtil.distance(
                this.path[i - 1].position,
                this.path[i].position
            );
            totalDistance += distance;
        }
        
        return {
            totalDistance: totalDistance, // 总距离(米)
            totalTime: totalTime, // 总时间(毫秒)
            averageSpeed: (totalDistance / (totalTime / 1000)) * 3.6, // 平均速度(km/h)
            pointCount: this.path.length
        };
    }
}

// 使用示例
var realTimeTrack = new RealTimeTrack(map, {
    strokeColor: '#00FF00',
    strokeWeight: 5,
    maxPoints: 50
});

// 模拟实时位置更新
setInterval(function() {
    var lng = 116.397428 + (Math.random() - 0.5) * 0.01;
    var lat = 39.90923 + (Math.random() - 0.5) * 0.01;
    realTimeTrack.addPoint([lng, lat]);
    
    var stats = realTimeTrack.getStats();
    if (stats) {
        console.log('轨迹统计:', stats);
    }
}, 2000);

4. 线条样式和交互

4.1 渐变线条

// 创建渐变色线条
function createGradientLine(path) {
    // 由于高德地图不直接支持渐变线条,我们可以通过分段绘制实现
    var segments = [];
    var colors = ['#FF0000', '#FF8800', '#FFFF00', '#88FF00', '#00FF00'];
    
    for (var i = 0; i < path.length - 1; i++) {
        var colorIndex = Math.floor((i / (path.length - 1)) * (colors.length - 1));
        var segment = new AMap.Polyline({
            path: [path[i], path[i + 1]],
            strokeColor: colors[colorIndex],
            strokeWeight: 6,
            strokeOpacity: 0.8
        });
        segments.push(segment);
    }
    
    map.add(segments);
    return segments;
}

// 使用示例
var gradientPath = [
    [116.368904, 39.913423],
    [116.382122, 39.901176],
    [116.387271, 39.912501],
    [116.398258, 39.904600],
    [116.405467, 39.907823]
];

createGradientLine(gradientPath);

4.2 动态线条效果

// 流动线条效果
function createFlowingLine(path) {
    var polyline = new AMap.Polyline({
        path: path,
        strokeColor: '#3366FF',
        strokeWeight: 6,
        strokeOpacity: 0.8,
        strokeDasharray: [20, 10] // 虚线样式
    });
    
    map.add(polyline);
    
    // 创建流动效果
    var offset = 0;
    setInterval(function() {
        offset += 2;
        if (offset > 30) offset = 0;
        
        // 通过改变虚线偏移创建流动效果
        polyline.setOptions({
            strokeDasharray: [20, 10],
            strokeDashoffset: offset
        });
    }, 100);
    
    return polyline;
}

// 脉冲线条效果
function createPulseLine(path) {
    var polyline = new AMap.Polyline({
        path: path,
        strokeColor: '#FF0000',
        strokeWeight: 4,
        strokeOpacity: 0.8
    });
    
    map.add(polyline);
    
    // 脉冲动画
    var growing = true;
    var weight = 4;
    
    setInterval(function() {
        if (growing) {
            weight += 0.5;
            if (weight >= 10) growing = false;
        } else {
            weight -= 0.5;
            if (weight <= 4) growing = true;
        }
        
        polyline.setOptions({
            strokeWeight: weight
        });
    }, 100);
    
    return polyline;
}

4.3 交互式线条编辑

// 交互式线条编辑器
class InteractiveLineEditor {
    constructor(map) {
        this.map = map;
        this.polyline = null;
        this.isDrawing = false;
        this.currentPath = [];
        
        this.initEvents();
    }
    
    initEvents() {
        // 地图点击事件
        this.map.on('click', (e) => {
            if (this.isDrawing) {
                this.addPoint([e.lnglat.lng, e.lnglat.lat]);
            }
        });
        
        // 双击结束绘制
        this.map.on('dblclick', () => {
            if (this.isDrawing) {
                this.finishDrawing();
            }
        });
    }
    
    // 开始绘制
    startDrawing() {
        this.isDrawing = true;
        this.currentPath = [];
        
        if (this.polyline) {
            this.map.remove(this.polyline);
        }
        
        this.map.getContainer().style.cursor = 'crosshair';
    }
    
    // 添加点
    addPoint(lnglat) {
        this.currentPath.push(lnglat);
        
        if (this.currentPath.length === 1) {
            // 创建折线
            this.polyline = new AMap.Polyline({
                path: this.currentPath,
                strokeColor: '#FF0000',
                strokeWeight: 4,
                strokeOpacity: 0.8
            });
            this.map.add(this.polyline);
        } else {
            // 更新路径
            this.polyline.setPath([...this.currentPath]);
        }
    }
    
    // 完成绘制
    finishDrawing() {
        this.isDrawing = false;
        this.map.getContainer().style.cursor = 'default';
        
        if (this.polyline && this.currentPath.length > 1) {
            // 启用编辑模式
            this.enableEdit();
        }
    }
    
    // 启用编辑模式
    enableEdit() {
        if (!this.polyline) return;
        
        var polyEditor = new AMap.PolyEditor(this.map, this.polyline);
        polyEditor.open();
        
        // 监听编辑事件
        polyEditor.on('addnode', (e) => {
            console.log('添加节点', e);
        });
        
        polyEditor.on('removenode', (e) => {
            console.log('删除节点', e);
        });
        
        polyEditor.on('adjust', (e) => {
            console.log('调整节点', e);
        });
        
        this.polyEditor = polyEditor;
    }
    
    // 禁用编辑模式
    disableEdit() {
        if (this.polyEditor) {
            this.polyEditor.close();
            this.polyEditor = null;
        }
    }
    
    // 清除
    clear() {
        if (this.polyline) {
            this.map.remove(this.polyline);
            this.polyline = null;
        }
        
        this.disableEdit();
        this.currentPath = [];
        this.isDrawing = false;
        this.map.getContainer().style.cursor = 'default';
    }
}

// 使用示例
var lineEditor = new InteractiveLineEditor(map);

// 添加控制按钮
document.getElementById('startDraw').onclick = function() {
    lineEditor.startDrawing();
};

document.getElementById('clear').onclick = function() {
    lineEditor.clear();
};

5. 实用工具函数

5.1 线条测量工具

// 线条测量工具
function measurePolyline(polyline) {
    var path = polyline.getPath();
    var totalDistance = 0;
    
    for (var i = 1; i < path.length; i++) {
        var distance = AMap.GeometryUtil.distance(path[i - 1], path[i]);
        totalDistance += distance;
    }
    
    return {
        totalDistance: totalDistance, // 总距离(米)
        totalDistanceKm: (totalDistance / 1000).toFixed(2), // 总距离(公里)
        pointCount: path.length, // 节点数量
        segments: path.length - 1 // 线段数量
    };
}

// 线条简化(道格拉斯-普克算法)
function simplifyPolyline(path, tolerance = 0.0001) {
    if (path.length <= 2) return path;
    
    function getPerpendicularDistance(point, lineStart, lineEnd) {
        var A = point[0] - lineStart[0];
        var B = point[1] - lineStart[1];
        var C = lineEnd[0] - lineStart[0];
        var D = lineEnd[1] - lineStart[1];
        
        var dot = A * C + B * D;
        var lenSq = C * C + D * D;
        var param = lenSq !== 0 ? dot / lenSq : -1;
        
        var xx, yy;
        if (param < 0) {
            xx = lineStart[0];
            yy = lineStart[1];
        } else if (param > 1) {
            xx = lineEnd[0];
            yy = lineEnd[1];
        } else {
            xx = lineStart[0] + param * C;
            yy = lineStart[1] + param * D;
        }
        
        var dx = point[0] - xx;
        var dy = point[1] - yy;
        return Math.sqrt(dx * dx + dy * dy);
    }
    
    function douglasPeucker(points, tolerance) {
        if (points.length <= 2) return points;
        
        var maxDistance = 0;
        var maxIndex = 0;
        
        for (var i = 1; i < points.length - 1; i++) {
            var distance = getPerpendicularDistance(
                points[i], 
                points[0], 
                points[points.length - 1]
            );
            
            if (distance > maxDistance) {
                maxDistance = distance;
                maxIndex = i;
            }
        }
        
        if (maxDistance > tolerance) {
            var left = douglasPeucker(points.slice(0, maxIndex + 1), tolerance);
            var right = douglasPeucker(points.slice(maxIndex), tolerance);
            
            return left.slice(0, -1).concat(right);
        } else {
            return [points[0], points[points.length - 1]];
        }
    }
    
    return douglasPeucker(path, tolerance);
}