日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

贝塞尔曲线理解与应用

發布時間:2023/12/2 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 贝塞尔曲线理解与应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

貝塞爾曲線并非是由貝塞爾發明的,但是是因為他把這個東西應用到當時的汽車領域而聞名的,所以取名為貝塞爾曲線。
在我看來,用簡單的話來理解一下貝塞爾曲線,他是通過少量幾個點,使用一套公式,生成一條平滑曲線。

原理

先盜用人家的圖,嘿嘿。

平面ABC 3個點。 在AB上找一個點D,在BC上找一個點E,使得AD:AB = BE:BC 然后在DE上找一個點F,使得DF:DE = AD:AB = BE:BC 接著,我們將D點從A點 --> B點慢慢移動,在這個過程中,會產生一系列的F點,將這些F點相連,就會形成一條曲線,嘿嘿,就是我們的貝塞爾曲線, 從這里可以看出,這里有3個關鍵點,起始點、終止點、控制點。 數學上的推理驗證,這里就不講了,直接給出公式。

二階貝塞爾曲線,一個控制點

三階貝塞爾曲線,二個控制點

一階貝塞爾曲線,就是一條直線

為了完整性,我給出貝塞爾曲線的n階通式

想看這個公式推導,我給出一個文章鏈接 n公式推導推導。 但是在一般應用中,二階,三階貝塞爾曲線是已經夠用了。

應用

先簡單的來使用一下,通過公式來描繪曲線。

