效果圖
- 升級(jí)版demo 2:改變粒子的形狀
- 升級(jí)版demo 1:改變粒子大小,顏色
原版粒子動(dòng)畫(huà)的JS部分也就一百多行代碼吧,看明白不難。盡管如此,在實(shí)現(xiàn)想要的效果,尤其是demo 2的過(guò)程中,仍遇到了不少問(wèn)題,甚至一度以為無(wú)法實(shí)現(xiàn)。好在最終都找到了解決辦法,完美實(shí)現(xiàn)了需求。
這篇文檔將介紹實(shí)現(xiàn)這兩個(gè)demo時(shí)需要注意的一些關(guān)鍵點(diǎn),遇到的一些問(wèn)題和解決辦法。
Three.js版本:r90
升級(jí)版demo 1
創(chuàng)建粒子的思路:定義一種 Material,然后作為參數(shù)傳遞到粒子構(gòu)造函數(shù)中。
new THREE.Sprite( material )
改變粒子顏色/大小
Material 是什么呢?它描述物體的外觀。
因此,要改變粒子顏色或者大小,從Material著手。在Three.js 版本更新過(guò)程中,Material 種類也發(fā)生了一些變化。舊版本支持的 Material 在新版本中不一定可以使用,這一點(diǎn)在文檔后面部分詳細(xì)說(shuō)。
粒子波浪示例使用了SpriteCanvasMaterial
來(lái)創(chuàng)建粒子材質(zhì),其 API定義 如下:
Create a material that can draw custom sprites using a 2d canvas.
構(gòu)造函數(shù)可接受兩個(gè)參數(shù)。如何修改粒子顏色大小一目了然。
var material = new THREE.SpriteCanvasMaterial( {
color: 0xffffff, //粒子的顏色
program: function ( context ) { //用于繪制粒子的方法
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true ); //畫(huà)一個(gè)圓形。此處可修改大小。
context.fill();
}
} );
只改變部分粒子
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
particle = particles[ i ++ ] = new THREE.Sprite( material ); //修改此處的material參數(shù)
particle.position.x = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 );
particle.position.z = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 );
scene.add( particle );
}
}
升級(jí)版demo 2
改變粒子形狀
根據(jù)上一個(gè)demo,我們可以大膽設(shè)想,要改變粒子形狀,只需要修改THREE.SpriteCanvasMaterial
方法中的program
參數(shù)。沒(méi)錯(cuò),思路正確。
比如我們繪制正方形粒子:
var material = new THREE.SpriteCanvasMaterial ({
color: "orange", //粒子的顏色
program: function(context) { //用于繪制粒子的方法
context.fillRect(0, 0, 2, 2); //繪制一個(gè)正方形
}
});
效果如下:
然而,當(dāng)我們挪動(dòng)鼠標(biāo)的時(shí)候,相機(jī)角度變化,就會(huì)發(fā)現(xiàn)一個(gè)很?chē)?yán)重的問(wèn)題:
畫(huà)布上會(huì)出現(xiàn)殘影。
非常慘烈。
這個(gè)問(wèn)題卡了我好久,最初以為是ThreeJS的問(wèn)題,后來(lái)才發(fā)現(xiàn)其實(shí)是我使用不當(dāng)。參考這個(gè)問(wèn)題 three.js canvas wont clear - particle/sprite trails 以及 issue#4418 。尤其是這兩段話:
也就是說(shuō),粒子必須在1x1的區(qū)域內(nèi)繪制。超出這個(gè)區(qū)域就會(huì)出現(xiàn)上面的殘影問(wèn)題,因?yàn)槌霾糠譄o(wú)法被及時(shí)清除。比如上面的橙色正方形,設(shè)置的邊長(zhǎng)是 2px,就出問(wèn)題了。
那如果要繪制大一些的粒子呢?思路是,program
方法中在1 x 1 的區(qū)域內(nèi)繪制粒子,之后再把粒子放大(scale),也就是修改particle.scale.x
, particle.scale.y
, particle.scale.z
這三個(gè)變量。
demo 2中對(duì)應(yīng)的代碼:
-
繪制粒子形狀:
var material2 = new THREE.SpriteCanvasMaterial ({ color: '#F28321', //粒子的顏色 program: function(context) { //用于繪制粒子的方法 context.beginPath(); //繪制漸變色的矩形 var lGrd = context.createLinearGradient(-0.008,0.25,0.016,-0.25); lGrd.addColorStop(0, '#F28321'); lGrd.addColorStop(1, 'transparent'); context.fillStyle = lGrd; context.fillRect(-0.008,0.25,0.016,-0.25); //注意此處的坐標(biāo)大小 //繪制底部和頂部圓圈 context.fillStyle = "#F28321"; context.arc(0, 0, 0.008, 0, PI2, true); //繪制底部圓圈 context.arc(0, 0.25, 0.008, 0, PI2, true); //繪制頂部圓圈 context.fill(); context.closePath(); //繪制頂部漸變色光圈 var rGrd = context.createRadialGradient(0, 0.25, 0, 0, 0.25, 0.025); rGrd.addColorStop(0, 'transparent'); rGrd.addColorStop(1, 'rgba(242,131,33,0.28)'); context.fillStyle = rGrd; context.arc(0, 0.25, 0.025, 0, PI2, true); //繪制一個(gè)圓圈 context.fill(); } });
?
-
放大粒子:
//更新粒子的位置和大小 for (var ix = 0; ix < AMOUNTX; ix++) { for (var iy = 0; iy < AMOUNTY; iy++) { particle = particles[i++]; //更新粒子位置 particle.position.y = (Math.sin((ix + count) * 0.3) * 50) + (Math.sin((iy + count) * 0.5) * 50); //更新粒子大小 particle.scale.x = particle.scale.y = particle.scale.z = ( (Math.sin((ix + count) * 0.3) + 1) * 4 + (Math.sin((iy + count) * 0.5) + 1) * 4 )*100; //正常情況下再放大100倍 } }
?
ThreeJS版本
在這個(gè)demo中,ThreeJS的版本很重要。一開(kāi)始我使用的不是官網(wǎng)的例子(http://www.css88.com/archives/5996/comment-page-1),其中ThreeJS 版本是 r56。
然而代碼中用到的 ParticleCanvasMaterial
和 Particle
在ThreeJS r62及更新版本中已經(jīng)被移除了。會(huì)出現(xiàn)以下問(wèn)題:
Uncaught TypeError: THREE.ParticleCanvasMaterial is not a constructor
THREE.Particle has been renamed to THREE.Sprite.
因此要留心ThreeJS的版本,以及其升級(jí)指南 。
在React中使用ThreeJS
正常情況下,可以按照官方文檔中介紹的如何導(dǎo)入ThreeJS模塊來(lái)操作:https://threejs.org/docs/index.html#manual/introduction/Import-via-modules
但是,在這個(gè)粒子波浪實(shí)例中,要用到 js/renderers/Projector.js
和 js/renderers/CanvasRenderer.js
。先導(dǎo)入ThreeJS模塊,再使用 require
加載前兩個(gè)文件。仍然行不通。
最后的解決辦法是,在HTML中引用 ThreeJS 的 .min.js
文件。然后在對(duì)應(yīng)組件中使用 require
加載那兩個(gè)js文件。
或許有更好的辦法,還需要調(diào)研。參考話題:
- Can't use CanvasRenderer or Projector when I'm using Three from npm : https://github.com/mrdoob/three.js/issues/10617
- transform
examples/js
to support modules : https://github.com/mrdoob/three.js/issues/9562