貝塞爾曲線
bezierCurveTo
線上工具
https://canvature.appspot.com/ [感覺這個好用一些]
https://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html
三次貝塞爾曲線必須包含三個點。前兩個點(cp1x,cp1y)
和(cp2x,cp2y)
是在三次貝塞爾曲線計算中使用的控制點,最後一個點(x,y)
是曲線的終點。
bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)
參量 | 型別 | 描述 |
---|---|---|
cp1x | number | 第一個貝塞爾控制點的x座標。 |
cp1y | number | 第一個貝塞爾控制點的y座標。 |
cp2x | number | 第二個貝塞爾控制點的x座標。 |
cp2y | number | 第二個貝塞爾控制點的y座標。 |
X | number | 要新增到當前路徑的點的x座標。 |
y | number | 要新增到當前路徑的點的y座標。 |
-
(0,300)是曲線的起點。[這個用
moveTo(x,y)
嘆氣的點] -
(150,0)即(cp1x,cp1y)是曲線的第一個控制位置。
-
(350,0)即(cp2x,cp2y)是曲線的第二個控制位置。
-
(500,300),即(x,y)是曲線的終點。
quadraticCurveTo
二次貝塞爾曲線,需要兩個點,控制點和曲線的終點
- (0,300)是曲線的起點。
- (250,0)即(cp1x,cp1y)是曲線的控制位置。
- (500,300),即(x,y)是曲線的終點。
繪製視覺化的二次貝塞爾曲線demo
初始化
<style>
* {
margin: 0;
padding: 0;
}
#app {
width: 100vw;
height: 100vh;
min-width: 1100px;
overflow: hidden;
font-family: 'Lato', sans-serif;
background-color: #ffffff;
}
#canvas {
border: 1px solid #ccc;
position: absolute;
top: 20px;
left: 20px;
}
#code{
margin-top:20px;
margin-left:520px;
display: inline-block;
padding: 0.5em;
background: #002b36;
color: #839496;
min-height: 11rem;
font-family: Consolas;
font-size: 20px;
}
</style>
<div id="app">
<canvas id="canvas" width="500" height="500"></canvas>
<pre id="code">code</pre>
</div>
let canvas = document.querySelector('#canvas')
// 程式碼文字
let code = document.querySelector('#code');
let ctx = canvas.getContext('2d'),
point,
style = {// 原點樣式
radius: 10,
width: 2,
color: '#900',
fill: 'rgba(200,200,200,.5)',
arc1: 0,
arc2: 2 * Math.PI
},
drag = null,// 按下的時候 確認滑鼠拿的那一個點
dPoint, // 拿到當前點的座標
cpline = {
width: 1,
color: 'red'
},
curve = {
width: 6,
color: '#333'
}
// 初始化預設資料
function init() {
point = {// 滑鼠的三個點
p1: { // moveTo
x: 100, y: 50
},
cp1: { // 貝塞爾第一個點
x: 100, y: 200
},
p2: {// 貝塞爾第二個點
x: 300, y: 200
}
}
}
// 程式碼文字
function showCode() {
if (code) {
code.firstChild.nodeValue =
"theCanvas = document.getElementById(\"canvas\");\n" +
"ctx = theCanvas.getContext(\"2d\")\n" +
"ctx.lineWidth = " + curve.width +
";\nctx.strokeStyle = \"" + curve.color +
"\";\nctx.beginPath();\n" +
"ctx.moveTo(" + point.p1.x + ", " + point.p1.y + ");\n" +
"ctx.quadraticCurveTo(" + point.cp1.x + ", " + point.cp1.y + ", " + point.p2.x + ", " + point.p2.y + ");"
+
"\nctx.stroke();"
;
}
}
畫出頁面
function drawScreen() {
// 清空畫布
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = cpline.width;
ctx.strokeStyle = cpline.color;
ctx.beginPath()
ctx.moveTo(point.p1.x, point.p1.y)
ctx.lineTo(point.cp1.x, point.cp1.y)
ctx.lineTo(point.p2.x, point.p2.y)
ctx.stroke();
//中間的弧度
ctx.lineWidth = curve.width;
ctx.strokeStyle = curve.color;
ctx.beginPath();
ctx.moveTo(point.p1.x, point.p1.y)
ctx.quadraticCurveTo(point.cp1.x, point.cp1.y, point.p2.x, point.p2.y)
ctx.stroke();
// 三個原點
for (let p in point) {
console.log(p);
ctx.lineWidth = style.width;
ctx.strokeStyle = style.color;
ctx.fillStyle = style.fill;
ctx.beginPath();
ctx.arc(point[p].x, point[p].y, style.radius, style.arc1, style.arc2, true)
ctx.fill();
ctx.stroke();
}
showCode()
}
拿到滑鼠的滑鼠
// 滑鼠的座標
function MousePos(event) {
event = event ? event : window.event;
return {
x: event.pageX - canvas.offsetLeft,
y: event.pageY - canvas.offsetTop
}
}
問個來了,當我們滑鼠移動的時候怎麼確定滑鼠放在圓裡面啦
這裡又運用了初中數學知識圓的標準方程
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', dragging, false);
canvas.addEventListener('mouseup', dragEnd, false);
canvas.addEventListener('mouseout', dragEnd, false);
這裡我們需要知道點應該在圓內
滑鼠按下的時候
function dragStart(e) {
e = MousePos(e)
let dx, dy;
// 找到滑鼠拿到哪一個點
for (let p in point) {
dx = point[p].x - e.x;
dy = point[p].y - e.y;
if ((dx ** 2) + (dy ** 2) < style.radius ** 2) {
// 確定了拿到那個點
drag = p;
// 確定了拿到點的滑鼠
dPoint=e;
canvas.style.cursor = 'move';
return;
}
}
}
滑鼠移動
function dragging(e) {
// 這個是用來判斷有按下的引數的時候觸發
if (drag) {
e = MousePos(e);
// 滑鼠的x - 開始滑鼠的點
point[drag].x += e.x - dPoint.x;
point[drag].y += e.y - dPoint.y;
dPoint = e;
drawScreen();
}
}
滑鼠離開
function dragEnd(e) {
drag = null;
canvas.style.cursor = 'default';
drawScreen();
}
再二次貝塞爾曲線的基礎上繪製三次貝塞爾曲線
修改1,給html
新增一個class
<canvas id="canvasOne" width="500" height="500" class="bezier"></canvas>
在初始化資料的時候,新增第二個點
// 初始化預設資料,預設不傳引數三次貝塞爾曲線,不預設二次
function init(quadratic) {
....
if (quadratic) {
point.cp1={
x:250,y:100
}
}else{
point.cp1={
x:150,y:100
}
point.cp2={
x:350,y:100
}
}
畫出螢幕的時候
function drawScreen() {
...
ctx.lineTo(point.cp1.x, point.cp1.y)
// 判斷是否有第二個點
if (point.cp2) {
ctx.moveTo(point.p2.x,point.p2.y)
ctx.lineTo(point.cp2.x,point.cp2.y)
}else{
ctx.lineTo(point.p2.x, point.p2.y);
}
...
ctx.moveTo(point.p1.x, point.p1.y)
// 確認二次還是三次
if (point.cp2) {
ctx.bezierCurveTo(point.cp1.x, point.cp1.y, point.cp2.x, point.cp2.y, point.p2.x, point.p2.y)
}else{
ctx.quadraticCurveTo(point.cp1.x, point.cp1.y, point.p2.x, point.p2.y);
}
...
}
修改程式碼顯示的部分
+(point.cp2 ?
"ctx.bezierCurveTo("+point.cp1.x+", "+point.cp1.y+", "+point.cp2.x+", "+point.cp2.y+", "+point.p2.x+", "+point.p2.y+");" :
"ctx.quadraticCurveTo("+point.cp1.x+", "+point.cp1.y+", "+point.p2.x+", "+point.p2.y+");"
) +