成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

《每周一點canvas動畫》——差分函數的妙用

開發 前端
這次我們不涉及canvas 3D的內容,主要分享一個比較炫的動畫效果,可以算是上一篇文章《每周一點canvas動畫》——3D點線與水波動畫的加強版。動畫效果來自codePen點擊預覽。在這篇文章中我們就分析這種效果是如何實現的。

[[173998]]

每周一點canvas動畫代碼文件

好像上次更新還是十一前,這唰唰唰的就過去大半個月了,現在更新我也沒什么不好意思的。這次我們不涉及canvas 3D的內容,主要分享一個比較炫的動畫效果,可以算是上一篇文章《每周一點canvas動畫》——3D點線與水波動畫的加強版。動畫效果來自codePen點擊預覽。在這篇文章中我們就分析這種效果是如何實現的,如果你對源碼比較懵逼,相信看完解析就會恍然大悟。先上效果圖:

1.原理分析

相比與上篇文章中簡陋的水波動畫的效果,本文的動畫效果不僅能夠和鼠標進行交互,而且波浪的形成更加自然,更加符合物理規律。整個動畫的形成過程就如動圖中所展示的那樣,在液面的位置點擊鼠標,此處的液面就會有一個比較大的起伏,然后此處的震動會向兩邊傳播,隨著能量的衰減,后面的震動幅度會越來越下,最后能量衰減到零,頁面趨于平靜。聽上去是不是很玄乎,感覺很高深!毛主席告訴我們千萬不要被物體的表面現象所迷惑(誰知道是誰說的呢o(^▽^)o)。下面我們就來一步一步的分析,這其中的原理。

首先,在靜止狀態下我們可以看到整個液面就相當于是個矩形。而當我們點擊液面的位置時,這個矩形就發生了相應的變化。但其實并不是整個矩形都發生了變化,而只是矩形的上邊發生了變化。那是如何做到僅僅讓矩形的上邊發生變化的呢?秘訣就在矩形的上邊并不是簡單的從左邊的點lineTo()到右邊的點。而是由很多的點lineTo()組成。這樣講可能不太好理解,看圖說話:

在上部我們設置了很多的點,這些點的縱坐標都是一樣的,只是在水平方向相隔一定的間距。這樣在靜止的狀態下,我們就可以它看見與普通的矩形別無二致。而改變這些點的位置時我們就能同時改變矩形的形狀,從而形成不同的效果。

2.差分方程

說到差分方程也許很多人會頭疼,不過也沒本法,疼就疼會吧!這個知識點在高數里講微分方程那一節,如果不明白,就算了吧!記住下面的用法也不錯,不過為了逼格我們還是簡單的介紹下。

在數學上,遞推關系(recurrence relation),也就是差分方程(difference equation),是一種遞推地定義一個序列的方程式:序列的每一項目是定義為前一項的函數。某些簡單定義的遞推關系式可能會表現出非常復雜的(混沌的)性質,他們屬于數學中的非線性分析領域。

記住一點,序列的每一項是定義為前一項的函數,我們用的就是這個原理。他的圖像如果用matalab來繪制就是下面這樣:

只關注原函數,紅色的那條曲線就可以了,是不是特別像水波。我們要做的就是讓那一堆點按照這樣的波形去排列。

3.代碼實現

1).準備工作

下面就到了大家最喜歡的代碼時間。首先,我們創建一個點類Vertexes, 它的作用就是定義并更新那一堆點,代碼在vertex.js中,如下:

  1. function Vertex(x,y,baseY){ 
  2.         this.baseY = baseY;         //基線 
  3.         this.x = x;                 //點的坐標 
  4.         this.y = y;             
  5.         this.vy = 0;                //豎直方向的速度 
  6.         this.targetY = 0;           //目標位置 
  7.         this.friction = 0.15;       //摩擦力 
  8.         this.deceleration = 0.95;   //減速 
  9.     } 
  10. //y坐標更新 
  11. Vertex.prototype.updateY = function(diffVal){ 
  12.         this.targetY = diffVal + this.baseY;   //改變目標位置 
  13.         this.vy += (this.targetY - this.y);       //速度 
  14.         this.vy *= this.deceleration; 
  15.         this.y += this.vy * this.friction;     //改變坐標豎直方向的位置 
  16.     } 

我們要用這個函數去創建那一堆點。回到我們的主文件index.js中。我們先初始化一些要用的東西:

  1. var canvas = document.getElementById('canvas'), 
  2.     ctx = canvas.getContext('2d'), 
  3.     W = window.innerWidth; 
  4.     H = window.innerHeight; 
  5.  
  6.     canvas.width = W; 
  7.     canvas.height = H; 
  8.  
  9. var color1 = "#6ca0f6",    //矩形1的顏色 
  10.     color2 = "#367aec";   //矩形2的顏色 
  11.      
  12. var vertexes = [],    //頂點坐標 
  13.     verNum = 250,     //頂點數 
  14.     diffPt = [],      //差分值 

