摘要:在數(shù)學(xué)上,遞推關(guān)系,也就是差分方程,是一種遞推地定義一個(gè)序列的方程式序列的每一項(xiàng)目是定義為前一項(xiàng)的函數(shù)。某些簡(jiǎn)單定義的遞推關(guān)系式可能會(huì)表現(xiàn)出非常復(fù)雜的混沌的性質(zhì),他們屬于數(shù)學(xué)中的非線性分析領(lǐng)域。
每周一點(diǎn)canvas動(dòng)畫(huà)代碼文件
好像上次更新還是十一前,這唰唰唰的就過(guò)去大半個(gè)月了,現(xiàn)在更新我也沒(méi)什么不好意思的。這次我們不涉及canvas 3D的內(nèi)容,主要分享一個(gè)比較炫的動(dòng)畫(huà)效果,可以算是上一篇文章《每周一點(diǎn)canvas動(dòng)畫(huà)》——3D點(diǎn)線與水波動(dòng)畫(huà)的加強(qiáng)版。動(dòng)畫(huà)效果來(lái)自codePen。在這篇文章中我們就分析這種效果是如何實(shí)現(xiàn)的,如果你對(duì)源碼比較懵逼,相信看完解析就會(huì)恍然大悟。先上效果圖:
1.原理分析相比與上篇文章中簡(jiǎn)陋的水波動(dòng)畫(huà)的效果,本文的動(dòng)畫(huà)效果不僅能夠和鼠標(biāo)進(jìn)行交互,而且波浪的形成更加自然,更加符合物理規(guī)律。整個(gè)動(dòng)畫(huà)的形成過(guò)程就如動(dòng)圖中所展示的那樣,在液面的位置點(diǎn)擊鼠標(biāo),此處的液面就會(huì)有一個(gè)比較大的起伏,然后此處的震動(dòng)會(huì)向兩邊傳播,隨著能量的衰減,后面的震動(dòng)幅度會(huì)越來(lái)越下,最后能量衰減到零,頁(yè)面趨于平靜。聽(tīng)上去是不是很玄乎,感覺(jué)很高深!毛主席告訴我們千萬(wàn)不要被物體的表面現(xiàn)象所迷惑(誰(shuí)知道是誰(shuí)說(shuō)的呢o(^▽^)o)。下面我們就來(lái)一步一步的分析,這其中的原理。
首先,在靜止?fàn)顟B(tài)下我們可以看到整個(gè)液面就相當(dāng)于是個(gè)矩形。而當(dāng)我們點(diǎn)擊液面的位置時(shí),這個(gè)矩形就發(fā)生了相應(yīng)的變化。但其實(shí)并不是整個(gè)矩形都發(fā)生了變化,而只是矩形的上邊發(fā)生了變化。那是如何做到僅僅讓矩形的上邊發(fā)生變化的呢?秘訣就在矩形的上邊并不是簡(jiǎn)單的從左邊的點(diǎn)lineTo()到右邊的點(diǎn)。而是由很多的點(diǎn)lineTo()組成。這樣講可能不太好理解,看圖說(shuō)話:
在上部我們?cè)O(shè)置了很多的點(diǎn),這些點(diǎn)的縱坐標(biāo)都是一樣的,只是在水平方向相隔一定的間距。這樣在靜止的狀態(tài)下,我們就可以它看見(jiàn)與普通的矩形別無(wú)二致。而改變這些點(diǎn)的位置時(shí)我們就能同時(shí)改變矩形的形狀,從而形成不同的效果。
2.差分方程說(shuō)到差分方程也許很多人會(huì)頭疼,不過(guò)也沒(méi)本法,疼就疼會(huì)吧!這個(gè)知識(shí)點(diǎn)在高數(shù)里講微分方程那一節(jié),如果不明白,就算了吧!記住下面的用法也不錯(cuò),不過(guò)為了逼格我們還是簡(jiǎn)單的介紹下。
在數(shù)學(xué)上,遞推關(guān)系(recurrence relation),也就是差分方程(difference equation),是一種遞推地定義一個(gè)序列的方程式:序列的每一項(xiàng)目是定義為前一項(xiàng)的函數(shù)。某些簡(jiǎn)單定義的遞推關(guān)系式可能會(huì)表現(xiàn)出非常復(fù)雜的(混沌的)性質(zhì),他們屬于數(shù)學(xué)中的非線性分析領(lǐng)域。
記住一點(diǎn),序列的每一項(xiàng)是定義為前一項(xiàng)的函數(shù),我們用的就是這個(gè)原理。他的圖像如果用matalab來(lái)繪制就是下面這樣:
只關(guān)注原函數(shù),紅色的那條曲線就可以了,是不是特別像水波。我們要做的就是讓那一堆點(diǎn)按照這樣的波形去排列。
3.代碼實(shí)現(xiàn) 1.準(zhǔn)備工作下面就到了大家最喜歡的代碼時(shí)間。首先,我們創(chuàng)建一個(gè)點(diǎn)類(lèi)Vertexes, 它的作用就是定義并更新那一堆點(diǎn),代碼在vertex.js中,如下:
function Vertex(x,y,baseY){ this.baseY = baseY; //基線 this.x = x; //點(diǎn)的坐標(biāo) this.y = y; this.vy = 0; //豎直方向的速度 this.targetY = 0; //目標(biāo)位置 this.friction = 0.15; //摩擦力 this.deceleration = 0.95; //減速 } //y坐標(biāo)更新 Vertex.prototype.updateY = function(diffVal){ this.targetY = diffVal + this.baseY; //改變目標(biāo)位置 this.vy += (this.targetY - this.y); //速度 this.vy *= this.deceleration; this.y += this.vy * this.friction; //改變坐標(biāo)豎直方向的位置 }
我們要用這個(gè)函數(shù)去創(chuàng)建那一堆點(diǎn)?;氐轿覀兊闹魑募ndex.js中。我們先初始化一些要用的東西:
var canvas = document.getElementById("canvas"), ctx = canvas.getContext("2d"), W = window.innerWidth; H = window.innerHeight; canvas.width = W; canvas.height = H; var color1 = "#6ca0f6", //矩形1的顏色 color2 = "#367aec"; //矩形2的顏色 var vertexes = [], //頂點(diǎn)坐標(biāo) verNum = 250, //頂點(diǎn)數(shù) diffPt = [], //差分值
然后,創(chuàng)建點(diǎn)并把它push進(jìn)vertexes中,同時(shí)也創(chuàng)建相應(yīng)數(shù)量的差分值,同樣把它放到diffPt數(shù)組中,這樣每個(gè)點(diǎn)都有了對(duì)應(yīng)的差分值。
for(var i=0; i結(jié)果是,每個(gè)頂點(diǎn)的y坐標(biāo)都在(H/2)的高度,水平坐標(biāo)每隔一定的間隔取一個(gè)點(diǎn)。在這里是每隔4.5個(gè)像素取一個(gè)點(diǎn),這與你canvas的寬度和點(diǎn)的數(shù)目有關(guān)。這樣我們就把點(diǎn)創(chuàng)建完成了,來(lái)繪制一下看看效果。
代碼如下:
function draw(){ //矩形1 ctx.save() ctx.fillStyle = color1; ctx.beginPath(); ctx.moveTo(0, H); ctx.lineTo(vertexes[0].x, vertexes[0].y); for(var i=1; i就像你看到的那樣此時(shí)我們的液面完全是靜止的(因?yàn)闆](méi)更新點(diǎn)嘛)。之所以要繪制兩個(gè)矩形,你看看效果圖就明白了,只是為了更好看,你完全可以繪制第三層,第四層。下面我們就來(lái)更新這些點(diǎn)的坐標(biāo)。
2.核心代碼點(diǎn)的更新我們放在了update函數(shù)中。首先,我們?cè)O(shè)置一個(gè)初始的震蕩點(diǎn),緩沖變量和初始差分值。
var vPos = 125; //震蕩點(diǎn) var dd = 15; //緩沖 var autoDiff = 1000; //初始差分值這里的震蕩點(diǎn)就是我們的起震位置,意思是vertexes中的第125號(hào)點(diǎn)開(kāi)始起震,它對(duì)應(yīng)的差分值就是autoDiff。它的改變會(huì)引起其他點(diǎn)的變化,從而達(dá)到更新其他差分值的效果。
function update(){ autoDiff -= autoDiff*0.9; //1 diffPt[vPos] = autoDiff; //左側(cè) for(var i=vPos-1; i>0; i--){ //2 var d = vPos-i; if(d > dd){ d=dd; } diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d); } //右側(cè) for(var i=vPos+1; idd){ d=dd; } diffPt[i] -= (diffPt[i] - diffPt[i-1])*(1-0.01*d); } //更新Y坐標(biāo) for(var i=0; i 現(xiàn)在我們對(duì)上面的部分做詳細(xì)解釋?zhuān)?br>代碼1: 我們?cè)O(shè)置了起震位置的差分偏移量為autoDiff=100,注意autoDiff -= autoDiff*0.9;, 也就是說(shuō)它的值每一幀都會(huì)變化。
代碼2:為起震位置的左邊,主要關(guān)注diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);這一行。i的起始位置為124,默認(rèn)差分值為0。稍作簡(jiǎn)單推算,你會(huì)發(fā)現(xiàn),經(jīng)過(guò)更新后第124號(hào)點(diǎn)的差分值為99,同理第123號(hào)為97.02。以此類(lèi)推,我們就可以得到第一幀的所有點(diǎn)的差分值。右邊同理。
代碼4:在得到第一幀的差分值后就該調(diào)用每個(gè)點(diǎn)的更新函數(shù)了,并且傳入計(jì)算好的差分值。形成的效果如下圖所示
看一下updateY函數(shù),我們把目標(biāo)位置targetY設(shè)置為差分值diffVal和基線baseY的和。然后,通過(guò)距離計(jì)算需要運(yùn)動(dòng)的速度vy,最后將速度作用于點(diǎn)的縱坐標(biāo)。這一段是不是與彈性動(dòng)畫(huà)緩動(dòng)動(dòng)畫(huà)那一節(jié)很相似呢?
在緩沖系數(shù)dd的作用下,兩側(cè)的波會(huì)在擴(kuò)散的過(guò)程中越來(lái)越小,最后趨近于0.我們也是通過(guò)這個(gè)變量去控制液體的粘度系數(shù),達(dá)到粘稠度高的物體擴(kuò)散的越緩慢并且起伏比較低,粘稠度低的物體擴(kuò)散迅速但起伏大的效果。
隨后,因?yàn)?b>autoDiff的不斷衰減,不同幅值波形的疊加形成波浪效果,最終衰減到0.液面也就趨于平靜了。
現(xiàn)在,我們把update()和draw()放入動(dòng)畫(huà)循環(huán)中你就會(huì)看到水波起伏然后趨于平靜的效果。
(function drawframe(){ ctx.clearRect(0, 0, W, H); window.requestAnimationFrame(drawframe, canvas); update() draw(); })()3.鼠標(biāo)交互上面的代碼已經(jīng)實(shí)現(xiàn)了波浪動(dòng)畫(huà)的效果,但是震蕩完成后就平靜了,不會(huì)再發(fā)生震蕩的效果。這一步我們就來(lái)實(shí)現(xiàn)點(diǎn)哪,哪震的效果。實(shí)現(xiàn)的思路很簡(jiǎn)單:水波之所以區(qū)域平靜是因?yàn)槠鹫鹞恢玫牟罘种挡粩嗨p的結(jié)果,我們只需要在點(diǎn)擊鼠標(biāo)的位置重設(shè)autoDiff就可以了。此外,起震點(diǎn)的位置也要變成鼠標(biāo)點(diǎn)擊的位置。代碼如下:
canvas.addEventListener("mousedown", function(e){ var mouse = {x:null, y:null}; if(e.pageX||e.pageY){ mouse.x = e.pageX; mouse.y = e.pageY; }else{ mouse.x = e.clientX + document.body.scrollLeft +document.documentElement.scrollLeft; mouse.y = e.clientY + document.body.scrollTop +document.documentElement.scrollTop; } //重設(shè)差分值 if(mouse.y>(H/2-50) && mouse.y<(H/2 +50)){ autoDiff = 1000; vPos = 1 + Math.floor((verNum - 2) * mouse.x / W); diffPt[vPos] = autoDiff; } console.log(mouse.x, mouse.y) }, false)在獲取鼠標(biāo)位置這里應(yīng)該注意一點(diǎn),我們沒(méi)有減去canvas的偏移量,這是因?yàn)樵谶@里canvas做的是全屏設(shè)置。所以,如果你的畫(huà)布并不是全屏大小,建議你使用我們的utils.js文件中的方法captureMouse來(lái)獲取鼠標(biāo)的坐標(biāo)。
另外在判斷鼠標(biāo)是否點(diǎn)擊在了液面上,我們?cè)O(shè)定了一個(gè)比較寬的范圍,上下共100px。這樣做的目的是讓用戶很容易就能觸發(fā)這個(gè)事件,而不是只在頁(yè)面那唯一的一個(gè)值上才能觸發(fā)。這種做法相信你以前做過(guò),對(duì)于比較小的物體我們會(huì)遮罩一個(gè)大一些的透明物體,然后在該物體上做事件的觸發(fā),便于用戶操作。
其他的顏色改變等細(xì)小功能就不做過(guò)多的介紹了,see you!??!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/87984.html
摘要:在數(shù)學(xué)上,遞推關(guān)系,也就是差分方程,是一種遞推地定義一個(gè)序列的方程式序列的每一項(xiàng)目是定義為前一項(xiàng)的函數(shù)。 《每周一點(diǎn)canvas動(dòng)畫(huà)》——差分函數(shù)的妙用 每周一點(diǎn)canvas動(dòng)畫(huà)代碼文件 好像上次更新還是十一前,這唰唰唰的就過(guò)去大半個(gè)月了,現(xiàn)在才更新實(shí)在不好意思。這次我們不涉及canvas 3D的內(nèi)容,主要分享一個(gè)比較炫的動(dòng)畫(huà)效果,可以算是上一篇文章《每周一點(diǎn)canvas動(dòng)畫(huà)》——3D...
摘要:注以下所有代碼托管到動(dòng)畫(huà)的數(shù)理分析有了前面的基礎(chǔ)知識(shí),現(xiàn)在我們就會(huì)想如果我們能夠在每秒幀,內(nèi)渲染張圖像,并且每一張圖像的內(nèi)容發(fā)生微調(diào),那么在秒鐘整個(gè)畫(huà)面就會(huì)產(chǎn)生動(dòng)畫(huà)效果了。 什么是動(dòng)畫(huà)? 就像思考哲學(xué)問(wèn)題無(wú)法回避思維和存在的關(guān)系一樣,制作動(dòng)畫(huà)同樣無(wú)法逃避的問(wèn)題是動(dòng)畫(huà)的原理是什么?這里提一句題外話,任何原理的東西通常難以讓你短期拾掇成果,但在隱約的未來(lái)會(huì)起到難以置信的效果,不信就看接下來(lái)...
閱讀 1781·2021-11-12 10:35
閱讀 1703·2021-08-03 14:02
閱讀 2770·2019-08-30 15:55
閱讀 2101·2019-08-30 15:54
閱讀 842·2019-08-30 14:01
閱讀 2490·2019-08-29 17:07
閱讀 2312·2019-08-26 18:37
閱讀 3105·2019-08-26 16:51