炫酷的粒子效果
源码地址:https://codepen.io/zadvorsky/pen/PNXbGo
代码已经整理好了,非常炫酷,可以单击并拖动以控制动画,但好像只能放两张图片(我自己尝试过可能自己技术不行吧),喜欢的话可以复制粘贴拿去~
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/bas.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/OrbitControls-2.js"></script>
</head>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
background-image: radial-gradient(#666, #333);
}
#instructions {
position: absolute;
color: #fff;
bottom: 0;
padding-bottom: 6px;
font-family: sans-serif;
width: 100%;
text-align: center;
pointer-events: none;
}
</style>
<body>
<div id="three-container"></div>
<div id="instructions">
click and drag to control the animation
</div>
</body>
<script>
window.onload = init;
console.ward = function() {}; // what warnings?
function init() {
var root = new THREERoot({
createCameraControls: !true,
antialias: (window.devicePixelRatio === 1),
fov: 80
});
root.renderer.setClearColor(0x000000, 0);
root.renderer.setPixelRatio(window.devicePixelRatio || 1);
root.camera.position.set(0, 0, 60);
var width = 100;
var height = 60;
var slide = new Slide(width, height, 'out');
var l1 = new THREE.ImageLoader();
l1.setCrossOrigin('Anonymous');
l1.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/winter.jpg', function(img) {
slide.setImage(img);
})
root.scene.add(slide);
var slide2 = new Slide(width, height, 'in');
var l2 = new THREE.ImageLoader();
l2.setCrossOrigin('Anonymous');
l2.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/spring.jpg', function(img) {
slide2.setImage(img);
})
root.scene.add(slide2);
var tl = new TimelineMax({repeat:-1, repeatDelay:1.0, yoyo: true});
tl.add(slide.transition(), 0);
tl.add(slide2.transition(), 0);
createTweenScrubber(tl);
window.addEventListener('keyup', function(e) {
if (e.keyCode === 80) {
tl.paused(!tl.paused());
}
});
}
// CLASSES
function Slide(width, height, animationPhase) {
var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);
THREE.BAS.Utils.separateFaces(plane);
var geometry = new SlideGeometry(plane);
geometry.bufferUVs();
var aAnimation = geometry.createAttribute('aAnimation', 2);
var aStartPosition = geometry.createAttribute('aStartPosition', 3);
var aControl0 = geometry.createAttribute('aControl0', 3);
var aControl1 = geometry.createAttribute('aControl1', 3);
var aEndPosition = geometry.createAttribute('aEndPosition', 3);
var i, i2, i3, i4, v;
var minDuration = 0.8;
var maxDuration = 1.2;
var maxDelayX = 0.9;
var maxDelayY = 0.125;
var stretch = 0.11;
this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;
var startPosition = new THREE.Vector3();
var control0 = new THREE.Vector3();
var control1 = new THREE.Vector3();
var endPosition = new THREE.Vector3();
var tempPoint = new THREE.Vector3();
function getControlPoint0(centroid) {
var signY = Math.sign(centroid.y);
tempPoint.x = THREE.Math.randFloat(0.1, 0.3) * 50;
tempPoint.y = signY * THREE.Math.randFloat(0.1, 0.3) * 70;
tempPoint.z = THREE.Math.randFloatSpread(20);
return tempPoint;
}
function getControlPoint1(centroid) {
var signY = Math.sign(centroid.y);
tempPoint.x = THREE.Math.randFloat(0.3, 0.6) * 50;
tempPoint.y = -signY * THREE.Math.randFloat(0.3, 0.6) * 70;
tempPoint.z = THREE.Math.randFloatSpread(20);
return tempPoint;
}
for (i = 0, i2 = 0, i3 = 0, i4 = 0; i < geometry.faceCount; i++, i2 += 6, i3 += 9, i4 += 12) {
var face = plane.faces[i];
var centroid = THREE.BAS.Utils.computeCentroid(plane, face);
// animation
var duration = THREE.Math.randFloat(minDuration, maxDuration);
var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
var delayY;
if (animationPhase === 'in') {
delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, 0.0, maxDelayY)
}
else {
delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0)
}
for (v = 0; v < 6; v += 2) {
aAnimation.array[i2 + v] = delayX + delayY + (Math.random() * stretch * duration);
aAnimation.array[i2 + v + 1] = duration;
}
// positions
endPosition.copy(centroid);
startPosition.copy(centroid);
if (animationPhase === 'in') {
control0.copy(centroid).sub(getControlPoint0(centroid));
control1.copy(centroid).sub(getControlPoint1(centroid));
}
else { // out
control0.copy(centroid).add(getControlPoint0(centroid));
control1.copy(centroid).add(getControlPoint1(centroid));
}
for (v = 0; v < 9; v += 3) {
aStartPosition.array[i3 + v] = startPosition.x;
aStartPosition.array[i3 + v + 1] = startPosition.y;
aStartPosition.array[i3 + v + 2] = startPosition.z;
aControl0.array[i3 + v] = control0.x;
aControl0.array[i3 + v + 1] = control0.y;
aControl0.array[i3 + v + 2] = control0.z;
aControl1.array[i3 + v] = control1.x;
aControl1.array[i3 + v + 1] = control1.y;
aControl1.array[i3 + v + 2] = control1.z;
aEndPosition.array[i3 + v] = endPosition.x;
aEndPosition.array[i3 + v + 1] = endPosition.y;
aEndPosition.array[i3 + v + 2] = endPosition.z;
}
}
var material = new THREE.BAS.BasicAnimationMaterial(
{
shading: THREE.FlatShading,
side: THREE.DoubleSide,
uniforms: {
uTime: {type: 'f', value: 0}
},
shaderFunctions: [
THREE.BAS.ShaderChunk['cubic_bezier'],
//THREE.BAS.ShaderChunk[(animationPhase === 'in' ? 'ease_out_cubic' : 'ease_in_cubic')],
THREE.BAS.ShaderChunk['ease_in_out_cubic'],
THREE.BAS.ShaderChunk['quaternion_rotation']
],
shaderParameters: [
'uniform float uTime;',
'attribute vec2 aAnimation;',
'attribute vec3 aStartPosition;',
'attribute vec3 aControl0;',
'attribute vec3 aControl1;',
'attribute vec3 aEndPosition;',
],
shaderVertexInit: [
'float tDelay = aAnimation.x;',
'float tDuration = aAnimation.y;',
'float tTime = clamp(uTime - tDelay, 0.0, tDuration);',
'float tProgress = ease(tTime, 0.0, 1.0, tDuration);'
//'float tProgress = tTime / tDuration;'
],
shaderTransformPosition: [
(animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;'),
'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
]
},
{
map: new THREE.Texture(),
}
);
THREE.Mesh.call(this, geometry, material);
this.frustumCulled = false;
}
Slide.prototype = Object.create(THREE.Mesh.prototype);
Slide.prototype.constructor = Slide;
Object.defineProperty(Slide.prototype, 'time', {
get: function () {
return this.material.uniforms['uTime'].value;
},
set: function (v) {
this.material.uniforms['uTime'].value = v;
}
});
Slide.prototype.setImage = function(image) {
this.material.uniforms.map.value.image = image;
this.material.uniforms.map.value.needsUpdate = true;
};
Slide.prototype.transition = function() {
return TweenMax.fromTo(this, 3.0, {time:0.0}, {time:this.totalDuration, ease:Power0.easeInOut});
};
function SlideGeometry(model) {
THREE.BAS.ModelBufferGeometry.call(this, model);
}
SlideGeometry.prototype = Object.create(THREE.BAS.ModelBufferGeometry.prototype);
SlideGeometry.prototype.constructor = SlideGeometry;
SlideGeometry.prototype.bufferPositions = function () {
var positionBuffer = this.createAttribute('position', 3).array;
for (var i = 0; i < this.faceCount; i++) {
var face = this.modelGeometry.faces[i];
var centroid = THREE.BAS.Utils.computeCentroid(this.modelGeometry, face);
var a = this.modelGeometry.vertices[face.a];
var b = this.modelGeometry.vertices[face.b];
var c = this.modelGeometry.vertices[face.c];
positionBuffer[face.a * 3] = a.x - centroid.x;
positionBuffer[face.a * 3 + 1] = a.y - centroid.y;
positionBuffer[face.a * 3 + 2] = a.z - centroid.z;
positionBuffer[face.b * 3] = b.x - centroid.x;
positionBuffer[face.b * 3 + 1] = b.y - centroid.y;
positionBuffer[face.b * 3 + 2] = b.z - centroid.z;
positionBuffer[face.c * 3] = c.x - centroid.x;
positionBuffer[face.c * 3 + 1] = c.y - centroid.y;
positionBuffer[face.c * 3 + 2] = c.z - centroid.z;
}
};
function THREERoot(params) {
params = utils.extend({
fov: 60,
zNear: 10,
zFar: 100000,
createCameraControls: true
}, params);
this.renderer = new THREE.WebGLRenderer({
antialias: params.antialias,
alpha: true
});
this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
document.getElementById('three-container').appendChild(this.renderer.domElement);
this.camera = new THREE.PerspectiveCamera(
params.fov,
window.innerWidth / window.innerHeight,
params.zNear,
params.zfar
);
this.scene = new THREE.Scene();
if (params.createCameraControls) {
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
}
this.resize = this.resize.bind(this);
this.tick = this.tick.bind(this);
this.resize();
this.tick();
window.addEventListener('resize', this.resize, false);
}
THREERoot.prototype = {
tick: function () {
this.update();
this.render();
requestAnimationFrame(this.tick);
},
update: function () {
this.controls && this.controls.update();
},
render: function () {
this.renderer.render(this.scene, this.camera);
},
resize: function () {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
};
// UTILS
var utils = {
extend: function (dst, src) {
for (var key in src) {
dst[key] = src[key];
}
return dst;
},
randSign: function () {
return Math.random() > 0.5 ? 1 : -1;
},
ease: function (ease, t, b, c, d) {
return b + ease.getRatio(t / d) * c;
},
fibSpherePoint: (function () {
var vec = {x: 0, y: 0, z: 0};
var G = Math.PI * (3 - Math.sqrt(5));
return function (i, n, radius) {
var step = 2.0 / n;
var r, phi;
vec.y = i * step - 1 + (step * 0.5);
r = Math.sqrt(1 - vec.y * vec.y);
phi = i * G;
vec.x = Math.cos(phi) * r;
vec.z = Math.sin(phi) * r;
radius = radius || 1;
vec.x *= radius;
vec.y *= radius;
vec.z *= radius;
return vec;
}
})(),
spherePoint: (function () {
return function (u, v) {
u === undefined && (u = Math.random());
v === undefined && (v = Math.random());
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var vec = {};
vec.x = (Math.sin(phi) * Math.cos(theta));
vec.y = (Math.sin(phi) * Math.sin(theta));
vec.z = (Math.cos(phi));
return vec;
}
})()
};
function createTweenScrubber(tween, seekSpeed) {
seekSpeed = seekSpeed || 0.001;
function stop() {
TweenMax.to(tween, 1, {timeScale:0});
}
function resume() {
TweenMax.to(tween, 1, {timeScale:1});
}
function seek(dx) {
var progress = tween.progress();
var p = THREE.Math.clamp((progress + (dx * seekSpeed)), 0, 1);
tween.progress(p);
}
var _cx = 0;
// desktop
var mouseDown = false;
document.body.style.cursor = 'pointer';
window.addEventListener('mousedown', function(e) {
mouseDown = true;
document.body.style.cursor = 'ew-resize';
_cx = e.clientX;
stop();
});
window.addEventListener('mouseup', function(e) {
mouseDown = false;
document.body.style.cursor = 'pointer';
resume();
});
window.addEventListener('mousemove', function(e) {
if (mouseDown === true) {
var cx = e.clientX;
var dx = cx - _cx;
_cx = cx;
seek(dx);
}
});
// mobile
window.addEventListener('touchstart', function(e) {
_cx = e.touches[0].clientX;
stop();
e.preventDefault();
});
window.addEventListener('touchend', function(e) {
resume();
e.preventDefault();
});
window.addEventListener('touchmove', function(e) {
var cx = e.touches[0].clientX;
var dx = cx - _cx;
_cx = cx;
seek(dx);
e.preventDefault();
});
}
</script>
</html>