還是老樣子,先啰嗦一點前言。 最近各種事務纏身,所以也就隔了比較長的時間才開始碼這篇文。希望不會這麼快就過氣。 好了,接下來就開始碼代碼。(寫到中途,突然感覺到的。本篇設計大量初中物理知識,請懷念的往下看) 這次不像之前,是已經寫好的文件,拿出來解析一波,就糊弄出來了一篇隨筆。總之,我就一邊的編代碼 ...
還是老樣子,先啰嗦一點前言。
最近各種事務纏身,所以也就隔了比較長的時間才開始碼這篇文。希望不會這麼快就過氣。
好了,接下來就開始碼代碼。(寫到中途,突然感覺到的。本篇設計大量初中物理知識,請懷念的往下看)
這次不像之前,是已經寫好的文件,拿出來解析一波,就糊弄出來了一篇隨筆。總之,我就一邊的編代碼,一邊寫文章。時不時的截圖一個效果出來,不知道這樣,能不能糊弄過去。。。。
這次,目標是準備實現很多canvas,素材網站上比較常見的煙花效果。只看過幾次效果,也許實現的最終效果有些差異。但是,希望大家最後能以自己的能力改成自己更期待的結果,而不是只能跟著我做出一樣的東西,沒有任何的衍生。那意義就會缺少很多了。
既然是canvas,這一段還是不能省的。(註:往後的文章,可能會省略這段代碼。主要記住ctx , width , height 這3個變數各自代表是什麼就好了。之後我就直接使用了。所以希望大家多多包涵。)

