以下是 js跟随鼠标移动旋转心形特效代码 的示例演示效果:
部分效果截图:
HTML代码(index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<title>js跟随鼠标移动旋转心形特效</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/nb.js"></script>
<script src="js/Projector.js"></script>
<script src="js/CanvasRenderer.js"></script>
<script src="js/tween.min.js"></script>
<script src="js/Sparks.js"></script>
<!-- load the font file from canvas-text -->
<script src="js/helvetiker_regular.typeface.js"></script>
<script>
var container;
var camera, scene, renderer;
var group, text, plane;
var targetRotation = 0;
var targetRotationOnMouseDown = 0;
var mouseX = 0;
var mouseXOnMouseDown = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var heartShape, particleCloud, sparksEmitter, emitterPos;
var _rotation = 0;
var timeOnShapePath = 0;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
//相机
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set( 0, 150, 800 );
//场景
scene = new THREE.Scene();
group = new THREE.Group();
scene.add( group );
// Get text from hash
var string = "LOVE U";
var hash = document.location.hash.substr( 1 );
if ( hash.length !== 0 ) {
string = hash;
}
var text3d = new THREE.TextGeometry( string, {
size: 80,
height: 20,
curveSegments: 2,
font: "helvetiker"
});
text3d.computeBoundingBox();
var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x );
var textMaterial = new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, overdraw: 0.5 } );
text = new THREE.Mesh( text3d, textMaterial );
// Potentially, we can extract the vertices or faces of the text to generate particles too.
// Geo > Vertices > Position
text.position.x = centerOffset;
text.position.y = 100;
text.position.z = 0;
text.rotation.x = 0;
text.rotation.y = Math.PI * 2;
group.add( text );
particleCloud = new THREE.Object3D(); // Just a group
particleCloud.y = 800;
group.add( particleCloud );
// Create Particle Systems
// Heart
var x = 0, y = 0;
heartShape = new THREE.Shape();
heartShape.moveTo( x + 25, y + 25 );
heartShape.bezierCurveTo( x + 25, y + 25, x + 20, y, x, y );
heartShape.bezierCurveTo( x - 30, y, x - 30, y + 35,x - 30,y + 35 );
heartShape.bezierCurveTo( x - 30, y + 55, x - 10, y + 77, x + 25, y + 95 );
heartShape.bezierCurveTo( x + 60, y + 77, x + 80, y + 55, x + 80, y + 35 );
heartShape.bezierCurveTo( x + 80, y + 35, x + 80, y, x + 50, y );
heartShape.bezierCurveTo( x + 35, y, x + 25, y + 25, x + 25, y + 25 );
var hue = 0;
var hearts = function ( context ) {
context.globalAlpha = 0.5;
var x = 0, y = 0;
context.scale(0.05, -0.05); // Scale so canvas render can redraw within bounds
context.beginPath();
// From http://blog.burlock.org/html5/130-paths
context.bezierCurveTo( x + 2.5, y + 2.5, x + 2.0, y, x, y );
context.bezierCurveTo( x - 3.0, y, x - 3.0, y + 3.5,x - 3.0,y + 3.5 );
context.bezierCurveTo( x - 3.0, y + 5.5, x - 1.0, y + 7.7, x + 2.5, y + 9.5 );
context.bezierCurveTo( x + 6.0, y + 7.7, x + 8.0, y + 5.5, x + 8.0, y + 3.5 );
context.bezierCurveTo( x + 8.0, y + 3.5, x + 8.0, y, x + 5.0, y );
context.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );
context.fill();
context.lineWidth = 0.5; //0.05
context.stroke();
}
var setTargetParticle = function() {
var material = new THREE.SpriteCanvasMaterial( {
program: hearts
} );
material.color.setHSL(hue, 1, 0.75);
hue += 0.001;
if (hue>1) hue-=1;
particle = new THREE.Sprite( material );
particle.scale.x = particle.scale.y = Math.random() * 40 + 40;
particleCloud.add( particle );
return particle;
};
var onParticleCreated = function( p ) {
p.target.position.copy( p.position );
};
var onParticleDead = function( particle ) {
particle.target.visible = false;
particleCloud.remove( particle.target );
};
sparksEmitter = new SPARKS.Emitter(new SPARKS.SteadyCounter(160));
emitterpos = new THREE.Vector3();
sparksEmitter.addInitializer(new SPARKS.Position( new SPARKS.PointZone( emitterpos ) ) );
sparksEmitter.addInitializer(new SPARKS.Lifetime(0,2));
sparksEmitter.addInitializer(new SPARKS.Target(null, setTargetParticle));
sparksEmitter.addInitializer(new SPARKS.Velocity(new SPARKS.PointZone(new THREE.Vector3(0,-50,10))));
// TOTRY Set velocity to move away from centroid
sparksEmitter.addAction(new SPARKS.Age());
//sparksEmitter.addAction(new SPARKS.Accelerate(0.2));
sparksEmitter.addAction(new SPARKS.Move());
sparksEmitter.addAction(new SPARKS.RandomDrift(50,50,2000));
sparksEmitter.addCallback("created", onParticleCreated);
sparksEmitter.addCallback("dead", onParticleDead);
sparksEmitter.addCallback("updated", function( particle ) {
particle.target.position.copy( particle.position );
});
sparksEmitter.start();
// End Particles
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
function onDocumentMouseDown( event ) {
event.preventDefault();
mouseXOnMouseDown = event.clientX - windowHalfX;
targetRotationOnMouseDown = targetRotation;
if ( sparksEmitter.isRunning() ) {
sparksEmitter.stop();
} else {
sparksEmitter.start();
}
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;
}
function onDocumentTouchStart( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
targetRotationOnMouseDown = targetRotation;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
}
}
//
function animate() {//更新场景
requestAnimationFrame( animate );
render();
}
function render() {
timeOnShapePath += 0.0337;
if (timeOnShapePath > 1) timeOnShapePath -= 1;
// TODO Create a PointOnShape Action/Zone in the particle engine
var pointOnShape = heartShape.getPointAt( timeOnShapePath );
emitterpos.x = pointOnShape.x * 5 - 100;
emitterpos.y = -pointOnShape.y * 5 + 400;
// Pretty cool effect if you enable this
// particleCloud.rotation.y += 0.05;
group.rotation.y += ( targetRotation - group.rotation.y ) * 0.05;
renderer.render( scene, camera );
}
</script>
</body>
</html>
JS代码(CanvasRenderer.js):
THREE.SpriteCanvasMaterial = function ( parameters ){
THREE.Material.call( this );
this.type = 'SpriteCanvasMaterial';
this.color = new THREE.Color( 0xffffff );
this.program = function ( context,color ){
}
;
this.setValues( parameters );
}
;
THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial;
THREE.SpriteCanvasMaterial.prototype.clone = function (){
var material = new THREE.SpriteCanvasMaterial();
THREE.Material.prototype.clone.call( this,material );
material.color.copy( this.color );
material.program = this.program;
return material;
}
;
//THREE.CanvasRenderer = function ( parameters ){
console.log( 'THREE.CanvasRenderer',THREE.REVISION );
var smoothstep = THREE.Math.smoothstep;
parameters = parameters ||{
}
;
var _this = this,_renderData,_elements,_lights,_projector = new THREE.Projector(),_canvas = parameters.canvas !== undefined ? parameters.canvas:document.createElement( 'canvas' ),_canvasWidth = _canvas.width,_canvasHeight = _canvas.height,_canvasWidthHalf = Math.floor( _canvasWidth / 2 ),_canvasHeightHalf = Math.floor( _canvasHeight / 2 ),_viewportX = 0,_viewportY = 0,_viewportWidth = _canvasWidth,_viewportHeight = _canvasHeight,pixelRatio = 1,_context = _canvas.getContext( '2d',{
alpha:parameters.alpha === true}
),_clearColor = new THREE.Color( 0x000000 ),_clearAlpha = parameters.alpha === true ? 0:1,_contextGlobalAlpha = 1,_contextGlobalCompositeOperation = 0,_contextStrokeStyle = null,_contextFillStyle = null,_contextLineWidth = null,_contextLineCap = null,_contextLineJoin = null,_contextLineDash = [],_camera,_v1,_v2,_v3,_v4,_v5 = new THREE.RenderableVertex(),_v6 = new THREE.RenderableVertex(),_v1x,_v1y,_v2x,_v2y,_v3x,_v3y,_v4x,_v4y,_v5x,_v5y,_v6x,_v6y,_color = new THREE.Color(),_color1 = new THREE.Color(),_color2 = new THREE.Color(),_color3 = new THREE.Color(),_color4 = new THREE.Color(),_diffuseColor = new THREE.Color(),_emissiveColor = new THREE.Color(),_lightColor = new THREE.Color(),_patterns ={
}
,_image,_uvs,_uv1x,_uv1y,_uv2x,_uv2y,_uv3x,_uv3y,_clipBox = new THREE.Box2(),_clearBox = new THREE.Box2(),_elemBox = new THREE.Box2(),_ambientLight = new THREE.Color(),_directionalLights = new THREE.Color(),_pointLights = new THREE.Color(),_vector3 = new THREE.Vector3(),// Needed for PointLight_centroid = new THREE.Vector3(),_normal = new THREE.Vector3(),_normalViewMatrix = new THREE.Matrix3();
// dash+gap fallbacks for Firefox and everything elseif ( _context.setLineDash === undefined ){
_context.setLineDash = function (){
}
}
this.domElement = _canvas;
this.autoClear = true;
this.sortObjects = true;
this.sortElements = true;
this.info ={
render:{
vertices:0,faces:0}
}
// WebGLRenderer compatibilitythis.supportsVertexTextures = function (){
}
;
this.setFaceCulling = function (){
}
;
//this.getPixelRatio = function (){
return pixelRatio;
}
;
this.setPixelRatio = function ( value ){
pixelRatio = value;
}
;
this.setSize = function ( width,height,updateStyle ){
_canvasWidth = width * pixelRatio;
_canvasHeight = height * pixelRatio;
_canvas.width = _canvasWidth;
_canvas.height = _canvasHeight;
_canvasWidthHalf = Math.floor( _canvasWidth / 2 );
_canvasHeightHalf = Math.floor( _canvasHeight / 2 );
if ( updateStyle !== false ){
_canvas.style.width = width + 'px';
_canvas.style.height = height + 'px';
}
_clipBox.min.set( -_canvasWidthHalf,-_canvasHeightHalf ),_clipBox.max.set( _canvasWidthHalf,_canvasHeightHalf );
_clearBox.min.set( - _canvasWidthHalf,- _canvasHeightHalf );
_clearBox.max.set( _canvasWidthHalf,_canvasHeightHalf );
_contextGlobalAlpha = 1;
_contextGlobalCompositeOperation = 0;
_contextStrokeStyle = null;
_contextFillStyle = null;
_contextLineWidth = null;
_contextLineCap = null;
_contextLineJoin = null;
this.setViewport( 0,0,width,height );
}
;
this.setViewport = function ( x,y,width,height ){
_viewportX = x * pixelRatio;
_viewportY = y * pixelRatio;
_viewportWidth = width * pixelRatio;
_viewportHeight = height * pixelRatio;
}
;
this.setScissor = function (){
}
;
this.enableScissorTest = function (){
}
;
this.setClearColor = function ( color,alpha ){
_clearColor.set( color );
_clearAlpha = alpha !== undefined ? alpha:1;
_clearBox.min.set( - _canvasWidthHalf,- _canvasHeightHalf );
_clearBox.max.set( _canvasWidthHalf,_canvasHeightHalf );
}
;
this.setClearColorHex = function ( hex,alpha ){
console.warn( 'THREE.CanvasRenderer:.setClearColorHex() is being removed. Use .setClearColor() instead.' );
this.setClearColor( hex,alpha );
}
;
this.getClearColor = function (){
return _clearColor;
}
;
this.getClearAlpha = function (){
return _clearAlpha;
}
;
this.getMaxAnisotropy = function (){
return 0;
}
;
this.clear = function (){
if ( _clearBox.empty() === false ){
_clearBox.intersect( _clipBox );
_clearBox.expandByScalar( 2 );
_clearBox.min.x = _clearBox.min.x + _canvasWidthHalf;
_clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf;
// higher y value !_clearBox.max.x = _clearBox.max.x + _canvasWidthHalf;
_clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf;
// lower y value !if ( _clearAlpha < 1 ){
_context.clearRect(_clearBox.min.x | 0,_clearBox.max.y | 0,( _clearBox.max.x - _clearBox.min.x ) | 0,( _clearBox.min.y - _clearBox.max.y ) | 0);
}
if ( _clearAlpha > 0 ){
setBlending( THREE.NormalBlending );
setOpacity( 1 );
setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' );
_context.fillRect(_clearBox.min.x | 0,_clearBox.max.y | 0,( _clearBox.max.x - _clearBox.min.x ) | 0,( _clearBox.min.y - _clearBox.max.y ) | 0);
}
_clearBox.makeEmpty();
}
}
;
// compatibilitythis.clearColor = function (){
}
;
this.clearDepth = function (){
}
;
this.clearStencil = function (){
}
;
this.render = function ( scene,camera ){
if ( camera instanceof THREE.Camera === false ){
console.error( 'THREE.CanvasRenderer.render:camera is not an instance of THREE.Camera.' );
return;
}
if ( this.autoClear === true ) this.clear();
_this.info.render.vertices = 0;
_this.info.render.faces = 0;
_context.setTransform( _viewportWidth / _canvasWidth,0,0,- _viewportHeight / _canvasHeight,_viewportX,_canvasHeight - _viewportY );
_context.translate( _canvasWidthHalf,_canvasHeightHalf );
_renderData = _projector.projectScene( scene,camera,this.sortObjects,this.sortElements );
_elements = _renderData.elements;
_lights = _renderData.lights;
_camera = camera;
_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse );
/* DEBUGsetFillStyle( 'rgba( 0,255,255,0.5 )' );
_context.fillRect( _clipBox.min.x,_clipBox.min.y,_clipBox.max.x - _clipBox.min.x,_clipBox.max.y - _clipBox.min.y );
*/
calculateLights();
for ( var e = 0,el = _elements.length;
e < el;
e ++ ){
var element = _elements[ e ];
var material = element.material;
if ( material === undefined || material.opacity === 0 ) continue;
_elemBox.makeEmpty();
if ( element instanceof THREE.RenderableSprite ){
_v1 = element;
_v1.x *= _canvasWidthHalf;
_v1.y *= _canvasHeightHalf;
renderSprite( _v1,element,material );
}
else if ( element instanceof THREE.RenderableLine ){
_v1 = element.v1;
_v2 = element.v2;
_v1.positionScreen.x *= _canvasWidthHalf;
_v1.positionScreen.y *= _canvasHeightHalf;
_v2.positionScreen.x *= _canvasWidthHalf;
_v2.positionScreen.y *= _canvasHeightHalf;
_elemBox.setFromPoints( [_v1.positionScreen,_v2.positionScreen] );
if ( _clipBox.isIntersectionBox( _elemBox ) === true ){
renderLine( _v1,_v2,element,material );
}
}
else if ( element instanceof THREE.RenderableFace ){
_v1 = element.v1;
_v2 = element.v2;
_v3 = element.v3;
if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue;
if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue;
if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue;
_v1.positionScreen.x *= _canvasWidthHalf;
_v1.positionScreen.y *= _canvasHeightHalf;
_v2.positionScreen.x *= _canvasWidthHalf;
_v2.positionScreen.y *= _canvasHeightHalf;
_v3.positionScreen.x *= _canvasWidthHalf;
_v3.positionScreen.y *= _canvasHeightHalf;
if ( material.overdraw > 0 ){
expand( _v1.positionScreen,_v2.positionScreen,material.overdraw );
expand( _v2.positionScreen,_v3.positionScreen,material.overdraw );
expand( _v3.positionScreen,_v1.positionScreen,material.overdraw );
}
_elemBox.setFromPoints( [_v1.positionScreen,_v2.positionScreen,_v3.positionScreen] );
if ( _clipBox.isIntersectionBox( _elemBox ) === true ){
renderFace3( _v1,_v2,_v3,0,1,2,element,material );
}
}
/* DEBUGsetLineWidth( 1 );
setStrokeStyle( 'rgba( 0,255,0,0.5 )' );
_context.strokeRect( _elemBox.min.x,_elemBox.min.y,_elemBox.max.x - _elemBox.min.x,_elemBox.max.y - _elemBox.min.y );
*/
_clearBox.union( _elemBox );
}
/* DEBUGsetLineWidth( 1 );
setStrokeStyle( 'rgba( 255,0,0,0.5 )' );
_context.strokeRect( _clearBox.min.x,_clearBox.min.y,_clearBox.max.x - _clearBox.min.x,_clearBox.max.y - _clearBox.min.y );
*/
_context.setTransform( 1,0,0,1,0,0 );
}
;
//function calculateLights(){
_ambientLight.setRGB( 0,0,0 );
_directionalLights.setRGB( 0,0,0 );
_pointLights.setRGB( 0,0,0 );
for ( var l = 0,ll = _lights.length;
l < ll;
l ++ ){
var light = _lights[ l ];
var lightColor = light.color;
if ( light instanceof THREE.AmbientLight ){
_ambientLight.add( lightColor );
}
else if ( light instanceof THREE.DirectionalLight ){
// for sprites_directionalLights.add( lightColor );
}
else if ( light instanceof THREE.PointLight ){
// for sprites_pointLights.add( lightColor );
}
}
}
function calculateLight( position,normal,color ){
for ( var l = 0,ll = _lights.length;
l < ll;
l ++ ){
var light = _lights[ l ];
_lightColor.copy( light.color );
if ( light instanceof THREE.DirectionalLight ){
var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize();
var amount = normal.dot( lightPosition );
if ( amount <= 0 ) continue;
amount *= light.intensity;
color.add( _lightColor.multiplyScalar( amount ) );
}
else if ( light instanceof THREE.PointLight ){
var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld );
var amount = normal.dot( _vector3.subVectors( lightPosition,position ).normalize() );
if ( amount <= 0 ) continue;
amount *= light.distance == 0 ? 1:1 - Math.min( position.distanceTo( lightPosition ) / light.distance,1 );
if ( amount == 0 ) continue;
amount *= light.intensity;
color.add( _lightColor.multiplyScalar( amount ) );
}
}
}
function renderSprite( v1,element,material ){
setOpacity( material.opacity );
setBlending( material.blending );
var scaleX = element.scale.x * _canvasWidthHalf;
var scaleY = element.scale.y * _canvasHeightHalf;
var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY );
// allow for rotated sprite_elemBox.min.set( v1.x - dist,v1.y - dist );
_elemBox.max.set( v1.x + dist,v1.y + dist );
if ( material instanceof THREE.SpriteMaterial ){
var texture = material.map;
if ( texture !== null && texture.image !== undefined ){
if ( texture.hasEventListener( 'update',onTextureUpdate ) === false ){
if ( texture.image.width > 0 ){
textureToPattern( texture );
}
texture.addEventListener( 'update',onTextureUpdate );
}
var pattern = _patterns[ texture.id ];
if ( pattern !== undefined ){
setFillStyle( pattern );
}
else{
setFillStyle( 'rgba( 0,0,0,1 )' );
}
//var bitmap = texture.image;
var ox = bitmap.width * texture.offset.x;
var oy = bitmap.height * texture.offset.y;
var sx = bitmap.width * texture.repeat.x;
var sy = bitmap.height * texture.repeat.y;
var cx = scaleX / sx;
var cy = scaleY / sy;
_context.save();
_context.translate( v1.x,v1.y );
if ( material.rotation !== 0 ) _context.rotate( material.rotation );
_context.translate( - scaleX / 2,- scaleY / 2 );
_context.scale( cx,cy );
_context.translate( - ox,- oy );
_context.fillRect( ox,oy,sx,sy );
_context.restore();
}
else{
// no texturesetFillStyle( material.color.getStyle() );
_context.save();
_context.translate( v1.x,v1.y );
if ( material.rotation !== 0 ) _context.rotate( material.rotation );
_context.scale( scaleX,- scaleY );
_context.fillRect( - 0.5,- 0.5,1,1 );
_context.restore();
}
}
else if ( material instanceof THREE.SpriteCanvasMaterial ){
setStrokeStyle( material.color.getStyle() );
setFillStyle( material.color.getStyle() );
_context.save();
_context.translate( v1.x,v1.y );
if ( material.rotation !== 0 ) _context.rotate( material.rotation );
_context.scale( scaleX,scaleY );
material.program( _context );
_context.restore();
}
/* DEBUGsetStrokeStyle( 'rgb(255,255,0)' );
_context.beginPath();
_context.moveTo( v1.x - 10,v1.y );
_context.lineTo( v1.x + 10,v1.y );
_context.moveTo( v1.x,v1.y - 10 );
_context.lineTo( v1.x,v1.y + 10 );
_context.stroke();
*/
}
function renderLine( v1,v2,element,material ){
setOpacity( material.opacity );
setBlending( material.blending );
_context.beginPath();
_context.moveTo( v1.positionScreen.x,v1.positionScreen.y );
_context.lineTo( v2.positionScreen.x,v2.positionScreen.y );
if ( material instanceof THREE.LineBasicMaterial ){
setLineWidth( material.linewidth );
setLineCap( material.linecap );
setLineJoin( material.linejoin );
if ( material.vertexColors !== THREE.VertexColors ){
setStrokeStyle( material.color.getStyle() );
}
else{
var colorStyle1 = element.vertexColors[ 0 ].getStyle();
var colorStyle2 = element.vertexColors[ 1 ].getStyle();
if ( colorStyle1 === colorStyle2 ){
setStrokeStyle( colorStyle1 );
}
else{
try{
var grad = _context.createLinearGradient(v1.positionScreen.x,v1.positionScreen.y,v2.positionScreen.x,v2.positionScreen.y);
grad.addColorStop( 0,colorStyle1 );
grad.addColorStop( 1,colorStyle2 );
}
catch ( exception ){
grad = colorStyle1;
}
setStrokeStyle( grad );
}
}
_context.stroke();
_elemBox.expandByScalar( material.linewidth * 2 );
}
else if ( material instanceof THREE.LineDashedMaterial ){
setLineWidth( material.linewidth );
setLineCap( material.linecap );
setLineJoin( material.linejoin );
setStrokeStyle( material.color.getStyle() );
setLineDash( [ material.dashSize,material.gapSize ] );
_context.stroke();
_elemBox.expandByScalar( material.linewidth * 2 );
setLineDash( [] );
}
}
function renderFace3( v1,v2,v3,uv1,uv2,uv3,element,material ){
_this.info.render.vertices += 3;
_this.info.render.faces ++;
setOpacity( material.opacity );
setBlending( material.blending );
_v1x = v1.positionScreen.x;
_v1y = v1.positionScreen.y;
_v2x = v2.positionScreen.x;
_v2y = v2.positionScreen.y;
_v3x = v3.positionScreen.x;
_v3y = v3.positionScreen.y;
drawTriangle( _v1x,_v1y,_v2x,_v2y,_v3x,_v3y );
if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ){
_diffuseColor.copy( material.color );
_emissiveColor.copy( material.emissive );
if ( material.vertexColors === THREE.FaceColors ){
_diffuseColor.multiply( element.color );
}
_color.copy( _ambientLight );
_centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 );
calculateLight( _centroid,element.normalModel,_color );
_color.multiply( _diffuseColor ).add( _emissiveColor );
material.wireframe === true ? strokePath( _color,material.wireframeLinewidth,material.wireframeLinecap,material.wireframeLinejoin ):fillPath( _color );
}
else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ){
if ( material.map !== null ){
var mapping = material.map.mapping;
if ( mapping === THREE.UVMapping ){
_uvs = element.uvs;
patternPath( _v1x,_v1y,_v2x,_v2y,_v3x,_v3y,_uvs[ uv1 ].x,_uvs[ uv1 ].y,_uvs[ uv2 ].x,_uvs[ uv2 ].y,_uvs[ uv3 ].x,_uvs[ uv3 ].y,material.map );
}
}
else if ( material.envMap !== null ){
if ( material.envMap.mapping === THREE.SphericalReflectionMapping ){
_normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix );
_uv1x = 0.5 * _normal.x + 0.5;
_uv1y = 0.5 * _normal.y + 0.5;
_normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix );
_uv2x = 0.5 * _normal.x + 0.5;
_uv2y = 0.5 * _normal.y + 0.5;
_normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix );
_uv3x = 0.5 * _normal.x + 0.5;
_uv3y = 0.5 * _normal.y + 0.5;
patternPath( _v1x,_v1y,_v2x,_v2y,_v3x,_v3y,_uv1x,_uv1y,_uv2x,_uv2y,_uv3x,_uv3y,material.envMap );
}
}
else{
_color.copy( material.color );
if ( material.vertexColors === THREE.FaceColors ){
_color.multiply( element.color );
}
material.wireframe === true ? strokePath( _color,material.wireframeLinewidth,material.wireframeLinecap,material.wireframeLinejoin ):fillPath( _color );
}
}
else if ( material instanceof THREE.MeshDepthMaterial ){
_color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w,_camera.near,_camera.far );
material.wireframe === true ? strokePath( _color,material.wireframeLinewidth,material.wireframeLinecap,material.wireframeLinejoin ):fillPath( _color );
}
else if ( material instanceof THREE.MeshNormalMaterial ){
_normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix );
_color.setRGB( _normal.x,_normal.y,_normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
material.wireframe === true ? strokePath( _color,material.wireframeLinewidth,material.wireframeLinecap,material.wireframeLinejoin ):fillPath( _color );
}
else{
_color.setRGB( 1,1,1 );
material.wireframe === true ? strokePath( _color,material.wireframeLinewidth,material.wireframeLinecap,material.wireframeLinejoin ):fillPath( _color );
}
}
//function drawTriangle( x0,y0,x1,y1,x2,y2 ){
_context.beginPath();
_context.moveTo( x0,y0 );
_context.lineTo( x1,y1 );
_context.lineTo( x2,y2 );
_context.closePath();
}
function strokePath( color,linewidth,linecap,linejoin ){
setLineWidth( linewidth );
setLineCap( linecap );
setLineJoin( linejoin );
setStrokeStyle( color.getStyle() );
_context.stroke();
_elemBox.expandByScalar( linewidth * 2 );
}
function fillPath( color ){
setFillStyle( color.getStyle() );
_context.fill();
}
function onTextureUpdate ( event ){
textureToPattern( event.target );
}
function textureToPattern( texture ){
if ( texture instanceof THREE.CompressedTexture ) return;
var repeatX = texture.wrapS === THREE.RepeatWrapping;
var repeatY = texture.wrapT === THREE.RepeatWrapping;
var image = texture.image;
var canvas = document.createElement( 'canvas' );
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext( '2d' );
context.setTransform( 1,0,0,- 1,0,image.height );
context.drawImage( image,0,0 );
_patterns[ texture.id ] = _context.createPattern(canvas,repeatX === true && repeatY === true ? 'repeat':repeatX === true && repeatY === false ? 'repeat-x':repeatX === false && repeatY === true ? 'repeat-y':'no-repeat');
}
function patternPath( x0,y0,x1,y1,x2,y2,u0,v0,u1,v1,u2,v2,texture ){
if ( texture instanceof THREE.DataTexture ) return;
if ( texture.hasEventListener( 'update',onTextureUpdate ) === false ){
if ( texture.image !== undefined && texture.image.width > 0 ){
textureToPattern( texture );
}
texture.addEventListener( 'update',onTextureUpdate );
}
var pattern = _patterns[ texture.id ];
if ( pattern !== undefined ){
setFillStyle( pattern );
}
else{
setFillStyle( 'rgba(0,0,0,1)' );
_context.fill();
return;
}
// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120var a,b,c,d,e,f,det,idet,offsetX = texture.offset.x / texture.repeat.x,offsetY = texture.offset.y / texture.repeat.y,width = texture.image.width * texture.repeat.x,height = texture.image.height * texture.repeat.y;
u0 = ( u0 + offsetX ) * width;
v0 = ( v0 + offsetY ) * height;
u1 = ( u1 + offsetX ) * width;
v1 = ( v1 + offsetY ) * height;
u2 = ( u2 + offsetX ) * width;
v2 = ( v2 + offsetY ) * height;
x1 -= x0;
y1 -= y0;
x2 -= x0;
y2 -= y0;
u1 -= u0;
v1 -= v0;
u2 -= u0;
v2 -= v0;
det = u1 * v2 - u2 * v1;
if ( det === 0 ) return;
idet = 1 / det;
a = ( v2 * x1 - v1 * x2 ) * idet;
b = ( v2 * y1 - v1 * y2 ) * idet;
c = ( u1 * x2 - u2 * x1 ) * idet;
d = ( u1 * y2 - u2 * y1 ) * idet;
e = x0 - a * u0 - c * v0;
f = y0 - b * u0 - d * v0;
_context.save();
_context.transform( a,b,c,d,e,f );
_context.fill();
_context.restore();
}
function clipImage( x0,y0,x1,y1,x2,y2,u0,v0,u1,v1,u2,v2,image ){
// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120var a,b,c,d,e,f,det,idet,width = image.width - 1,height = image.height - 1;
u0 *= width;
v0 *= height;
u1 *= width;
v1 *= height;
u2 *= width;
v2 *= height;
x1 -= x0;
y1 -= y0;
x2 -= x0;
y2 -= y0;
u1 -= u0;
v1 -= v0;
u2 -= u0;
v2 -= v0;
det = u1 * v2 - u2 * v1;
idet = 1 / det;
a = ( v2 * x1 - v1 * x2 ) * idet;
b = ( v2 * y1 - v1 * y2 ) * idet;
c = ( u1 * x2 - u2 * x1 ) * idet;
d = ( u1 * y2 - u2 * y1 ) * idet;
e = x0 - a * u0 - c * v0;
f = y0 - b * u0 - d * v0;
_context.save();
_context.transform( a,b,c,d,e,f );
_context.clip();
_context.drawImage( image,0,0 );
_context.restore();
}
// Hide anti-alias gapsfunction expand( v1,v2,pixels ){
var x = v2.x - v1.x,y = v2.y - v1.y,det = x * x + y * y,idet;
if ( det === 0 ) return;
idet = pixels / Math.sqrt( det );
x *= idet;
y *= idet;
v2.x += x;
v2.y += y;
v1.x -= x;
v1.y -= y;
}
// Context cached methods.function setOpacity( value ){
if ( _contextGlobalAlpha !== value ){
_context.globalAlpha = value;
_contextGlobalAlpha = value;
}
}
function setBlending( value ){
if ( _contextGlobalCompositeOperation !== value ){
if ( value === THREE.NormalBlending ){
_context.globalCompositeOperation = 'source-over';
}
else if ( value === THREE.AdditiveBlending ){
_context.globalCompositeOperation = 'lighter';
}
else if ( value === THREE.SubtractiveBlending ){
_context.globalCompositeOperation = 'darker';
}
_contextGlobalCompositeOperation = value;
}
}
function setLineWidth( value ){
if ( _contextLineWidth !== value ){
_context.lineWidth = value;
_contextLineWidth = value;
}
}
function setLineCap( value ){
// "butt","round","square"if ( _contextLineCap !== value ){
_context.lineCap = value;
_contextLineCap = value;
}
}
function setLineJoin( value ){
// "round","bevel","miter"if ( _contextLineJoin !== value ){
_context.lineJoin = value;
_contextLineJoin = value;
}
}
function setStrokeStyle( value ){
if ( _contextStrokeStyle !== value ){
_context.strokeStyle = value;
_contextStrokeStyle = value;
}
}
function setFillStyle( value ){
if ( _contextFillStyle !== value ){
_context.fillStyle = value;
_contextFillStyle = value;
}
}
function setLineDash( value ){
if ( _contextLineDash.length !== value.length ){
_context.setLineDash( value );
_contextLineDash = value;
}
}
}
;
JS代码(Sparks.js):
/* * @author zz85 (http://github.com/zz85 http://www.lab4games.net/zz85/blog) * * a simple to use javascript 3d particles system inspired by FliNT and Stardust * created with TWEEN.js and THREE.js * * for feature requests or bugs,please visit https://github.com/zz85/sparks.js * * licensed under the MIT license */
var SPARKS ={
}
;
/********************************* Emitter Class** Creates and Manages Particles*********************************/
SPARKS.Emitter = function (counter){
this._counter = counter ? counter:new SPARKS.SteadyCounter(10);
// provides number of particles to producethis._particles = [];
this._initializers = [];
// use for creation of particlesthis._actions = [];
// uses action to update particlesthis._activities = [];
// not supported yetthis._handlers = [];
this.callbacks ={
}
;
}
;
SPARKS.Emitter.prototype ={
_TIMESTEP:15,_timer:null,_lastTime:null,_timerStep:10,_velocityVerlet:true,// run its built in timer / steppingstart:function(){
this._lastTime = Date.now();
this._timer = setTimeout(this.step,this._timerStep,this);
this._isRunning = true;
}
,stop:function(){
this._isRunning = false;
clearTimeout(this._timer);
}
,isRunning:function(){
return this._isRunning & true;
}
,// Step gets called upon by the engine// but attempts to call update() on a regular basics// This method is also described in http://gameclosure.com/2011/04/11/deterministic-delta-tee-in-js-games/step:function(emitter){
var time = Date.now();
var elapsed = time - emitter._lastTime;
if (!this._velocityVerlet){
// if elapsed is way higher than time step,(usually after switching tabs,or excution cached in ff)// we will drop cycles. perhaps set to a limit of 10 or something?var maxBlock = emitter._TIMESTEP * 20;
if (elapsed >= maxBlock){
//console.log('warning:sparks.js is fast fowarding engine,skipping steps',elapsed / emitter._TIMESTEP);
//emitter.update( (elapsed - maxBlock) / 1000);
elapsed = maxBlock;
}
while (elapsed >= emitter._TIMESTEP){
emitter.update(emitter._TIMESTEP / 1000);
elapsed -= emitter._TIMESTEP;
}
emitter._lastTime = time - elapsed;
}
else{
emitter.update(elapsed / 1000);
emitter._lastTime = time;
}
if (emitter._isRunning)setTimeout(emitter.step,emitter._timerStep,emitter);
}
,// Update particle engine in seconds,not milliseconds update:function(time){
var i,j;
var len = this._counter.updateEmitter( this,time );
// Create particlesfor ( i = 0;
i < len;
i ++ ){
this.createParticle();
}
// Update activitieslen = this._activities.length;
for ( i = 0;
i < len;
i ++ ){
this._activities[i].update( this,time );
}
len = this._actions.length;
var particle;
var action;
var len2 = this._particles.length;
for ( j = 0;
j < len;
j ++ ){
action = this._actions[j];
for ( i = 0;
i < len2;
++ i ){
particle = this._particles[i];
action.update( this,particle,time );
}
}
// remove dead particlesfor ( i = len2;
i --;
){
particle = this._particles[i];
if ( particle.isDead ){
//particle =this._particles.splice( i,1 );
this.dispatchEvent("dead",particle);
SPARKS.VectorPool.release(particle.position);
//SPARKS.VectorPool.release(particle.velocity);
}
else{
this.dispatchEvent("updated",particle);
}
}
this.dispatchEvent("loopUpdated");
}
,createParticle:function(){
var particle = new SPARKS.Particle();
// In future,use a Particle Factoryvar len = this._initializers.length,i;
for ( i = 0;
i < len;
i ++ ){
this._initializers[i].initialize( this,particle );
}
this._particles.push( particle );
this.dispatchEvent("created",particle);
// ParticleCreatedreturn particle;
}
,addInitializer:function (initializer){
this._initializers.push(initializer);
}
,addAction:function (action){
this._actions.push(action);
}
,removeInitializer:function (initializer){
var index = this._initializers.indexOf(initializer);
if (index > -1){
this._initializers.splice( index,1 );
}
}
,removeAction:function (action){
var index = this._actions.indexOf(action);
if (index > -1){
this._actions.splice( index,1 );
}
//console.log('removeAction',index,this._actions);
}
,addCallback:function(name,callback){
this.callbacks[name] = callback;
}
,dispatchEvent:function(name,args){
var callback = this.callbacks[name];
if (callback){
callback(args);
}
}
}
;
/* * Constant Names for * Events called by emitter.dispatchEvent() * */
SPARKS.EVENT_PARTICLE_CREATED = "created"SPARKS.EVENT_PARTICLE_UPDATED = "updated"SPARKS.EVENT_PARTICLE_DEAD = "dead";
SPARKS.EVENT_LOOP_UPDATED = "loopUpdated";
/* * Steady Counter attempts to produces a particle rate steadily * */
// Number of particles per secondsSPARKS.SteadyCounter = function(rate){
this.rate = rate;
// we use a shortfall counter to make up for slow emittersthis.leftover = 0;
}
;
SPARKS.SteadyCounter.prototype.updateEmitter = function(emitter,time){
var targetRelease = time * this.rate + this.leftover;
var actualRelease = Math.floor(targetRelease);
this.leftover = targetRelease - actualRelease;
return actualRelease;
}
;
/* * Shot Counter produces specified particles * on a single impluse or burst */
SPARKS.ShotCounter = function(particles){
this.particles = particles;
this.used = false;
}
;
SPARKS.ShotCounter.prototype.updateEmitter = function(emitter,time){
if (this.used){
return 0;
}
else{
this.used = true;
}
return this.particles;
}
;
/********************************* Particle Class** Represents a single particle*********************************/
SPARKS.Particle = function(){
/** * The lifetime of the particle,in seconds. */
this.lifetime = 0;
/** * The age of the particle,in seconds. */
this.age = 0;
/** * The energy of the particle. */
this.energy = 1;
/** * Whether the particle is dead and should be removed from the stage. */
this.isDead = false;
this.target = null;
// tag /** * For 3D */
this.position = SPARKS.VectorPool.get().set(0,0,0);
//new THREE.Vector3( 0,0,0 );
this.velocity = SPARKS.VectorPool.get().set(0,0,0);
//new THREE.Vector3( 0,0,0 );
this._oldvelocity = SPARKS.VectorPool.get().set(0,0,0);
// rotation vec3 // angVelocity vec3 // faceAxis vec3}
;
/********************************* Action Classes** An abstract class which have* update function*********************************/
SPARKS.Action = function(){
this._priority = 0;
}
;
SPARKS.Age = function(easing){
this._easing = (easing == null) ? TWEEN.Easing.Linear.None:easing;
}
;
SPARKS.Age.prototype.update = function (emitter,particle,time){
particle.age += time;
if ( particle.age >= particle.lifetime ){
particle.energy = 0;
particle.isDead = true;
}
else{
var t = this._easing(particle.age / particle.lifetime);
particle.energy = -1 * t + 1;
}
}
;
/*/
/ Mark particle as dead when particle's < 0SPARKS.Death = function(easing){
this._easing = (easing == null) ? TWEEN.Linear.None:easing;
}
;
SPARKS.Death.prototype.update = function (emitter,particle,time){
if (particle.life <= 0){
particle.isDead = true;
}
}
;
*/
SPARKS.Move = function(){
}
;
SPARKS.Move.prototype.update = function(emitter,particle,time){
// attempt verlet velocity updating.var p = particle.position;
var v = particle.velocity;
var old = particle._oldvelocity;
if (this._velocityVerlet){
p.x += (v.x + old.x) * 0.5 * time;
p.y += (v.y + old.y) * 0.5 * time;
p.z += (v.z + old.z) * 0.5 * time;
}
else{
p.x += v.x * time;
p.y += v.y * time;
p.z += v.z * time;
}
// OldVel = Vel;
// Vel = Vel + Accel * dt;
// Pos = Pos + (vel + Vel + Accel * dt) * 0.5 * dt;
}
;
/* Marks particles found in specified zone dead */
SPARKS.DeathZone = function(zone){
this.zone = zone;
}
;
SPARKS.DeathZone.prototype.update = function(emitter,particle,time){
if (this.zone.contains(particle.position)){
particle.isDead = true;
}
}
;
/* * SPARKS.ActionZone applies an action when particle is found in zone */
SPARKS.ActionZone = function(action,zone){
this.action = action;
this.zone = zone;
}
;
SPARKS.ActionZone.prototype.update = function(emitter,particle,time){
if (this.zone.contains(particle.position)){
this.action.update( emitter,particle,time );
}
}
;
/* * Accelerate action affects velocity in specified 3d direction */
SPARKS.Accelerate = function(x,y,z){
if (x instanceof THREE.Vector3){
this.acceleration = x;
return;
}
this.acceleration = new THREE.Vector3(x,y,z);
}
;
SPARKS.Accelerate.prototype.update = function(emitter,particle,time){
var acc = this.acceleration;
var v = particle.velocity;
particle._oldvelocity.set(v.x,v.y,v.z);
v.x += acc.x * time;
v.y += acc.y * time;
v.z += acc.z * time;
}
;
/* * Accelerate Factor accelerate based on a factor of particle's velocity. */
SPARKS.AccelerateFactor = function(factor){
this.factor = factor;
}
;
SPARKS.AccelerateFactor.prototype.update = function(emitter,particle,time){
var factor = this.factor;
var v = particle.velocity;
var len = v.length();
var adjFactor;
if (len > 0){
adjFactor = factor * time / len;
adjFactor += 1;
v.multiplyScalar(adjFactor);
// v.x *= adjFactor;
// v.y *= adjFactor;
// v.z *= adjFactor;
}
}
;
/*AccelerateNormal * AccelerateVelocity affects velocity based on its velocity direction */
SPARKS.AccelerateVelocity = function(factor){
this.factor = factor;
}
;
SPARKS.AccelerateVelocity.prototype.update = function(emitter,particle,time){
var factor = this.factor;
var v = particle.velocity;
v.z += - v.x * factor;
v.y += v.z * factor;
v.x += v.y * factor;
}
;
/* Set the max ammount of x,y,z drift movements in a second */
SPARKS.RandomDrift = function(x,y,z){
if (x instanceof THREE.Vector3){
this.drift = x;
return;
}
this.drift = new THREE.Vector3(x,y,z);
}
SPARKS.RandomDrift.prototype.update = function(emitter,particle,time){
var drift = this.drift;
var v = particle.velocity;
v.x += ( Math.random() - 0.5 ) * drift.x * time;
v.y += ( Math.random() - 0.5 ) * drift.y * time;
v.z += ( Math.random() - 0.5 ) * drift.z * time;
}
;
/********************************* Zone Classes** An abstract classes which have* getLocation() function*********************************/
SPARKS.Zone = function(){
}
;
// TODO,contains() for ZoneSPARKS.PointZone = function(pos){
this.pos = pos;
}
;
SPARKS.PointZone.prototype.getLocation = function(){
return this.pos;
}
;
SPARKS.PointZone = function(pos){
this.pos = pos;
}
;
SPARKS.PointZone.prototype.getLocation = function(){
return this.pos;
}
;
SPARKS.LineZone = function(start,end){
this.start = start;
this.end = end;
this._length = end.clone().sub( start );
}
;
SPARKS.LineZone.prototype.getLocation = function(){
var len = this._length.clone();
len.multiplyScalar( Math.random() );
return len.add( this.start );
}
;
// Basically a RectangleZoneSPARKS.ParallelogramZone = function(corner,side1,side2){
this.corner = corner;
this.side1 = side1;
this.side2 = side2;
}
;
SPARKS.ParallelogramZone.prototype.getLocation = function(){
var d1 = this.side1.clone().multiplyScalar( Math.random() );
var d2 = this.side2.clone().multiplyScalar( Math.random() );
d1.add(d2);
return d1.add( this.corner );
}
;
SPARKS.CubeZone = function(position,x,y,z){
this.position = position;
this.x = x;
this.y = y;
this.z = z;
}
;
SPARKS.CubeZone.prototype.getLocation = function(){
//TODO use pool?var location = this.position.clone();
location.x += Math.random() * this.x;
location.y += Math.random() * this.y;
location.z += Math.random() * this.z;
return location;
}
;
SPARKS.CubeZone.prototype.contains = function(position){
var startX = this.position.x;
var startY = this.position.y;
var startZ = this.position.z;
var x = this.x;
// widthvar y = this.y;
// depthvar z = this.z;
// heightif (x < 0){
startX += x;
x = Math.abs(x);
}
if (y < 0){
startY += y;
y = Math.abs(y);
}
if (z < 0){
startZ += z;
z = Math.abs(z);
}
var diffX = position.x - startX;
var diffY = position.y - startY;
var diffZ = position.z - startZ;
if ( (diffX > 0) && (diffX < x) &&(diffY > 0) && (diffY < y) &&(diffZ > 0) && (diffZ < z) ){
return true;
}
return false;
}
;
/** * The constructor creates a DiscZone 3D zone. * * @param centre The point at the center of the disc. * @param normal A vector normal to the disc. * @param outerRadius The outer radius of the disc. * @param innerRadius The inner radius of the disc. This defines the hole * in the center of the disc. If set to zero,there is no hole. */
/*/
/ BUGGY!!SPARKS.DiscZone = function(center,radiusNormal,outerRadius,innerRadius){
this.center = center;
this.radiusNormal = radiusNormal;
this.outerRadius = (outerRadius==undefined) ? 0:outerRadius;
this.innerRadius = (innerRadius==undefined) ? 0:innerRadius;
}
;
SPARKS.DiscZone.prototype.getLocation = function(){
var rand = Math.random();
var _innerRadius = this.innerRadius;
var _outerRadius = this.outerRadius;
var center = this.center;
var _normal = this.radiusNormal;
_distToOrigin = _normal.dot( center );
var radius = _innerRadius + (1 - rand * rand ) * ( _outerRadius - _innerRadius );
var angle = Math.random() * SPARKS.Utils.TWOPI;
var _distToOrigin = _normal.dot( center );
var axes = SPARKS.Utils.getPerpendiculars( _normal.clone() );
var _planeAxis1 = axes[0];
var _planeAxis2 = axes[1];
var p = _planeAxis1.clone();
p.multiplyScalar( radius * Math.cos( angle ) );
var p2 = _planeAxis2.clone();
p2.multiplyScalar( radius * Math.sin( angle ) );
p.add( p2 );
return _center.add( p );
}
;
*/
SPARKS.SphereCapZone = function(x,y,z,minr,maxr,angle){
this.x = x;
this.y = y;
this.z = z;
this.minr = minr;
this.maxr = maxr;
this.angle = angle;
}
;
SPARKS.SphereCapZone.prototype.getLocation = function(){
var theta = Math.PI * 2 * SPARKS.Utils.random();
var r = SPARKS.Utils.random();
//new THREE.Vector3var v = SPARKS.VectorPool.get().set(r * Math.cos(theta),-1 / Math.tan(this.angle * SPARKS.Utils.DEGREE_TO_RADIAN),r * Math.sin(theta));
//v.length = StardustMath.interpolate(0,_minRadius,1,_maxRadius,Math.random());
var i = this.minr - ((this.minr - this.maxr) * Math.random() );
v.multiplyScalar(i);
v.__markedForReleased = true;
return v;
}
;
/********************************* Initializer Classes** Classes which initializes* particles. Implements initialize( emitter:Emitter,particle:Particle )*********************************/
// Specifies random life between max and minSPARKS.Lifetime = function(min,max){
this._min = min;
this._max = max ? max:min;
}
;
SPARKS.Lifetime.prototype.initialize = function( emitter/*Emitter*/
,particle/*Particle*/
){
particle.lifetime = this._min + SPARKS.Utils.random() * ( this._max - this._min );
}
;
SPARKS.Position = function(zone){
this.zone = zone;
}
;
SPARKS.Position.prototype.initialize = function( emitter/*Emitter*/
,particle/*Particle*/
){
var pos = this.zone.getLocation();
particle.position.set(pos.x,pos.y,pos.z);
}
;
SPARKS.Velocity = function(zone){
this.zone = zone;
}
;
SPARKS.Velocity.prototype.initialize = function( emitter/*Emitter*/
,particle/*Particle*/
){
var pos = this.zone.getLocation();
particle.velocity.set(pos.x,pos.y,pos.z);
if (pos.__markedForReleased){
//console.log("release");
SPARKS.VectorPool.release(pos);
pos.__markedForReleased = false;
}
}
;
SPARKS.Target = function(target,callback){
this.target = target;
this.callback = callback;
}
;
SPARKS.Target.prototype.initialize = function( emitter,particle ){
if (this.callback){
particle.target = this.callback();
}
else{
particle.target = this.target;
}
}
;
/********************************* VectorPool** Reuse much of Vectors if possible*********************************/
SPARKS.VectorPool ={
__pools:[],// Get a new Vectorget:function(){
if (this.__pools.length > 0){
return this.__pools.pop();
}
return this._addToPool();
}
,// Release a vector back into the poolrelease:function(v){
this.__pools.push(v);
}
,// Create a bunch of vectors and add to the pool_addToPool:function(){
//console.log("creating some pools");
for (var i = 0,size = 100;
i < size;
i ++){
this.__pools.push(new THREE.Vector3());
}
return new THREE.Vector3();
}
}
;
/********************************* Util Classes** Classes which initializes* particles. Implements initialize( emitter:Emitter,particle:Particle )*********************************/
SPARKS.Utils ={
random:function(){
return Math.random();
}
,DEGREE_TO_RADIAN:Math.PI / 180,TWOPI:Math.PI * 2,getPerpendiculars:function(normal){
var p1 = this.getPerpendicular( normal );
var p2 = normal.cross( p1 );
p2.normalize();
return [ p1,p2 ];
}
,getPerpendicular:function( v ){
if ( v.x == 0 ){
return new THREE.Vector3D( 1,0,0 );
}
else{
var temp = new THREE.Vector3( v.y,-v.x,0 );
return temp.normalize();
}
}
}
;