概述
本文分享一个二阶贝塞尔曲线曲线生成弧线的算法。
效果
实现
1. 封装方法
class ArcLine {constructor(from, to, num = 100) {this.from = from;this.to = to;this.num = num;return this.getPointList();}getPointList() {const { from, to } = thisconst ctrlPoint = this.getOffsetPoint(from, to);const points = this.create2PBezier(from, ctrlPoint, to)return points}getOffsetPoint(start, end) {const distance = this.getDistance(start, end) / 2; //除以3?let angle, dX, dY;const mp = [start[0], start[1]];const deltaAngle = - Math.PI / 8; //偏移0.2弧度if (start[0] != end[0] && start[1] != end[1]) { //斜率存在const k = (end[1] - start[1]) / (end[0] - start[0]);angle = Math.atan(k);} else if (start[0] == end[0]) { //垂直线angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2;} else { //水平线angle = 0;}if (start[0] <= end[0]) {angle -= deltaAngle;dX = Math.round(Math.cos(angle) * distance);dY = Math.round(Math.sin(angle) * distance);mp[0] += dX;mp[1] += dY;} else {angle += deltaAngle;dX = Math.round(Math.cos(angle) * distance);dY = Math.round(Math.sin(angle) * distance);mp[0] -= dX;mp[1] -= dY;}return mp;}getDistance(p1, p2) {return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));}bezier2P(p0, p1, p2, t) {const P0 = p0 * Math.pow(1 - t, 2);const P1 = p1 * 2 * t * (1 - t);const P2 = p2 * t * t;return P0 + P1 + P2;}getBezierNowPoint2P(p0, p1, p2, num, tick) {return {x: this.bezier2P(p0[0], p1[0], p2[0], num * tick),y: this.bezier2P(p0[1], p1[1], p2[1], num * tick),};}create2PBezier(p0, p1, p2) {const num = this.numconst t = 1 / (num - 1);const points = [];for (let i = 0; i < num; i++) {const point = this.getBezierNowPoint2P(p0, p1, p2, i, t);points.push([point.x, point.y]);}return points;}
}
2. 前端调用
示例使用openlayers实现。
let vetSource = new ol.source.Vector({features: [],
});
let vectorLayer = new ol.layer.Vector({source: vetSource,style: new ol.style.Style({stroke: new ol.style.Stroke({color: "#f00",width: 2,}),}),
});
map.addLayer(vectorLayer)function addAllLines() {let features = [];for (let i = 0; i < pointData.length; i++) {const after = pointData[i];const from = [101.797439042302, 36.5937248286007];const to = [after.lon, after.lat].map(Number);let points = new ArcLine(from, to);points = points.map((p) => ol.proj.fromLonLat(p));lineData.push(points);features.push(new ol.Feature({geometry: new ol.geom.LineString(points),}));}vetSource.addFeatures(features);map.getView().animate({center: [12474607.173951693, 4278483.982819865],zoom: 3.8,});
}