var drawing = document.getElementById('drawing') var ctx = drawing.getContext('2d') var width = drawing.width var height = drawing.heightView Code
- 對煙花的分析:
大家心裡都對,煙花有一定的印象。煙花有一個升空的過程,然後在空中爆炸散開成很多束。然後漸漸消散。
總之,就是有2種狀態了。用一個 Boolean 值來判斷就好了。
除此之外,還有煙花的位置,x, y軸上的速度,以及煙花的各種顏色。當然,還有其他各種屬性,一時之間也想不到那麼完美,不如我們走一步看一步,如何呢?
function Fireworks (x, y, xSpeed, ySpeed, color) {
this.x = x
this.y = y
this.xSpeed = xSpeed
this.ySpeed = ySpeed
this.color = color
this.boom = false
}其實,寫到這裡挺猶豫的。因為,不知道各位看官的知識面有多廣,是否應該用 class ,最終,考量了一下,使用es6的,肯定知道構造函數,但是,反過來就不一定了。所以,也就簡單做一個優雅降級吧。
- 上天吧!
嘛。。。此情此景上天的當然是煙花咯。
繼承的寫法多種多樣,希望大家不要過多的考究。萬分感謝。
Fireworks.prototype.draw = function(){ ctx.clearRect(0, 0, width, height) ctx.beginPath() ctx.arc(this.x, this.y, 10 , 0 , 2 *Math.PI) ctx.fill() } Fireworks.prototype.toSky = function(){ this.x += this.xSpeed this.y += this.ySpeed this.draw() }
為了更加的真實,我們還需要模擬重力加速度。 var g = 9.8 當然,這裡只是假設。並不是我們必須設定的和實際的重力加速度一樣。只是方便理解,而我百分百的確定,我之後一定會改變這個值的。
Fireworks.prototype.toSky = function(){ this.x += this.xSpeed this.y += this.ySpeed this.ySpeed -= g this.draw() }
此時。比較細心的朋友,以及正跟著一步步敲代碼的朋友,應該都會發現。這段代碼裡面有一些問題。我當然是故意的啦。不然,我就直接去上面改成沒問題的代碼,然後減掉這一段了!
問題的bug,就是我們速度的正負值,以及canvas的y軸方向和我們正常習慣的y軸方向。有一些比較厲害的朋友,自己寫方法將canvas的軸,修改成,我們通常使用的。
但是,本篇還是使用的原生的,y軸正方向向下的情況。於是。。我們的煙花要升天。就要使用一個負值的速度。重力加速度,也需要相應的改動。我就不在這裡做改動,混字數了,大家可以小修正一下y軸相關值正負,然後創建一個 Fireworks 實例加一個定時器來調用 toSky。就可以看到一小效果了,但是,這樣就夠了嗎?其實,我接下來有更多需要豐滿的地方。 - 現在該做什麼呢?
開頭說的那麼故弄玄虛,到現在不是就值做出了一個走拋物線的小球嗎?而且還是寫死的半徑為10的黑球。就連之前說好的 color 都沒有加上去。教練,這和說好的不一樣啊!!
所以,本來這一段效果優化的代碼,準備放在總體邏輯完成之後再寫下來的。但是現在的體驗極差。就先豐滿這一部分的視覺效果吧。
首先,做一個簡單操作 var r = Math.random.bind(Math) 很多人看到random。 就感覺有大事要發生。沒錯,就是這樣。讓我們對最初的構造函數,先做出小改動吧。
function Fireworks () { var x = width * (r() * .8 + .1) this.x = x this.y = height this.xSpeed = x > width / 2 ? -(20 * r() - 10) : (20 * r() - 10) this.ySpeed = 20 * r() + 10 this.color = 'rgb('+ f(r() * 256) +',' + f(r() * 256) + ','+ f(r() * 256) +' )' this.radius = 5 this.dotNum = 20 this.dots = [] }
大致的改動,大家都是可以自己看出來的。將起始點,定在canvas的最底部,起始點最少距離左右邊緣都會有十分之一的寬度,根據起始點的位置,來決定,x軸的方向。將速度,顏色。都改成了隨機數。將圓的半徑也定義了進來。
這些數字類型的值,大多都是可以自己按需修改,或者定義常量來使用。就不要過於糾結了。當然,這些數值都還不完善。只能最後我們添加了定時器,實際運行的時候,才能確定比較合理的數值。
既然說到了定時器,不如就多說幾句吧。一般我們動畫上使用的定時器,每幀的間隔都會使用15ms。至於為什麼就不說了。隨便查一下就能找到,而且說太多了也偏離了本篇的主題了。想多說的就是做動畫效果的時候的另外一個API, requestAnimationFrame 這裡也不會介紹太多關於這個API,因為會偏題呀。只是之後會使用,所以,還不太瞭解的同學,可以先去瞭解一下這個API。
到此,似乎,我們實際的效果並沒有什麼改變。
那麼接下來,就讓我來使用新增的2個屬性吧。Fireworks.prototype.createDotArr = function(){ var length = this.dots.length if (length < this.dotNum) this.dots.push( { x: this.x, y: this.y }) } else { for (var i = 0; i < length - 1; i++) { this.dots[i] = this.dots[i + 1] } this.dots[length - 1] = { x: this.x, y: this.y } } }
上面這個方法,將我們之前的那個拋物線的圓,經過的點記錄下來。因為只是一段較短的拋物線,所以我們暫時只記錄20個點。也就是 dotNum 這個屬性控制的點數。
接下來就是將每個點都進行繪製,所以改寫了我們之前的方法。於是就成了這樣。Fireworks.prototype.draw = function(){ var self = this ctx.clearRect(0, 0, width, height) self.dots.forEach(function(v){ ctx.beginPath() ctx.arc(v.x, v.y, self.radius , 0 , 2 * Math.PI) ctx.fill() }) } Fireworks.prototype.toSky = function(){ this.x += this.xSpeed this.y -= this.ySpeed this.ySpeed -= g this.createDotArr() this.draw() }
接下來,讓我們來進行一下測試。看看現在效果如何了。
var test = new Fireworks() function animate () { requestAnimationFrame(function () { test.toSky() animate() }) } animate()
並不是最終的調用形式。 只是做一個測試的效果。所以,之後這段是不包含在邏輯代碼里的。另外,大家可以在做這個測試的時候,去改變構造函數內的常量參數,將它變成你喜歡的樣子。我就不多廢話了。
其實,現在看起來好像也還不是很美觀,顏色也並沒有加上去。那就讓我來加上顏色吧。當然並不是你們所想的 fillStyle = this.color 。因為這樣看起來也並不好看啊。Fireworks.prototype.draw = function(){ var self = this ctx.clearRect(0, 0, width, height) self.dots.forEach(function(v, i){ var gradients = ctx.createRadialGradient(v.x, v.y, 1, v.x, v.y, i / self.dots.length * self.radius + 1) gradients.addColorStop(0, "rgba(" + self.R + "," + self.G + "," + self.B + "," + i / self.dotNum + ")") gradients.addColorStop(1, "rgba(" + self.R + "," + self.G + "," + self.B + ", 0 )") ctx.fillStyle = gradients ctx.beginPath() ctx.arc(v.x, v.y, self.radius , 0 , 2 * Math.PI) ctx.fill() }) }
忘記補充的一點,R,G,B屬性。是我在構造函數中,將color屬性,拆分成了,這3個屬性。大家可以自己實現,我就不貼代碼了。
這裡採用的漸變色,內部為最深的顏色,但是在點的索引逐漸變大,也就是這個點存在的時間變長的時候,逐漸變的透明,向外部延伸直至完全透明。要保證漸變向外延伸,所以在 createRadialGradient 的第六個參數的最後 + 1,以此確保這個值是大於1的。
小結:
至此,就是一個可以上天,但是還不能爆炸的啞炮煙花了。代碼敲到這裡,主要實現煙花的各種邏輯已經全部都有講到了,之後的內容沒有什麼新的知識點,大家可以自己嘗試完成接下來的內容。由於時間不足,就寫到這裡,不久之後,我會回來填坑的。
大家就不要打我了。