***d2(){this.name = '二次貝賽爾曲線方程';let _this = this;let oCanvas = document.querySelector("#canvas"),oGc = oCanvas.getContext('2d');let percent = 0;function animate() {oGc.clearRect(0, 0, 800, 800);oGc.beginPath();oGc.strokeStyle = 'red';oGc.moveTo( 40, 80 );//oGc.quadraticCurveTo( 137, 80, 140, 280 );_this.d2_(oGc,[40, 80],[137, 80],[140, 280],percent);oGc.stroke();percent = (percent 1) % 100;requestAnimationFrame(animate);}animate()},d2_(oGc,start,cp,end, percent){for (var t = 0; t <= percent / 100; t = 0.01) { var x = this.quadraticBezier(start[0], cp[0], end[0], t); var y = this.quadraticBezier(start[1], cp[1], end[1], t);oGc.lineTo(x, y);} },quadraticBezier(p0, p1, p2, t) {var k = 1 - t;return k * k * p0 2 * (1 - t) * t * p1 t * t * p2; // 這個方程就是二次貝賽爾曲線方程 },***

這個就是根據公式描述出相關的點,然后連接起來。 但是在實際應用中,很大程度上會在canvas中繪圖,canvas提供2個api,
quadraticCurveTo:二階貝塞爾曲線,參數是 控制點,結束點
bezierCurveTo :三階貝塞爾曲線,參數是 控制點1,控制點2,結束點
你們發現沒,它們沒有開始點,它們的開始點是畫筆開始的位置。

在舉一個例子,畫起伏波浪

直接講思路,就是先畫一個靜止的波浪

好,現在來看一下,這個該怎么入手,先把這個輪廓描繪出來,要描繪,先拆分, 它是由一條曲線,3條直接拼接而成,有了這個思路,已經完成了一半, 那條曲線該如何繪制,其實我覺得思路不止一種,我們應該先自己給這個曲線下定義,我認為他應該是半圓的弧連接,應該是橢圓的弧鏈接,應該是其他。我先給它下一個定義

我用二階和三階分別來描述這個曲線,1,2,3,4這4個點描述出來了,那么這個曲線也就繪制完成了
1: (0.5d,waveH)
2: (d, 0)
3: (1.5d,-waveH)
4: (2d,0)
我選擇的這個規則是很中規中矩的,上一個波形是畫2個二階貝塞爾曲線,下一個波形是畫一個3階貝塞爾曲線。這個就可以把靜止的波形給繪制出來了,然后你想象一個給這個坐標加橫向偏移,加縱向偏移,他就可以起伏波動了

***init2(){this.name = '2階';let c = document.getElementById("myCanvas"),ctx = c.getContext("2d"),waveWidth = 800,offset = 0, //xwaveHeight = 20, // 波浪大小waveCount = 5,startX = -200,startY = 208,progress = 0, //高度progressStep = 0.5,d2 = waveWidth / waveCount,d = d2 / 2,hd = d / 2;ctx.fillStyle = "rgba(0,222,255, 0.2)";function tick() {offset -= 4; // x 移動progress = progressStep;if (progress > 220 || progress < 0) progressStep *= -1;if (-1 * offset === d2) offset = 0;ctx.clearRect(0, 0, c.width, c.height);ctx.beginPath();let offsetY = startY - progress; //y 坐標高低ctx.moveTo(startX - offset, offsetY);for (var i = 0; i < waveCount; i ) {var dx = i * d2;var offsetX = dx startX - offset;ctx.quadraticCurveTo(offsetX hd, offsetY waveHeight, offsetX d, offsetY);ctx.quadraticCurveTo(offsetX hd d, offsetY - waveHeight, offsetX d2, offsetY);}ctx.lineTo(startX waveWidth, 300);ctx.lineTo(startX, 300);ctx.fill();requestAnimationFrame(tick);}tick();}, ***

上面是二階貝塞爾曲線,用三階畫的話,就是
ctx.quadraticCurveTo(offsetX hd, offsetY waveHeight, offsetX d, offsetY);
ctx.quadraticCurveTo(offsetX hd d, offsetY - waveHeight, offsetX d2, offsetY);

換成
ctx.bezierCurveTo(offsetX hd, offsetY waveHeight, offsetX d hd, offsetY-waveHeight, offsetX d2, offsetY );
就可以了。

其實我覺得貝塞爾曲線在使用過程中,最關鍵的是控制點的選擇,不同點的選擇,會展現不同的效果,但是選擇控制點,是一件挺有意思的事。
下面我們再來看一個案例,粘性拖動

要實現這個功能,來理一下思路,首先來描繪一下這個輪廓,一樣的套路,是不是,來,思考一下,這個圖形是由什么組成的。 畫的丑,別介意,這么看這個輪廓,是不是出來了,你可以想象,是由2個半圓的圓弧和2條曲線,可以先畫ABCD這個路徑,再畫2個圓,這樣這個輪廓就出來了。接下來,再看這個曲線如何完成。這個曲線開始和結束點已經有了,再找一個控制點也能畫出來,那么控制點在哪里,我下的定義簡單粗暴,在2圓心的鏈接線的終點,然后再把ABCD 4個點描述出來,這個路徑就解決了,如何描述ABCD,請允許我盜圖 如何讓這個圖形動起來,可以這么想第一個圓,可以是手開始觸摸的點,也可以自己先寫死,另一個圓是手拖動的位置,所以只要動態的改變第二個圓心的位置,那么這個拖動的效果就出來了。 我在拖動的時候,d的距離在改變,那么制定一個規則,d越大,第一個圓的半徑就越小,那么基本上就可以實現了。

***data() {return {radius: 7,x: 300,//手移動y: 300,//手移動anchorX: 200,// 控制點anchorY: 200,// 控制點startX: 100, //開始startY: 100,//開始}},mounted() {document.removeEventListener('touchstart', this.wrapTouchStart);document.addEventListener("touchstart", this.wrapTouchStart);document.removeEventListener('touchmove', this.wrapTouchMove);document.addEventListener('touchmove', this.wrapTouchMove);document.removeEventListener('touchend', this.wrapTouchEnd);document.addEventListener('touchend', this.wrapTouchEnd);document.removeEventListener('touchcancel', this.wrapTouchCancel);document.addEventListener('touchcancel', this.wrapTouchCancel);},methods: {wrapTouchStart(e) {},wrapTouchMove(e) {this.x = e.changedTouches[0].clientX;this.y = e.changedTouches[0].clientY;this.anchorX = (e.changedTouches[0].clientX this.startX) / 2;this.anchorY = (e.changedTouches[0].clientY this.startY) / 2;this.d2();},wrapTouchEnd() {this.radius = 20;// 手勢坐標this.x = 300;this.y = 300;// 控制點坐標this.anchorX = 200;this.anchorY = 200;// 起點坐標this.startX = 100;this.startY = 100;},wrapTouchCancel() {let oCanvas = document.querySelector("#canvas"),ctx = oCanvas.getContext('2d');ctx.clearRect(0, 0, 360, 600);},d2() {let _this = this;let oCanvas = document.querySelector("#canvas");ctx = oCanvas.getContext('2d');ctx.strokeStyle = 'red';var distance = Math.sqrt(Math.pow(this.y - this.startY, 2) Math.pow(this.x - this.startX, 2));this.radius = -distance / 15 20;// 當氣泡拉到一定程度,斷開鏈條且鏈條消失//if (this.radius < 7) {if(distance > 250){ctx.clearRect(0, 0, 360, 600);ctx.beginPath();ctx.arc(this.x, this.y, 20, 0, 2 * Math.PI);ctx.strokeStyle = 'red';ctx.fill();console.log('end');return;}let sin = (this.x - this.startX) / distance;let cos = (this.y - this.startY) / distance;var x1 = this.startX - this.radius * cos;var y1 = this.startY this.radius * sin;var x2 = this.x - 20 * cos;var y2 = this.y 20 * sin;var x3 = this.x 20 * cos;var y3 = this.y - 20 * sin;var x4 = this.startX this.radius * cos;var y4 = this.startY - this.radius * sin;ctx.clearRect(0, 0, 360, 600);ctx.beginPath();ctx.moveTo(x1, y1);ctx.quadraticCurveTo(this.anchorX, this.anchorY, x2, y2);ctx.lineTo(x3, y3);ctx.quadraticCurveTo(this.anchorX, this.anchorY, x4, y4);ctx.lineTo(x1, y1);ctx.fillStyle = 'red'; ctx.stroke();ctx.fill();// 兩圓圈ctx.beginPath();ctx.arc(this.startX, this.startY, this.radius, 0, 2 * Math.PI)ctx.arc(this.x, this.y, 20, 0, 2 * Math.PI)ctx.strokeStyle = 'red';ctx.fill();},}***

到這里,應該要結束了,但是我想說這控制點,其實還有其他選擇,還有一種是是AC連線的中點,和BD連線的中點,具體的項目我晚一點附上地址。

by cs

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的贝塞尔曲线理解与应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。