|
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 = 80; |
|
|
|
var slide = new Slide(width, height, 'out'); |
|
var l1 = new THREE.ImageLoader(); |
|
l1.setCrossOrigin('Anonymous'); |
|
slide.setImage(l1.load('https://scontent-mia3-2.xx.fbcdn.net/v/t1.0-9/96280972_10217948916054533_1492589731945381888_o.jpg?_nc_cat=103&_nc_sid=8bfeb9&_nc_ohc=CPxKdLumauAAX_-Zi3K&_nc_ht=scontent-mia3-2.xx&oh=f64a3207b4b6d80e7d273316449f0b99&oe=5EE45097')); |
|
root.scene.add(slide); |
|
|
|
var slide2 = new Slide(width, height, 'in'); |
|
var l2 = new THREE.ImageLoader(); |
|
l2.setCrossOrigin('Anonymous'); |
|
slide2.setImage(l2.load('https://scontent-mia3-1.xx.fbcdn.net/v/t1.0-9/96540561_10217948916454543_4116088698464894976_o.jpg?_nc_cat=104&_nc_sid=8bfeb9&_nc_ohc=MjxPRKLIyrUAX_9-4RZ&_nc_ht=scontent-mia3-1.xx&oh=963d1b8324115d453361788a3a6825a1&oe=5EE60BFF')); |
|
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(); |
|
}); |
|
} |