ホームページ >ウェブフロントエンド >htmlチュートリアル >扑面而来的碎片--图片3D炸裂效果初体验 - 茄果
之前逛园子的时候看到 ChokCoco 的爆炸效果作品:【BOOM】一款有趣的Javascript动画效果 (大神英文有没有拼错呀←.←),觉得蛮有意思的,效果如下:
不过觉得这个爆炸效果还是偏软了一点,没有爆炸那种碎片飞溅的感觉,一直念念不忘想要自己做一个3D效果的爆炸动效。这两天在搞一些小动画,就顺便也把3D爆炸做了出来,动画效果:
原理很简单,就是用很多小图片拼凑成大图片,然后让小图片按照一定规律运动形成爆炸效果。这里的爆炸效果用的是 CSS3 的 3D 变换来做的,通过 js 动态改变变换参数形成动画。实现步骤简单说说:
1、图片拼凑
这一步相对简单,用的是很多 div 标签的背景图拼凑的,设置好每个 div 标签的 position 和 background-position 就可以了。这里要注意的一点就是添加 div 标签是记得要用 innerHTML 一次性全部添加进去。虽然这里没有直接显示图片,但是这里还是 new 了一个 image,并将图片拼凑放在 load 事件中执行。效果和代码分别如下(实际效果没格子线的):
<span style="color: #0000ff;">var</span> img = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Image(); img.src </span>= 'img/zoro.jpg'; <span style="color: #008000;">//</span><span style="color: #008000;">160*160,or you need to change wrapper's size</span> img.onload = <span style="color: #0000ff;">function</span><span style="color: #000000;"> () { </span><span style="color: #0000ff;">var</span> x = y = 0<span style="color: #000000;">, div </span>= styleCtn = ''<span style="color: #000000;">, imgWidth </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.width, imgHeight </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.height, pwidth </span>= pheight = 10<span style="color: #000000;">, nx </span>= Math.floor(imgWidth / pwidth), <span style="color: #008000;">//</span><span style="color: #008000;">x方向粒子个数</span> ny = Math.floor(imgHeight / pheight), <span style="color: #008000;">//</span><span style="color: #008000;">y方向粒子个数</span> wrap = document.getElementById('zd-wrap'<span style="color: #000000;">); </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">var</span> i = 0, num = nx * ny; i ) { x = (i % nx) *<span style="color: #000000;"> pwidth; y </span>= Math.floor(i / ny) * 10<span style="color: #000000;">; styleCtn </span>= 'left: ' + x + 'px; top: ' + y + 'px; background-position: ' + (-x) + 'px ' + (-y) + 'px;'<span style="color: #000000;">; div </span>= div + '<div class="bomb" style="background-image: url(' + <span style=" color:>this.src + '); ' + styleCtn + '"></div>'<span style="color: #000000;">; } wrap.innerHTML </span>= div; <span style="color: #008000;">//</span><span style="color: #008000;">添加图片</span> };
2、爆炸效果
这一步相对难了许多,因为都是三维的运动,分为平移和翻转两种运动会简单一点。
a)平移运动:在立体空间中的爆炸应该是这样的:
回到平面空间应该是这样的:
从上图可以总结出小块的运动方向,根据小块所处的位置运动方向是不同的,左上角向左上角飞去,右下角向右下角飞去这样。这里应该有一部分小块向屏幕飞来,还有一部分远离屏幕以突出扑面而来的感觉。编程中表现各轴运动的是 translate3d,这里注意屏幕和图片的坐标关系,屏幕的 Y 轴就是现实中的垂直方向、向下为正,Z轴就是面向用户方向。运动总结起来就是:
左上角:vx
左下角:vx 0, vz随机
右上角:vx > 0, vy
右下角: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) + '<span style="color: #000000;">rad rotateY: Math.sin(0.1 * xs) + </span>'rad
最后设置终止条件,可以根据 粒子小块的x、y 坐标判断是否应该让其退出动画循环。
这样就实现了动画,当然还必须要开启父对象的透视属性,大概设置透视距离 300px 左右就可以了。
我使用的图片尺寸为160*160,粒子尺寸为10*10,在iOS中表现优秀,移动chrome中表现的也还不错。虽然已经调用了 GPU 加速渲染,但已经到了很多国产移动浏览器的上限了,所以不建议再增加粒子数量。性能提升也做了,但是没找到好的突破点,如果你有更好的点子,请联系我!
耗时测试:
可以看到瓶颈在 Painting 上,再细分的话主要是图层重组,接着看一下动画过程:
可以看到在最开始的时候出现了密密麻麻的绿框,也就是这里发生了大量的 paint flashing(重绘)。再看我们一开始拼凑背景图的方法,用的是绝对定位+ left + top,这里会导致大量的重绘。虽然你可以在最开始用一个 translate3d(0, 0, 0)来限定渲染层让绿框消失,但并不会有多大的效果,因为主要耗时的是图层合成,并不是绘制。分层是必须的,我也尝试过使用 translate 去代替 left + top,但是效果并不理想,暂时没有想到更好的办法改善渲染性能……
DOM操作方面倒是可以再改善,现在动效的代码是:
<span style="color: #0000ff;">this</span>.nodes[i].style[<span style="color: #0000ff;">this</span>.transformProperty] = 'translate3d(' + <span style="color: #0000ff;">this</span>.xs[i] + 'px, ' + <span style="color: #0000ff;">this</span>.ys[i] + 'px, ' + <span style="color: #0000ff;">this</span>.zs[i] + 'px) rotateX(' + Math.cos(0.1 * <span style="color: #0000ff;">this</span>.ys[i]) + 'rad) rotateY(' + Math.sin(0.1 * <span style="color: #0000ff;">this</span>.xs[i]) + 'rad)';
循环中每次都会操作dom,而且在设置style上还是用属性查找的方式,那这里应该是可以改善的。一是重写style,避免查找属性。二就是重写父对象div里面的innerHTML,就像开头设置背景图一样,一次更新所有粒子块。
不过我在步进调试的时候发现,除了第一次执行时会一个一个地设置粒子块的属性,后面的动画循环中都已经被浏览器优化成整体重写了,每次更新都是全体更新的,所以上面的方法貌似也不能提升太多。真的没想到其他优化的办法了,如果你有点子,请联系我!
源码已经放到GitHub(bomb.js)上面去了,有兴趣的同学可以fork来看看,求星星!
我已经将 js+HTML+CSS 都封装好了,设置好容器之后直接引用bomb.js就可以了,如下:
<span style="color: #0000ff;"><span style="color: #800000;">style </span><span style="color: #ff0000;">type</span><span style="color: #0000ff;">="text/css"</span><span style="color: #0000ff;">></span><span style="background-color: #f5f5f5; color: #800000;"> .wrapper </span><span style="background-color: #f5f5f5; color: #000000;">{</span><span style="background-color: #f5f5f5; color: #ff0000;"> width</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> 160px</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;"> margin</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> 100px auto 0</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;"> position</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> relative</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;"> cursor</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> pointer</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;"> perspective</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> 200px</span><span style="background-color: #f5f5f5; color: #000000;">;</span> <span style="background-color: #f5f5f5; color: #000000;">}</span> <span style="color: #0000ff;"></span><span style="color: #800000;">style</span><span style="color: #0000ff;">></span> <span style="color: #0000ff;"><span style="color: #800000;">div </span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="wrapper"</span><span style="color: #ff0000;"> id</span><span style="color: #0000ff;">="zd-wrap"</span><span style="color: #0000ff;">></span><span style="color: #800000;">div</span><span style="color: #0000ff;">></span> <span style="color: #0000ff;"><span style="color: #800000;">script </span><span style="color: #ff0000;">src</span><span style="color: #0000ff;">="js/bomb.js"</span><span style="color: #ff0000;"> type</span><span style="color: #0000ff;">="text/javascript"</span><span style="color: #ff0000;"> charset</span><span style="color: #0000ff;">="utf-8"</span><span style="color: #0000ff;">></span><span style="color: #800000;">script</span><span style="color: #0000ff;">></span> <span style="color: #0000ff;"><span style="color: #800000;">script </span><span style="color: #ff0000;">type</span><span style="color: #0000ff;">="text/javascript"</span><span style="color: #0000ff;">></span> <span style="background-color: #f5f5f5; color: #0000ff;">var</span><span style="background-color: #f5f5f5; color: #000000;"> explore </span><span style="background-color: #f5f5f5; color: #000000;">=</span> <span style="background-color: #f5f5f5; color: #0000ff;">new</span><span style="background-color: #f5f5f5; color: #000000;"> ParticlesTemplate(), exploreImg </span><span style="background-color: #f5f5f5; color: #000000;">=</span> <span style="background-color: #f5f5f5; color: #0000ff;">new</span><span style="background-color: #f5f5f5; color: #000000;"> Image(), wrapper </span><span style="background-color: #f5f5f5; color: #000000;">=</span><span style="background-color: #f5f5f5; color: #000000;"> document.getElementById(</span><span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">zd-wrap</span><span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">); exploreImg.src </span><span style="background-color: #f5f5f5; color: #000000;">=</span> <span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">img/zoro.jpg</span><span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">; exploreImg.onload </span><span style="background-color: #f5f5f5; color: #000000;">=</span> <span style="background-color: #f5f5f5; color: #0000ff;">function</span><span style="background-color: #f5f5f5; color: #000000;"> () { explore.init(exploreImg, wrapper); wrapper.addEventListener(</span><span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">click</span><span style="background-color: #f5f5f5; color: #000000;">'</span><span style="background-color: #f5f5f5; color: #000000;">, </span><span style="background-color: #f5f5f5; color: #0000ff;">function</span><span style="background-color: #f5f5f5; color: #000000;"> () { explore.go(); }, </span><span style="background-color: #f5f5f5; color: #0000ff;">false</span><span style="background-color: #f5f5f5; color: #000000;">); }; </span><span style="color: #0000ff;"></span><span style="color: #800000;">script</span><span style="color: #0000ff;">></span></span></span></span></span>
就写到这了,码字不易,随手点赞哈~~~
参考资料:
3) http://www.zachstronaut.com/
(图片出处:小周)
原创文章,转载请注明出处!本文链接:http://www.cnblogs.com/qieguo/p/5491192.html