簡體   English   中英

畫一條畫線,最后一點

[英]Make a canvas line with a point at the end

我正在嘗試將畫布的末端變成一個點(有點像箭頭,只是側面不應該超出線的寬度...請參見下圖,以了解線末端應如何使用的示例看)。

請參見下圖,以獲取有關線端外觀的示例。

我正在嘗試線上限,但是唯一可用的上限是“圓形”或“正方形”( http://www.w3schools.com/tags/canvas_linecap.asp )。

下面的小提琴是我試圖指出的終點。

http://jsfiddle.net/699ktkv8/

代碼如下:

<body>

<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(20, 100, 200, 100, 200, 20);
ctx.lineWidth=10
ctx.stroke();

</script> 

</body>

不幸的是,畫布上沒有這樣的功能。 您將必須手動計算直線的輸出角度(這意味着您需要實現Bezier數學)。

然后使用該線的寬度根據該角度自己繪制蓋帽。

第1步-找到方向

讓我們使用不向上或不成90°的一端使它更具挑戰性:

第1步

 var ctx = document.querySelector("canvas").getContext("2d"); // draw as normal ctx.beginPath(); ctx.moveTo(20, 20); ctx.bezierCurveTo(20, 100, 170, 70, 200, 20); ctx.lineWidth=10 ctx.stroke(); // get two points from the end var pt1 = order3(20, 20, 20, 100, 170, 70, 200, 20, 0.98); var pt2 = order3(20, 20, 20, 100, 170, 70, 200, 20, 1); // show direction ctx.lineWidth = 2; ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(pt1.x, pt1.y); ctx.lineTo(pt1.x + (pt2.x - pt1.x) * 10, pt1.y + (pt2.y - pt1.y) * 10); ctx.stroke(); //B(t) = (1-t)^3 * z0 + 3t (1-t)^2 * c0 + 3 t^2 (1-t) * c1 + t^3 * z1 for 0 <=t <= 1 function order3(z0x, z0y, c0x, c0y, c1x, c1y, z1x, z1y, t) { var tm1 = 1 - t, // (1 - t) tm12 = tm1 * tm1, // (1 - t) ^ 2 tm13 = tm12 * tm1, // (1 - t) ^ 3 t2 = t * t, // t ^ 2 t3 = t2 * t, // t ^ 3 tmm3 = t * 3 * tm12, // 3 xt * (1 - t) ^ 2 tmm23 = t2 * 3 * tm1, // t ^ 2 * 3 * (1 - t) x, y; x = (tm13 * z0x + tmm3 * c0x + tmm23 * c1x + t3 * z1x + 0.5) | 0; y = (tm13 * z0y + tmm3 * c0y + tmm23 * c1y + t3 * z1y + 0.5) | 0; return { x: x, y: y } } 
 <canvas width=220 height=100 /> 

更新或作為markE點,您可以從控制點進行計算(我很糟糕,我完全忘記了這一點-謝謝markE)-與使用“ t ”方法相比,這在大多數情況下可能是更好的方法。

為了完整起見,這里將其包括在內:

// calculate the ending angle from the two last nodes (cp2 and end point)
var dx = pt2.x - cp2.x;   // assumes points and control points as objects
var dy = pt2.y - cp2.y;
var angle = Math.atan2(dy, dx);

更新結束

第2步-查找角度和距離

我們需要計算實際角度,以便可以將其用作箭頭的底部:

// get angle
var diffX = pt1.x - pt2.x;   // see update comment above
var diffY = pt1.y - pt2.y;
var angle = Math.atan2(diffY, diffX);
var tangent = Math.atan2(diffX, -diffY);

第2步

(顯示為有意偏移)

第3步-拉帽

現在,我們有足夠的信息來確定上限:

第三步

 var ctx = document.querySelector("canvas").getContext("2d"); // draw as normal ctx.beginPath(); ctx.moveTo(20, 20); ctx.bezierCurveTo(20, 100, 170, 70, 200, 20); ctx.lineWidth=40 ctx.stroke(); // get two points from the end var pt1 = order3(20, 20, 20, 100, 170, 70, 200, 20, 0.98); var pt2 = order3(20, 20, 20, 100, 170, 70, 200, 20, 1); var diffX = pt1.x - pt2.x; var diffY = pt1.y - pt2.y; var angle = Math.atan2(diffY, diffX); var tangent = Math.atan2(diffX, -diffY); var lw = ctx.lineWidth * 0.5 - 0.5; // draw cap ctx.beginPath(); ctx.moveTo(pt2.x + lw * Math.cos(tangent), pt2.y + lw * Math.sin(tangent)); ctx.lineTo(pt2.x - lw * Math.cos(tangent), pt2.y - lw * Math.sin(tangent)); ctx.lineTo(pt1.x - lw * Math.cos(angle), pt1.y - lw * Math.sin(angle)); ctx.fill(); // due to inaccuracies, you may have to mask tiny gaps ctx.lineWidth = 1; ctx.stroke(); //B(t) = (1-t)^3 * z0 + 3t (1-t)^2 * c0 + 3 t^2 (1-t) * c1 + t^3 * z1 for 0 <=t <= 1 function order3(z0x, z0y, c0x, c0y, c1x, c1y, z1x, z1y, t) { var tm1 = 1 - t, // (1 - t) tm12 = tm1 * tm1, // (1 - t) ^ 2 tm13 = tm12 * tm1, // (1 - t) ^ 3 t2 = t * t, // t ^ 2 t3 = t2 * t, // t ^ 3 tmm3 = t * 3 * tm12, // 3 xt * (1 - t) ^ 2 tmm23 = t2 * 3 * tm1, // t ^ 2 * 3 * (1 - t) x, y; x = (tm13 * z0x + tmm3 * c0x + tmm23 * c1x + t3 * z1x + 0.5) | 0; y = (tm13 * z0y + tmm3 * c0y + tmm23 * c1y + t3 * z1y + 0.5) | 0; return { x: x, y: y } } 
 <canvas width=240 height=100 /> 

終點的實際角度取決於您選擇終點附近的哪個點(而不是實際終點,即t=1 )。 您可能必須計算總線長,並將其用作應為多少t基礎。

您還可能遇到角度不完全正確且出現小的間隙的情況。

您可以通過應用筆划來掩蓋這些間隙,也可以根據先前計算出的角度/方向(在步驟1中使用線性插值,只是使用負t )來稍微偏移蓋帽,或者使它准確的唯一其他方法是手動計算線的牆等,例如將其視為多邊形並將其填充為單個對象。

@KenFrystenberg已很好地回答了您的問題。

這是一個有趣的數學筆記,可簡化計算。

三次貝塞爾曲線的終止角(和起始角)可以直接從控制點計算:

// define 4 cubic Bezier control points
var cp0={x:20,y:20};
var cp1={x:20,y:100};
var cp2={x:170,y:70};
var cp3={x:200,y:20};

// calculate the ending angle from cp2 & cp3
var dx=cp3.x-cp2.x;
var dy=cp3.y-cp2.y;
var angle=Math.atan2(dy,dx);

這是示例代碼和有關此數學筆記的演示:

在此處輸入圖片說明

 var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var cw=canvas.width; var ch=canvas.height; var cp0={x:20,y:20}; var cp1={x:20,y:100}; var cp2={x:170,y:70}; var cp3={x:200,y:20}; ctx.lineWidth=10; ctx.beginPath(); ctx.moveTo(20, 20); ctx.bezierCurveTo(20, 100, 170, 70, 200, 20); ctx.lineWidth=20 ctx.stroke(); ctx.beginPath(); ctx.arc(200,20,3,0,Math.PI*2); ctx.closePath(); ctx.fillStyle='red'; ctx.fill(); var dx=cp3.x-cp2.x; var dy=cp3.y-cp2.y; var angle=Math.atan2(dy,dx); var x=cp3.x+15*Math.cos(angle); var y=cp3.y+15*Math.sin(angle); ctx.beginPath(); ctx.moveTo(cp3.x,cp3.y); ctx.lineTo(x,y); ctx.lineWidth=1; ctx.strokeStyle='red'; ctx.stroke(); 
 body{ background-color: ivory; } #canvas{border:1px solid red;} 
 <canvas id="canvas" width=300 height=300></canvas> 

暫無
暫無

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

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