以前逛園子的時候看到 ChokCoco 的爆炸效果做品:【BOOM】一款有趣的Javascript動畫效果 (大神英文有沒有拼錯呀←.←),以爲蠻有意思的,效果以下:javascript
不過以爲這個爆炸效果仍是偏軟了一點,沒有爆炸那種碎片飛濺的感受,一直念念不忘想要本身作一個3D效果的爆炸動效。這兩天在搞一些小動畫,就順便也把3D爆炸作了出來,動畫效果:css
原理很簡單,就是用不少小圖片拼湊成大圖片,而後讓小圖片按照必定規律運動造成爆炸效果。這裏的爆炸效果用的是 CSS3 的 3D 變換來作的,經過 js 動態改變變換參數造成動畫。實現步驟簡單說說:html
一、圖片拼湊前端
這一步相對簡單,用的是不少 div 標籤的背景圖拼湊的,設置好每一個 div 標籤的 position 和 background-position 就能夠了。這裏要注意的一點就是添加 div 標籤是記得要用 innerHTML 一次性所有添加進去。雖然這裏沒有直接顯示圖片,可是這裏仍是 new 了一個 image,並將圖片拼湊放在 load 事件中執行。效果和代碼分別以下(實際效果沒格子線的):java
var img = new Image(); img.src = 'img/zoro.jpg'; //160*160,or you need to change wrapper's size img.onload = function () { var x = y = 0, div = styleCtn = '', imgWidth = this.width, imgHeight = this.height, pwidth = pheight = 10, nx = Math.floor(imgWidth / pwidth), //x方向粒子個數 ny = Math.floor(imgHeight / pheight), //y方向粒子個數 wrap = document.getElementById('zd-wrap'); for (var i = 0, num = nx * ny; i < num; i++) { x = (i % nx) * pwidth; y = Math.floor(i / ny) * 10; styleCtn = 'left: ' + x + 'px; top: ' + y + 'px; background-position: ' + (-x) + 'px ' + (-y) + 'px;'; div = div + '<div class="bomb" style="background-image: url(' + this.src + '); ' + styleCtn + '"></div>'; } wrap.innerHTML = div; //添加圖片 };
二、爆炸效果node
這一步相對難了許多,由於都是三維的運動,分爲平移和翻轉兩種運動會簡單一點。git
a)平移運動:在立體空間中的爆炸應該是這樣的:github
回到平面空間應該是這樣的:算法
從上圖能夠總結出小塊的運動方向,根據小塊所處的位置運動方向是不一樣的,左上角向左上角飛去,右下角向右下角飛去這樣。這裏應該有一部分小塊向屏幕飛來,還有一部分遠離屏幕以突出撲面而來的感受。編程中表現各軸運動的是 translate3d,這裏注意屏幕和圖片的座標關係,屏幕的 Y 軸就是現實中的垂直方向、向下爲正,Z軸就是面向用戶方向。運動總結起來就是:chrome
左上角:vx < 0, vy < 0, vz隨機
左下角:vx < 0, vy > 0, vz隨機
右上角:vx > 0, vy < 0, vz隨機
右下角:vx > 0, vy > 0, vz隨機
這裏 Y 方向沒有嚴格對半分,有一種總體向上的感受。加速度的話模仿須要模仿重力: vxa = 0, vya = 0.5 (模仿重力),vza能夠適當加點,能增強撲面而來的感受。
b)翻轉運動
翻轉效果,就是 X 軸的旋轉,爲了效果更加逼真須要引入 Y 軸的旋轉,能夠忽略 z 軸的旋轉。由於粒子小塊的尺寸很小,因此這裏不須要嚴格控制旋轉參數,一方面簡化模型,另外一方面減輕瀏覽器負荷。固然也沒必要引入旋轉變量了,直接用 x、y 的座標代入也能夠獲得不錯的翻轉效果: rotateX(xdeg) rotateY(ydeg),我這裏引用了 Zachstronaut 的算法:
rotateX: Math.cos(0.1 *ys) + 'rad
rotateY: Math.sin(0.1 * xs) + 'rad
最後設置終止條件,能夠根據 粒子小塊的x、y 座標判斷是否應該讓其退出動畫循環。
這樣就實現了動畫,固然還必需要開啓父對象的透視屬性,大概設置透視距離 300px 左右就能夠了。
我使用的圖片尺寸爲160*160,粒子尺寸爲10*10,在iOS中表現優秀,移動chrome中表現的也還不錯。雖然已經調用了 GPU 加速渲染,但已經到了不少國產移動瀏覽器的上限了,因此不建議再增長粒子數量。性能提高也作了,可是沒找到好的突破點,若是你有更好的點子,請聯繫我!
耗時測試:
能夠看到瓶頸在 Painting 上,再細分的話主要是圖層重組,接着看一下動畫過程:
能夠看到在最開始的時候出現了密密麻麻的綠框,也就是這裏發生了大量的 paint flashing(重繪)。再看咱們一開始拼湊背景圖的方法,用的是絕對定位+ left + top,這裏會致使大量的重繪。雖然你能夠在最開始用一個 translate3d(0, 0, 0)來限定渲染層讓綠框消失,但並不會有多大的效果,由於主要耗時的是圖層合成,並非繪製。分層是必須的,我也嘗試過使用 translate 去代替 left + top,可是效果並不理想,暫時沒有想到更好的辦法改善渲染性能……
DOM操做方面卻是能夠再改善,如今動效的代碼是:
this.nodes[i].style[this.transformProperty] = 'translate3d(' + this.xs[i] + 'px, ' + this.ys[i] + 'px, ' + this.zs[i] + 'px) rotateX(' + Math.cos(0.1 * this.ys[i]) + 'rad) rotateY(' + Math.sin(0.1 * this.xs[i]) + 'rad)';
循環中每次都會操做dom,並且在設置style上仍是用屬性查找的方式,那這裏應該是能夠改善的。一是重寫style,避免查找屬性。二就是重寫父對象div裏面的innerHTML,就像開頭設置背景圖同樣,一次更新全部粒子塊。
不過我在步進調試的時候發現,除了第一次執行時會一個一個地設置粒子塊的屬性,後面的動畫循環中都已經被瀏覽器優化成總體重寫了,每次更新都是全體更新的,因此上面的方法貌似也不能提高太多。真的沒想到其餘優化的辦法了,若是你有點子,請聯繫我!
源碼已經放到GitHub(bomb.js)上面去了,有興趣的同窗能夠fork來看看,求星星!
我已經將 js+HTML+CSS 都封裝好了,設置好容器以後直接引用bomb.js就能夠了,以下:
<style type="text/css"> .wrapper { width: 160px; margin: 100px auto 0; position: relative; cursor: pointer; perspective: 200px; } </style> <div class="wrapper" id="zd-wrap"></div> <script src="js/bomb.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var explore = new ParticlesTemplate(), exploreImg = new Image(), wrapper = document.getElementById('zd-wrap'); exploreImg.src = 'img/zoro.jpg'; exploreImg.onload = function () { explore.init(exploreImg, wrapper); wrapper.addEventListener('click', function () { explore.go(); }, false); }; </script>
就寫到這了,碼字不易,隨手點贊哈~~~
參考資料:
3) http://www.zachstronaut.com/
(圖片出處:小周)
原創文章,轉載請註明出處!本文連接:http://www.cnblogs.com/qieguo/p/5491192.html