然后,創建點并把它push進vertexes中,同時也創建相應數量的差分值,同樣把它放到diffPt數組中,這樣每個點都有了對應的差分值。

  1. for(var i=0; i<verNum; i++){ 
  2.     vertexes[i] = new Vertex(W/(verNum-1)*i, H/2, H/2); 
  3.     diffPt[i] = 0;                                         //初始值都為0 

結果是,每個頂點的y坐標都在(H/2)的高度,水平坐標每隔一定的間隔取一個點。在這里是每隔4.5個像素取一個點,這與你canvas的寬度和點的數目有關。這樣我們就把點創建完成了,來繪制一下看看效果。

[[174001]]

代碼如下:

  1. function draw(){ 
  2.          
  3.         //矩形1 
  4.         ctx.save() 
  5.         ctx.fillStyle = color1; 
  6.         ctx.beginPath(); 
  7.         ctx.moveTo(0, H); 
  8.         ctx.lineTo(vertexes[0].x, vertexes[0].y); 
  9.         for(var i=1; i<vertexes.length; i++){ 
  10.             ctx.lineTo(vertexes[i].x, vertexes[i].y); 
  11.         } 
  12.         ctx.lineTo(W,H); 
  13.         ctx.lineTo(0,H); 
  14.         ctx.fill(); 
  15.         ctx.restore(); 
  16.          
  17.         //矩形2 
  18.         ctx.save(); 
  19.         ctx.fillStyle = color2; 
  20.         ctx.beginPath(); 
  21.         ctx.moveTo(0, H); 
  22.         ctx.lineTo(vertexes[0].x, vertexes[0].y+5); 
  23.         for(var i=1; i<vertexes.length; i++){ 
  24.             ctx.lineTo(vertexes[i].x, vertexes[i].y+5); 
  25.         } 
  26.         ctx.lineTo(W, H); 
  27.         ctx.lineTo(0, H); 
  28.         ctx.fill(); 
  29.         ctx.restore(); 

就像你看到的那樣此時我們的液面完全是靜止的(因為沒更新點嘛)。之所以要繪制兩個矩形,你看看效果圖就明白了,只是為了更好看,你完全可以繪制第三層,第四層。下面我們就來更新這些點的坐標。

2).核心代碼

點的更新我們放在了update函數中。首先,我們設置一個初始的震蕩點,緩沖變量和初始差分值。

  1. var vPos = 125;  //震蕩點 
  2. var dd = 15;     //緩沖 
  3. var autoDiff = 1000;  //初始差分值 

這里的震蕩點就是我們的起震位置,意思是vertexes中的第125號點開始起震,它對應的差分值就是autoDiff。它的改變會引起其他點的變化,從而達到更新其他差分值的效果。

  1. function update(){ 
  2.         autoDiff -= autoDiff*0.9;        //1 
  3.         diffPt[vPos] = autoDiff;          
  4.  
  5.         //左側 
  6.         for(var i=vPos-1; i>0; i--){     //2 
  7.             var d = vPos-i; 
  8.             if(d > dd){ 
  9.                 d=dd; 
  10.             } 
  11.             diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d); 
  12.         } 
  13.         //右側 
  14.         for(var i=vPos+1; i<verNum; i++){   //3 
  15.             var d = i-vPos; 
  16.             if(d>dd){ 
  17.                 d=dd; 
  18.             } 
  19.             diffPt[i] -= (diffPt[i] - diffPt[i-1])*(1-0.01*d); 
  20.         } 
  21.  
  22.         //更新Y坐標 
  23.         for(var i=0; i<vertexes.length; i++){  //4 
  24.             vertexes[i].updateY(diffPt[i]); 
  25.         } 
  26.     } 

現在我們對上面的部分做詳細解釋:

代碼1: 我們設置了起震位置的差分偏移量為autoDiff=100,注意autoDiff -= autoDiff*0.9;, 也就是說它的值每一幀都會變化。

代碼2:為起震位置的左邊,主要關注diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);這一行。i的起始位置為124,默認差分值為0。稍作簡單推算,你會發現,經過更新后第124號點的差分值為99,同理第123號為97.02。以此類推,我們就可以得到第一幀的所有點的差分值。右邊同理。

代碼4:在得到第一幀的差分值后就該調用每個點的更新函數了,并且傳入計算好的差分值。形成的效果如下圖所示

看一下updateY函數,我們把目標位置targetY設置為差分值diffVal和基線baseY的和。然后,通過距離計算需要運動的速度vy,最后將速度作用于點的縱坐標。這一段是不是與彈性動畫緩動動畫那一節很相似呢?

在緩沖系數dd的作用下,兩側的波會在擴散的過程中越來越小,最后趨近于0.我們也是通過這個變量去控制液體的粘度系數,達到粘稠度高的物體擴散的越緩慢并且起伏比較低,粘稠度低的物體擴散迅速但起伏大的效果。

隨后,因為autoDiff的不斷衰減,不同幅值波形的疊加形成波浪效果,最終衰減到0.液面也就趨于平靜了。

現在,我們把update()和draw()放入動畫循環中你就會看到水波起伏然后趨于平靜的效果。

  1. (function drawframe(){ 
  2.         ctx.clearRect(0, 0, W, H); 
  3.         window.requestAnimationFrame(drawframe, canvas); 
  4.         update() 
  5.         draw(); 
  6.     })() 

3).鼠標交互

上面的代碼已經實現了波浪動畫的效果,但是震蕩完成后就平靜了,不會再發生震蕩的效果。這一步我們就來實現點哪,哪震的效果。實現的思路很簡單:水波之所以區域平靜是因為起震位置的差分值不斷衰減的結果,我們只需要在點擊鼠標的位置重設autoDiff就可以了。此外,起震點的位置也要變成鼠標點擊的位置。代碼如下:

  1. canvas.addEventListener('mousedown'function(e){ 
  2.         var mouse = {x:null, y:null}; 
  3.  
  4.         if(e.pageX||e.pageY){ 
  5.             mouse.x = e.pageX; 
  6.             mouse.y = e.pageY; 
  7.         }else
  8.             mouse.x = e.clientX + document.body.scrollLeft +document.documentElement.scrollLeft; 
  9.             mouse.y = e.clientY + document.body.scrollTop +document.documentElement.scrollTop; 
  10.         } 
  11.  
  12.         //重設差分值 
  13.         if(mouse.y>(H/2-50) && mouse.y<(H/2 +50)){ 
  14.             autoDiff = 1000; 
  15.             vPos = 1 + Math.floor((verNum - 2) * mouse.x / W); 
  16.             diffPt[vPos] = autoDiff; 
  17.         } 
  18.  
  19.         console.log(mouse.x, mouse.y) 
  20.  
  21.     }, false

在獲取鼠標位置這里應該注意一點,我們沒有減去canvas的偏移量,這是因為在這里canvas做的是全屏設置。所以,如果你的畫布并不是全屏大小,建議你使用我們的utils.js文件中的方法captureMouse來獲取鼠標的坐標。

另外在判斷鼠標是否點擊在了液面上,我們設定了一個比較寬的范圍,上下共100px。這樣做的目的是讓用戶很容易就能觸發這個事件,而不是只在頁面那唯一的一個值上才能觸發。這種做法相信你以前做過,對于比較小的物體我們會遮罩一個大一些的透明物體,然后在該物體上做事件的觸發,便于用戶操作。

其他的顏色改變等細小功能就不做過多的介紹了,see you!!!

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2009-12-24 17:10:42

WPF動畫類

2009-08-13 17:52:13

C#構造函數

2010-05-20 15:29:43

優化IIS

2012-03-27 08:49:19

Json

2009-07-09 15:09:05

JDK卸載

2009-09-14 19:44:27

LINQ To SQL

2025-05-29 00:00:00

UI 庫前端模塊化

2016-04-05 10:12:58

HiveSQLHadoop

2009-09-14 20:17:05

并行LINQ

2014-06-04 10:48:38

Swift蘋果iOS

2012-07-12 10:49:53

項目管理

2011-07-04 09:33:04

惠普轉型李艾科

2016-01-06 09:49:59

青云/SDN

2025-05-12 08:50:00

apply()Pandas函數

2013-01-08 10:06:43

創業創業方法

2022-03-09 09:00:41

SwiftUI視圖生成器Swift

2011-03-15 10:41:05

內部類

2009-12-15 14:27:30

Ruby存取器

2017-09-27 13:42:42

數據庫MySQL斷電恢復

2013-03-06 10:19:56

重構架構設計
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人自拍视频网站 | 欧美国产日韩成人 | 91免费在线 | 好姑娘影视在线观看高清 | 极品在线 | 又黑又粗又长的欧美一区 | 精品国产乱码久久久久久闺蜜 | 天堂一区二区三区 | 日韩资源| 亚洲欧美一区二区三区国产精品 | 在线观看国产视频 | 日韩在线免费视频 | 久久久久国产一区二区三区 | 黑人精品欧美一区二区蜜桃 | 日韩视频―中文字幕 | 国产午夜久久久 | 国产精品亚洲精品日韩已方 | 日韩中文字幕在线视频观看 | 国产一区不卡 | 美女视频h | 国产成人亚洲精品自产在线 | 欧美一区二区三区视频在线播放 | 久久久av| 日韩精品一二三区 | 免费在线成人 | 成人小视频在线观看 | 亚洲成av | av黄在线观看 | 亚洲国产精品91 | 91精品国产综合久久久久久漫画 | 二区三区视频 | 亚洲欧美日韩精品久久亚洲区 | 毛片免费观看视频 | 日韩中文字幕在线观看 | 中文字幕亚洲一区 | 国产一区精品在线 | 久久久久se | 日韩三级电影一区二区 | 欧美精品欧美精品系列 | 国产精品亚洲成在人线 | 成人欧美一区二区三区在线播放 |