高德地图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);
}