簡體   English   中英

使用HTML Canvas Bezier曲線繪制美觀(如果不准確)的飛行路徑

[英]Drawing good looking (if not accurate) flight paths with an HTML Canvas Bezier Curve

在傳單地圖上,我有幾點。 在這些點之間,發生流量。 為了直觀顯示,我想繪制這些點之間的飛行路徑。 主要的問題是這些點看起來不錯, 就像 2點之間的最短路徑一樣。 如果我全力以赴並正確實施此方法,美俄之間的飛行路線將如下所示:

美國飛往俄羅斯

美日之間的路徑如下所示:

美國到日本

這對我而言不是理想的。 我希望我的飛行路線不離開它而留在地圖上。 閱讀了本教程 (用R編寫的很難理解的R),我被指出要使用Bezier曲線作為飛行路徑。 我正在使用Leaflet插件fullcanvas ,並對其進行了修改。 我繪制曲線的代碼如下所示:

drawBezierCurve: function (startPoint, endPoint, style) {
        var context = this.getCanvas().getContext("2d");
        context.strokeStyle = (style && style.strokeStyle) ? style.strokeStyle : "rgba(0,0,0, 1)";
        context.lineWidth = (style && style.lineWidth) ? style.lineWidth : 3;

        var mapSize = map.getSize();

        var controlPoint1X = startPoint.x + 50;
        var controlPoint1Y = startPoint.y > (mapSize.y / 2) ? startPoint.y + 50 : startPoint.y - 50;
        var controlPoint2X = endPoint.x - 50;
        var controlPoint2Y = endPoint.y > (mapSize.y / 2) ? endPoint.y + 50 : endPoint.y - 50;

        context.moveTo(startPoint.x, startPoint.y);

        context.bezierCurveTo(controlPoint1X, controlPoint1Y, controlPoint2X, controlPoint2Y, endPoint.x, endPoint.y);
        context.stroke();
    }

僅當兩點之間的距離一定時,此方法的最終結果才可以:

我目前的結果

從美國出發的道路都是精心繪制的,就像我想要的那樣。 但是,從日本到澳大利亞的路線(彼此之間)和在東南亞的路線(彼此之間非常靠近)被弄亂了。 另外,如果放大或縮小,我都會得到如下效果:

哎呀

盡管對我的同事很有趣,但這並不是我想要的。 我的問題:

如何編輯JavaScript函數,使飛行路線看起來更自然? 飛行路線不一定正確,只需要看起來正常即可。 理想情況下,我會得到一個看起來像這樣的結果。

使用quadraticCurveTo() (二階Bezier)可能會產生更自然的曲線,因為它使用單個控制點而不是兩個控制點,您可以將其放置在線的中間,並基於與該線的切線的偏移量:

  • 獲得點之間的差異
  • 獲得90°偏移的角度(只需使用atan2切換diff x和y)
  • 使用插值獲得中點
  • 使用坡度作為半徑與中點和角度的半徑來創建目標點

快照

對於比例尺地圖,您可能需要調整代碼,以使斜率或多或少是自動的。 行長度的百分比-此示例使用百分比(或歸一化值):

例:

 function drawSlope(ctx, x1, y1, x2, y2, slope) { var dx = x2 - x1, // difference between points dy = y2 - y1, len = Math.sqrt(dx*dx + dy*dy), // length of line angle = Math.atan2(dx, dy), // angle + 90 deg offset (switch x/y) midX = x1 + dx * 0.5, // mid point midY = y1 + dy * 0.5, sx = midX + len * slope * Math.cos(angle), // midway slope point sy = midY - len * slope * Math.sin(angle); ctx.moveTo(x1, y1); ctx.quadraticCurveTo(sx, sy, x2, y2); } // prep and draw some example curves var ctx = canvas.getContext("2d"); ctx.lineWidth = 4; ctx.strokeStyle = "blue"; for(var i = 2; i > 0; i--) { drawSlope(ctx, 40, 100, 490, 300, 0.15); drawSlope(ctx, 60, 140, 480, 220, 0.15); drawSlope(ctx, 45, 120, 450, 30, -0.20); // create a "mini" version to show how it act scaled ctx.setTransform(0.3, 0, 0, 0.3, 50, canvas.height*0.4); } ctx.stroke(); 
 <canvas id=canvas width=500 height=500></canvas> 

Bezier曲線的缺點是該線不通過控制點。 為此,您將需要使用基數樣條/ Catmull-Rom。 如果需要,您可以例如查看我對Cardinal Spline的實現 您可以使用上面相同的代碼,只需將點推入數組並調用curve()函數。

希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM