遊離在平面中的畫素
上一篇文章中,我們講解了最基礎的粒子動畫,實現了在瀏覽器中跑來跑去的粒子以及講解了粒子的一些常見特效的原理。
我們把所有的運動公式都封裝在了dot類中,雖說實現了我們的當時需求,但是如果引申到一些方向性的東西的時候,就會顯得蒼白而無力。
平面中的指向標(vector2)
向量確實是一個神奇的東西,當座標系確定了之後,向量不僅僅可以看作是一個座標點,還可以看做是一段距離,同時還具有方向性,這就對於我們在平面中的定向操作就提供了很大的幫助。
-
定義一個二維向量類
function vector2(x,y) { //當前的座標的x,y this.x=x; this.y=y; }複製程式碼
-
向量的移動
當
x2
與y2
都為0的時候,也就是存在於座標原點時,相減之後還會是(x1,y1)
,這也是為什麼我說它不僅可以代表一個座標,還可以代表一段距離vector2.prototype.move = function(vec2) { this.x+=vec2.x; this.y+=vec2.y; };複製程式碼
-
把向量單位化
單位向量,可以理解成在一段向量上,長度為1的向量
vector2.prototype.normalize = function(vec2) { var x=vec2.x-this.x; var y=vec2.y-this.y; //以上是為了將當前座標轉換為原點,從而生成一段新的向量 return { //返回一個單位向量 x:x/Math.sqrt(x*x+y*y), y:y/Math.sqrt(x*x+y*y) } };複製程式碼
如果對於向量不熟悉的可以去看一看向量的公式,也許你看著會有一點暈,那麼我來舉一個例子。
(1,1)
這是一個二維的向量,我可以用它來代表一個座標,也可以代表它到原點的距離,同時也可以代表從原點到該點的方向。
到目前,一個簡單的向量類就已經足夠我們去了解向量在圖形中的運用了。
擁有向量的粒子類
現在,我們就可以對之前的粒子類進行重寫
-
當粒子遇上向量
function dot(x,y,ax=0,ay=0,color="black") { //用向量來替代之前的純座標 this.site=new vector2(x,y); //初始時的目標座標向量既是當前座標向量 this.end=new vector2(x,y); //速度全部交由系統來調整 this.vx=0; this.vy=0; //加速度受外界力影響 this.ax=ax; this.ay=ay; this.color=color; //設定水平與豎直方向的值,具體作用下面有講 this.direction={ hor:0, ver:0 } this.ctx={}; }複製程式碼
在實現的時候,我們需要知道什麼是受我們直接影響的,什麼是受我們間接影響的,這樣可以可以在實現的實現的時候更具有聯動性,把更多的計算交由計算機去處理,可以大大避免一些不必要的誤操作。
-
最終點的設定
dot.prototype.setEnd = function(vec2) { this.end=vec2; this.vx=this.site.normalize(this.end).x; this.vy=this.site.normalize(this.end).y; if(vec2.x>this.site.x){this.direction.hor=1} if(vec2.x<this.site.x){this.direction.hor=-1} if(vec2.y>this.site.y){this.direction.ver=-1} if(vec2.y<this.site.y){this.direction.ver=1} };複製程式碼
這裡會發現我們的方向速度量是取的位移向量的單位向量,這裡的原因是
X=VT
,當時間一定時,速度可以視作位移上的單位距離,然後將其方向化,也就獲得了方向速度。這裡我將粒子的初始位置與目標位置做了對比,獲得了相對而言的上下或者左右,並且使用
1
或者-1
來分別代替下右跟上左,有部分人不知道這裡為什麼是下右跟上左,這是因為在螢幕上的y軸的計算是以下方問正方向的,這也為什麼我們在繪製的時候是用(100,100)
而不是(100,-100)
-
粒子的移動
dot.prototype.move = function() { this.vy+=this.ay*t; this.vx+=this.ax*t; var hor=""; var ver=""; //對取整的方式進行判斷 switch (this.direction.hor) { case 1: hor=`floor`; break; case 0: hor=`floor`; break; case -1: hor=`ceil`; default: break; } switch (this.direction.ver) { case 1: ver=`ceil`; break; case 0: ver=`floor`; break; case -1: ver=`floor`; default: break; } if(Math[hor](this.site.x)!=this.end.x||Math[ver](this.site.y)!=this.end.y){ this.site.move(new vector2(this.vx,this.vy)); } };複製程式碼
移動的基本公式上一節也有基礎的介紹,這裡直接用的是帶加速度的,
v=v0+at
(v0是初速度,v是勻變速t時間之後的速度)在位置判斷的時候我使用了向下取整的方法,是因為在做單位向量的時候會出現帶根號的數,為了便於判斷,我們就需要對位置的座標進行取整,這個時候,我們就需要根據粒子點在目標點的方位來決定是向上還是向下取整。
取整之後就可以進行判斷是否達到目標位置,如果沒有就會繼續呼叫
move
的命令來不斷更新座標位置。注意:
需要注意的是,我進行了取整,這也就意味著目標值要是整數,畫素不存在小數位的畫素,但是計算機在渲染如1.5px的時候,還是渲染出一些奇怪的東西,可以用以下的程式碼做一下測試,你就清楚那不存在的小數位畫素到底是什麼。
ctx.fillStyle=`red`; ctx.fillRect(1,2,1,1); ctx.fillRect(1,1,1.5,1); ctx.fillRect(1,3,1.2,1); ctx.fill();複製程式碼
-
後續工作
再之後,就是重複上一節的內容,將粒子實現渲染與更新,如果不清楚的可以去看我的上一篇文章。
到目前為止,我們已經實現的粒子特效中最主要的部分,我們可以利用這個粒子類來實現粒子幾乎常見運動,如 勻速運動 勻變速/非勻變速運動 圓周運動 甚至是軌跡運動
是不是感覺那些看起來飛來飛去的特效開始有邏輯可循了
不定期更新canvas與svg的相關技術教程,有實戰型,也會有主原理型的,2d 2.5d 3d都會包含到,同時涉及的有 線性代數 物理 圖形學等相關的基礎知識。
相關原始碼:vector2
歡迎各位客官收藏關注,投硬幣餵養。
歡迎大家為我提出不足與錯誤~