I would like to have more than one Three.js transition in one page but it only displays the second instance. I know it is possible because I have read some other questions on the subject but I'm very new to Three.js and I still don't understand how I would do it in this specific instance. Thanks in advance!
Codepen here:
https://codepen.io/mastroneel/pen/pQqWKd
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 = 100;
var slide = new Slide(width, height, 'out');
var l1 = new THREE.ImageLoader();
l1.setCrossOrigin('Anonymous');
slide.setImage(l1.load('https://image.ibb.co/f6mVsA/helmet.png'));
root.scene.add(slide);
var slide2 = new Slide(width, height, 'in');
var l2 = new THREE.ImageLoader();
l2.setCrossOrigin('Anonymous');
slide2.setImage(l2.load('https://image.ibb.co/mb1KkV/player.png'));
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-container1').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();
});
}
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 = 100;
var slide = new Slide(width, height, 'out');
var l1 = new THREE.ImageLoader();
l1.setCrossOrigin('Anonymous');
slide.setImage(l1.load('https://image.ibb.co/f6mVsA/helmet.png'));
root.scene.add(slide);
var slide2 = new Slide(width, height, 'in');
var l2 = new THREE.ImageLoader();
l2.setCrossOrigin('Anonymous');
slide2.setImage(l2.load('https://image.ibb.co/mb1KkV/player.png'));
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-container2').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();
});
}
body {
margin: 0;
background: #fff;
}
canvas {
background: #fff;
}
<div id="three-container1"></div>
<div id="three-container2"></div>
Related
We coded a spinning 3d shape in js. There's a flicker in the render of the top triangle, we think it's because the z sorting is not working correctly. How do we resolve this?
Here's a jsfiddle.
Here's the z sorting code:
// z sorting
// dots_for_rendering.sort((a,b) => Math.sqrt((b.x)**2 + (b.y)**2) - Math.sqrt((a.x)**2 + (a.y)**2))
for (var i = 0; i < polygons.length; i++) {
polygons[i].maxz = -Infinity;
polygons[i].minz = Infinity;
polygons[i].midz = 0;
for (var j = 0; j < polygons[i].verticies.length; j++) {
var z = rotated_verticies[polygons[i].verticies[j]].vector[2];
if (z > polygons[i].maxz) {
polygons[i].maxz = z;
}
if (z < polygons[i].minz) {
polygons[i].minz = z;
}
polygons[i].midz += z;
}
polygons[i].midz /= polygons[i].verticies.length;
}
polygons.sort((a, b) => b.midz - a.midz)
// polygons.sort((a,b) => Math.max(b.maxz - a.minz, b.minz - a.maxz))
// polygons.sort((a,b) => {
// if (a.minz < b.maxz) {
// return 0;
// }
// if (b.minz < a.maxz) {
// return -1;
// }
// return 0;
// })
Here's a code snippet:
class Tensor {
constructor(){
var input = this.takeInput(...arguments);
this.vector = input;
}
takeInput() {
var a = true;
for (var arg of arguments) {
if (typeof arg !== "number"){
a = false
}
}
if (a && arguments[2] !== true){
return new Array(...arguments);
}
else {
if (arguments[0] instanceof Tensor){
return arguments[0].vector;
}
else {
if (typeof arguments[0] === "number" && typeof arguments[1] === "number" && arguments[2] === true) {
var res = [];
for (var i = 0; i < arguments[0]; i++) {
res.push(arguments[1]);
}
return res;
}
}
}
}
// used for + - * /
change(f, input){
for (var i in this.vector) {
this.vector[i] = f(this.vector[i], input[i]);
}
return this;
}
copy() {
return new Tensor(...this.vector);
}
dimentions() {
return this.vector.length;
}
//-----------
len() {
var s = 0;
for (var dim of this.vector) {
s += dim ** 2;
}
return Math.sqrt(s);
}
norm() {
return this.div(this.dimentions(), this.len(), true)
}
add() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x + y, input);
}
sub() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x - y, input);
}
mult() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x * y, input);
}
div() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x / y, input);
}
dot() {
var input = this.takeInput(...arguments);
var res = 0;
for (var i in this.vector) {
res += this.vector[i] * input[i]
}
return res;
}
rotate() {
// WARNING: only for 3D currently!!!
var input = this.takeInput(...arguments);
var [x, y, z] = this.vector;
// rotate Z
var t_x = x * Math.cos(input[2]) - y * Math.sin(input[2])
y = y * Math.cos(input[2]) + x * Math.sin(input[2])
x = t_x
// rotate X
var t_y = y * Math.cos(input[0]) - z * Math.sin(input[0])
z = z * Math.cos(input[0]) + y * Math.sin(input[0])
y = t_y
// rotate Y
t_x = x * Math.cos(input[1]) + z * Math.sin(input[1])
z = z * Math.cos(input[1]) - x * Math.sin(input[1])
x = t_x
this.vector = [x, y, z];
return this;
}
}
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext("2d")
w = 300
h = 286
fov = 0.1
scale = 65;
offset = new Tensor(w / 2 - 5, h / 2 - 92, 0.1);
light = new Tensor(3.5, 0.5, 1).norm();
canvas.width = w;
canvas.height = h;
var verticies = [];
verticies.push(new Tensor(0.5, 1, 0))
verticies.push(new Tensor(0.5, -1, 0))
verticies.push(new Tensor(-1, 0, 0))
verticies.push(new Tensor(0, 0, 2))
var polygons = [];
polygons.push({
verticies: [0, 3, 1],
color: 'red',
nf: 1
});
polygons.push({
verticies: [2, 3, 0],
color: 'blue',
nf: 1
});
polygons.push({
verticies: [2, 3, 1],
color: 'green',
nf: -1
});
polygons.push({
verticies: [0, 1, 2],
color: 'yellow',
nf: -1
});
for (var i = 0; i < polygons.length; i++) {
polygons[i].id = i;
}
theta = new Tensor(1.5 * Math.PI, 0, 1.5 * Math.PI);
function loop() {
ctx.clearRect(0, 0, w, h);
rotated_verticies = [];
for (var i = 0; i < verticies.length; i++) {
rotated_verticies.push(verticies[i].copy().rotate(theta));
}
// z sorting
// dots_for_rendering.sort((a,b) => Math.sqrt((b.x)**2 + (b.y)**2) - Math.sqrt((a.x)**2 + (a.y)**2))
for (var i = 0; i < polygons.length; i++) {
polygons[i].maxz = -Infinity;
polygons[i].minz = Infinity;
polygons[i].midz = 0;
for (var j = 0; j < polygons[i].verticies.length; j++) {
var z = rotated_verticies[polygons[i].verticies[j]].vector[2];
// z += 1 * (Math.random() * 2 - 1)
if (z > polygons[i].maxz) {
polygons[i].maxz = z;
}
if (z < polygons[i].minz) {
polygons[i].minz = z;
}
polygons[i].midz += z;
}
polygons[i].midz /= polygons[i].verticies.length;
}
polygons.sort((a, b) => b.midz - a.midz)
// polygons.sort((a,b) => Math.max(b.maxz - a.minz, b.minz - a.maxz))
// polygons.sort((a,b) => {
// if (a.minz < b.maxz) {
// return 0;
// }
// if (b.minz < a.maxz) {
// return -1;
// }
// return 0;
// })
for (var i = 0; i < polygons.length; i++) {
var polygon_2 = [];
for (var j = 0; j < polygons[i].verticies.length; j++) {
var v = rotated_verticies[polygons[i].verticies[j]]
polygon_2.push(v.vector);
}
var norm = getNormal(polygon_2, polygons[i].nf);
// var rotated_light = light.copy().rotate(theta);
var brightness = Math.max(0, norm.dot(light))
//ctx.fillStyle = "hsl(31, "+100+"%, "+(Math.min(9.0*brightness + 40, 100))+"%)";
ctx.fillStyle = "hsl(190, "+100+"%, "+(Math.min(9.0*brightness + 40, 100))+"%)";
// ctx.fillStyle = polygons[i].color
ctx.beginPath();
for (var j = 0; j < polygons[i].verticies.length; j++) {
var vertex = rotated_verticies[polygons[i].verticies[j]].copy();
vertex.mult(scale, scale, 1);
vertex.add(offset);
var n = 1 + vertex.vector[2] * fov;
vertex.div(n, n, 1)
// console.log(vertex.vector)
if (j == 0) {
ctx.moveTo(vertex.vector[0], vertex.vector[1]);
} else {
ctx.lineTo(vertex.vector[0], vertex.vector[1]);
}
}
ctx.closePath();
ctx.fill()
// ctx.stroke()
polygons[i].mid = new Tensor(3, 0, true);
for (var k = 0; k < polygons[i].verticies.length; k++) {
var vertex = rotated_verticies[polygons[i].verticies[k]].copy();
vertex.mult(scale, scale, 1);
vertex.add(offset);
var n = 1 + vertex.vector[2] * fov;
vertex.div(n, n, 1)
polygons[i].mid.add(vertex);
}
polygons[i].mid.div(3, polygons[i].verticies.length, true);
ctx.fillStyle = "red"
ctx.font = '50px serif';
// ctx.fillText(polygons[i].id + ", " + polygons[i].nf, polygons[i].mid.vector[0], polygons[i].mid.vector[1])
}
// theta.add(theta.vector[0] + (0.01*mouseY - theta.vector[0]) * 0.1, 0, theta.vector[2] + (-0.01*mouseX - theta.vector[2]) * 0.1)
theta.add(0, -0.0375, 0);
// fov = (mouseX - w/2) * 0.001
requestAnimationFrame(loop);
}
loop();
// setInterval(loop, 1000 / 60)
function getNormal(polygon, nf) {
var Ax = polygon[1][0] - polygon[0][0];
var Ay = polygon[1][1] - polygon[0][1];
var Az = polygon[1][2] - polygon[0][2];
var Bx = polygon[2][0] - polygon[0][0];
var By = polygon[2][1] - polygon[0][1];
var Bz = polygon[2][2] - polygon[0][2];
var Nx = Ay * Bz - Az * By
var Ny = Az * Bx - Ax * Bz
var Nz = Ax * By - Ay * Bx
return new Tensor(nf * Nx, nf * Ny, nf * Nz);
}
function len(p1, p2) {
return Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2 + (p2[2] - p1[2]) ** 2);
}
mouseX = 0
mouseY = 0
onmousemove = (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name=”ad.size” content=”width=300,height=600”>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>...</title>
</head>
<body>
<canvas id="canvas" width="300" height="238"></canvas>
</body>
</html>
** Edit:
Ok, we've significantly edited the code, see this fiddle, and the following code snippet below. It's still not working correctly, we think it's something to do with the first line of this piece of code, any ideas?
if (polygons[i].mid.copy().sub(camera).dot(norm) < 0) {
var pathelem = document.createElementNS("http://www.w3.org/2000/svg", "path");
pathelem.setAttribute("d", path);
pathelem.setAttribute("fill", "hsl(31, "+100+"%, "+(Math.min(9.0*brightness + 40, 100))+"%)");
svg.appendChild(pathelem);
}
class Tensor {
constructor(){
var input = this.takeInput(...arguments);
this.vector = input;
}
takeInput() {
var a = true;
for (var arg of arguments) {
if (typeof arg !== "number"){
a = false
}
}
if (a && arguments[2] !== true){
return new Array(...arguments);
}
else {
if (arguments[0] instanceof Tensor){
return arguments[0].vector;
}
else {
if (typeof arguments[0] === "number" && typeof arguments[1] === "number" && arguments[2] === true) {
var res = [];
for (var i = 0; i < arguments[0]; i++) {
res.push(arguments[1]);
}
return res;
}
}
}
}
// used for + - * /
change(f, input){
for (var i in this.vector) {
this.vector[i] = f(this.vector[i], input[i]);
}
return this;
}
copy() {
return new Tensor(...this.vector);
}
dimentions() {
return this.vector.length;
}
//-----------
len() {
var s = 0;
for (var dim of this.vector) {
s += dim ** 2;
}
return Math.sqrt(s);
}
norm() {
return this.div(this.dimentions(), this.len(), true)
}
add() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x + y, input);
}
sub() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x - y, input);
}
mult() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x * y, input);
}
div() {
var input = this.takeInput(...arguments);
return this.change((x, y) => x / y, input);
}
dot() {
var input = this.takeInput(...arguments);
var res = 0;
for (var i in this.vector) {
res += this.vector[i] * input[i]
}
return res;
}
rotate() {
// WARNING: only for 3D currently!!!
var input = this.takeInput(...arguments);
var [x, y, z] = this.vector;
// rotate Z
var t_x = x * Math.cos(input[2]) - y * Math.sin(input[2])
y = y * Math.cos(input[2]) + x * Math.sin(input[2])
x = t_x
// rotate X
var t_y = y * Math.cos(input[0]) - z * Math.sin(input[0])
z = z * Math.cos(input[0]) + y * Math.sin(input[0])
y = t_y
// rotate Y
t_x = x * Math.cos(input[1]) + z * Math.sin(input[1])
z = z * Math.cos(input[1]) - x * Math.sin(input[1])
x = t_x
this.vector = [x, y, z];
return this;
}
}
var svg = document.getElementById('svg')
w = 300
h = 286
fov = 0.1
scale = 65;
camera = new Tensor(-w / 2 + 5, -h / 2 + 92, 0.1);
light = new Tensor(3.5, 0.5, 1).norm();
svg.setAttribute('width', w);
svg.setAttribute('height', h);
var vertices = [
new Tensor(0.5, 1, 0),
new Tensor(0.5, -1, 0),
new Tensor(-1, 0, 0),
new Tensor(0, 0, 2)
];
var polygons = [];
polygons.push({
vertices: [0, 3, 1],
color: 'red',
nf: 1
});
polygons.push({
vertices: [2, 3, 0],
color: 'blue',
nf: 1
});
polygons.push({
vertices: [2, 3, 1],
color: 'green',
nf: -1
});
polygons.push({
vertices: [0, 1, 2],
color: 'yellow',
nf: 1
});
for (var i = 0; i < polygons.length; i++) {
polygons[i].id = i;
}
theta = new Tensor(1.5 * Math.PI, 0, 1.5 * Math.PI);
function loop() {
// ctx.clearRect(0, 0, w, h);
svg.innerHTML = "";
rotated_vertices = [];
for (var i = 0; i < vertices.length; i++) {
rotated_vertices.push(vertices[i].copy().rotate(theta));
}
// z sorting
// dots_for_rendering.sort((a,b) => Math.sqrt((b.x)**2 + (b.y)**2) - Math.sqrt((a.x)**2 + (a.y)**2))
for (var i = 0; i < polygons.length; i++) {
polygons[i].maxz = -Infinity;
polygons[i].minz = Infinity;
polygons[i].midz = 0;
for (var j = 0; j < polygons[i].vertices.length; j++) {
var z = rotated_vertices[polygons[i].vertices[j]].vector[2];
// z += 1 * (Math.random() * 2 - 1)
if (z > polygons[i].maxz) {
polygons[i].maxz = z;
}
if (z < polygons[i].minz) {
polygons[i].minz = z;
}
polygons[i].midz += z;
}
polygons[i].midz /= polygons[i].vertices.length;
polygons[i].mid = new Tensor(3, 0, true);
for (var k = 0; k < polygons[i].vertices.length; k++) {
var vertex = rotated_vertices[polygons[i].vertices[k]].copy();
vertex.mult(scale, scale, 1);
vertex.sub(camera);
var n = 1 + vertex.vector[2] * fov;
vertex.div(n, n, 1)
polygons[i].mid.add(vertex);
}
polygons[i].mid.div(3, polygons[i].vertices.length, true);
}
polygons.sort((a, b) => b.midz - a.midz)
// polygons.sort((a,b) => Math.max(b.maxz - a.minz, b.minz - a.maxz))
// polygons.sort((a,b) => {
// if (a.minz < b.maxz) {
// return 0;
// }
// if (b.minz < a.maxz) {
// return -1;
// }
// return 0;
// })
for (var i = 0; i < polygons.length; i++) {
var polygons_embedded_point_coords = [];
for (var j = 0; j < polygons[i].vertices.length; j++) {
var v = rotated_vertices[polygons[i].vertices[j]]
polygons_embedded_point_coords.push(v.vector);
}
var norm = getNormal(polygons_embedded_point_coords, polygons[i].nf);
// var rotated_light = light.copy().rotate(theta);
var brightness = Math.max(0, norm.dot(light))
// ctx.fillStyle = "hsl(31, "+100+"%, "+(Math.min(9.0*brightness + 40, 100))+"%)";
// ctx.fillStyle = polygons[i].color
// ctx.beginPath();
var path = [];
for (var j = 0; j < polygons[i].vertices.length; j++) {
var vertex = rotated_vertices[polygons[i].vertices[j]].copy();
vertex.mult(scale, scale, 1);
vertex.sub(camera);
var n = 1 + vertex.vector[2] * fov;
vertex.div(n, n, 1)
// console.log(vertex.vector)
if (j == 0) {
// ctx.moveTo(vertex.vector[0], vertex.vector[1]);
path.push("M "+vertex.vector[0]+" "+vertex.vector[1]);
} else {
path.push("L "+vertex.vector[0]+" "+vertex.vector[1]);
// ctx.lineTo(vertex.vector[0], vertex.vector[1]);
}
}
// that should work
if (polygons[i].mid.copy().sub(camera).dot(norm) < 0) {
var pathelem = document.createElementNS("http://www.w3.org/2000/svg", "path");
pathelem.setAttribute("d", path);
pathelem.setAttribute("fill", "hsl(31, "+100+"%, "+(Math.min(9.0*brightness + 40, 100))+"%)");
svg.appendChild(pathelem);
}
// ctx.fillStyle = "red"
// ctx.font = '15px serif';
//
// ctx.fillText(polygons[i].id + ", " + polygons[i].nf, polygons[i].mid.vector[0], polygons[i].mid.vector[1])
}
// theta.add(theta.vector[0] + (0.01*mouseY - theta.vector[0]) * 0.1, 0, theta.vector[2] + (-0.01*mouseX - theta.vector[2]) * 0.1)
theta.add(0, -0.0375, 0);
// fov = (mouseX - w/2) * 0.001
requestAnimationFrame(loop);
}
loop();
// setInterval(loop, 1000 / 60)
function getNormal(vertices, nf) {
var Ax = vertices[1][0] - vertices[0][0];
var Ay = vertices[1][1] - vertices[0][1];
var Az = vertices[1][2] - vertices[0][2];
var Bx = vertices[2][0] - vertices[0][0];
var By = vertices[2][1] - vertices[0][1];
var Bz = vertices[2][2] - vertices[0][2];
var Nx = Ay * Bz - Az * By
var Ny = Az * Bx - Ax * Bz
var Nz = Ax * By - Ay * Bx
return new Tensor(nf * Nx, nf * Ny, nf * Nz);
}
function len(p1, p2) {
return Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2 + (p2[2] - p1[2]) ** 2);
}
mouseX = 0
mouseY = 0
onmousemove = (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<script src="Tensor.js"></script>
<script src="script-tensors-svg.js" async defer></script>
</head>
<body>
<!-- <canvas id="canvas"></canvas> -->
<svg id="svg" xmlns="http://www.w3.org/2000/svg"></svg>
</body>
</html>
Sorting by average Z just doesn't give you a reliable rendering order. Since your shape is convex, though, you don't need to sort at all.
Make sure the vertices of each triangle are sorted so that you can consistently get a surface normal that points outward. Then, just don't render any triangles with normals that point away from the camera, i.e.:
if (vector_from_camera_to_poly_midpoint \dot poly_normal < 0) {
//render the poly
}
Now you will only render the side of the object that is facing the camera -- none of the polygons will overlap, so you can render them in any order.
I've been trying to add a (code pen) animation on my website and I'm honestly not sure what I'm missing on this one. I have tried running it in jsfiddle as well and it tells me that delaunay is not defined. https://codepen.io/hduffin1/pen/QOMZJg I'm not too sure what I'm doing wrong since the code works inside of code pen and I have been able to replicate other ones that I've tried using from code pen but for whatever reason, I can't seem to get this one to work.
Html
<canvas id="stars" width="300" height="300"></canvas>
CSS
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #31102f; //#280B29
background: radial-gradient(
ellipse at center,
rgba(49, 16, 47, 1) 0%,
rgba(40, 11, 41, 1) 100%
);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 16rem;
height: 100vh;
z-index: 1;
}
JS
/**
* Stars
* Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
* by Cory Hughart - http://coryhughart.com
*/
// Settings
var particleCount = 40,
flareCount = 10,
motion = 0.05,
tilt = 0.05,
color = '#FFEED4',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 1, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.05,
renderParticles = true,
renderParticleGlare = true,
renderFlares = true,
renderLinks = true,
renderMesh = false,
flicker = true,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 1;
var canvas = document.getElementById('stars'),
//orbits = document.getElementById('orbits'),
context = canvas.getContext('2d'),
mouse = { x: 0, y: 0 },
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {x: 0, y: 0},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Fade in background
/*
var background = document.getElementById('background'),
bgImg = new Image(),
bgURL = '/img/background.jpg';
bgImg.onload = function() {
//console.log('background loaded');
background.style.backgroundImage = 'url("'+bgURL+'")';
background.className += ' loaded';
}
bgImg.src = bgURL;
*/
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x*c, p.y*c]);
}
//console.log(JSON.stringify(points));
// Delaunay triangulation
//var Delaunay = require('delaunay-fast');
vertices = Delaunay.triangulate(points);
//console.log(JSON.stringify(vertices));
// Create an array of "triangles" (groups of 3 indices)
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
//console.log(JSON.stringify(triangles));
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
//console.log(JSON.stringify(particles));
if (renderFlares) {
// Create flare positions
for (i = 0; i < flareCount; i++) {
flares.push(new Flare());
}
}
// Motion mode
//if (Modernizr && Modernizr.deviceorientation) {
if ('ontouchstart' in document.documentElement && window.DeviceOrientationEvent) {
console.log('Using device orientation');
window.addEventListener('deviceorientation', function(e) {
mouse.x = (canvas.clientWidth / 2) - ((e.gamma / 90) * (canvas.clientWidth / 2) * 2);
mouse.y = (canvas.clientHeight / 2) - ((e.beta / 90) * (canvas.clientHeight / 2) * 2);
//console.log('Center: x:'+(canvas.clientWidth/2)+' y:'+(canvas.clientHeight/2));
//console.log('Orientation: x:'+mouse.x+' ('+e.gamma+') y:'+mouse.y+' ('+e.beta+')');
}, true);
}
else {
// Mouse move listener
console.log('Using mouse movement');
document.body.addEventListener('mousemove', function(e) {
//console.log('moved');
mouse.x = e.clientX;
mouse.y = e.clientY;
});
}
// Random motion
if (randomMotion) {
//var SimplexNoise = require('simplex-noise');
//var simplex = new SimplexNoise();
}
// Animation loop
(function animloop(){
requestAnimFrame(animloop);
resize();
render();
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
//console.log('NOISE x:'+nPos.x+' y:'+nPos.y);
}
// Clear
context.clearRect(0, 0, canvas.width, canvas.height);
if (blurSize > 0) {
context.shadowBlur = blurSize;
context.shadowColor = color;
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
if (renderMesh) {
// Render all lines
context.beginPath();
for (var v = 0; v < vertices.length-1; v++) {
// Splits the array into triplets
if ((v + 1) % 3 === 0) { continue; }
var p1 = particles[vertices[v]],
p2 = particles[vertices[v+1]];
//console.log('Line: '+p1.x+','+p1.y+'->'+p2.x+','+p2.y);
var pos1 = position(p1.x, p1.y, p1.z),
pos2 = position(p2.x, p2.y, p2.z);
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
}
if (renderLinks) {
// Possibly start a new link
if (random(0, linkChance) == linkChance) {
var length = random(linkLengthMin, linkLengthMax);
var start = random(0, particles.length-1);
startLink(start, length);
}
// Render existing links
// Iterate in reverse so that removing items doesn't affect the loop
for (var l = links.length-1; l >= 0; l--) {
if (links[l] && !links[l].finished) {
links[l].render();
}
else {
delete links[l];
}
}
}
if (renderFlares) {
// Render flares
for (var j = 0; j < flareCount; j++) {
flares[j].render();
}
}
/*
if (orbitTilt) {
var tiltX = -(((canvas.clientWidth / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * tilt),
tiltY = (((canvas.clientHeight / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * tilt);
orbits.style.transform = 'rotateY('+tiltX+'deg) rotateX('+tiltY+'deg)';
}
*/
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
function startLink(vertex, length) {
//console.log('LINK from '+vertex+' (length '+length+')');
links.push(new Link(vertex, length));
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0,4);
this.color = color;
this.opacity = random(0.1,1,true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
if (flicker) {
var newVal = random(-0.5, 0.5, true);
this.flicker += (newVal - this.flicker) / flickerSmoothing;
if (this.flicker > 0.5) this.flicker = 0.5;
if (this.flicker < -0.5) this.flicker = -0.5;
o += this.flicker;
if (o > 1) o = 1;
if (o < 0) o = 0;
}
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
/*
context.ellipse(pos.x, pos.y, r * 30, r, 90 * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
*/
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
var Flare = function() {
this.x = random(-0.25, 1.25, true);
this.y = random(-0.25, 1.25, true);
this.z = random(0,2);
this.color = color;
this.opacity = random(0.001, 0.01, true);
};
Flare.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * flareSizeMultiplier) + flareSizeBase) * (sizeRatio() / 1000);
// Feathered circles
/*
var grad = context.createRadialGradient(x+r,y+r,0,x+r,y+r,r);
grad.addColorStop(0, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(0.8, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = grad;
context.beginPath();
context.fillRect(x, y, r*2, r*2);
context.closePath();
*/
context.beginPath();
context.globalAlpha = this.opacity;
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fillStyle = this.color;
context.fill();
context.closePath();
context.globalAlpha = 1;
};
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
Link.prototype.render = function() {
// Stages:
// 0. Vertex collection
// 1. Render line reaching from vertex to vertex
// 2. Fade out
// 3. Finished (delete me)
var i, p, pos, points;
switch (this.stage) {
// VERTEX COLLECTION STAGE
case 0:
// Grab the last member of the link
var last = particles[this.verts[this.verts.length-1]];
//console.log(JSON.stringify(last));
if (last && last.neighbors && last.neighbors.length > 0) {
// Grab a random neighbor
var neighbor = last.neighbors[random(0, last.neighbors.length-1)];
// If we haven't seen that particle before, add it to the link
if (this.verts.indexOf(neighbor) == -1) {
this.verts.push(neighbor);
}
// If we have seen that article before, we'll just wait for the next frame
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (0)');
this.stage = 3;
this.finished = true;
}
if (this.verts.length >= this.length) {
// Calculate all distances at once
for (i = 0; i < this.verts.length-1; i++) {
var p1 = particles[this.verts[i]],
p2 = particles[this.verts[i+1]],
dx = p1.x - p2.x,
dy = p1.y - p2.y,
dist = Math.sqrt(dx*dx + dy*dy);
this.distances.push(dist);
}
//console.log('Distances: '+JSON.stringify(this.distances));
//console.log('verts: '+this.verts.length+' distances: '+this.distances.length);
//console.log(this.verts[0]+' moving to stage 1');
this.stage = 1;
}
break;
// RENDER LINE ANIMATION STAGE
case 1:
if (this.distances.length > 0) {
points = [];
//var a = 1;
// Gather all points already linked
for (i = 0; i < this.linked.length; i++) {
p = particles[this.linked[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
var linkSpeedRel = linkSpeed * 0.00001 * canvas.width;
this.traveled += linkSpeedRel;
var d = this.distances[this.linked.length-1];
// Calculate last point based on linkSpeed and distance travelled to next point
if (this.traveled >= d) {
this.traveled = 0;
// We've reached the next point, add coordinates to array
//console.log(this.verts[0]+' reached vertex '+(this.linked.length+1)+' of '+this.verts.length);
this.linked.push(this.verts[this.linked.length]);
p = particles[this.linked[this.linked.length-1]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
if (this.linked.length >= this.verts.length) {
//console.log(this.verts[0]+' moving to stage 2 (1)');
this.stage = 2;
}
}
else {
// We're still travelling to the next point, get coordinates at travel distance
// http://math.stackexchange.com/a/85582
var a = particles[this.linked[this.linked.length-1]],
b = particles[this.verts[this.linked.length]],
t = d - this.traveled,
x = ((this.traveled * b.x) + (t * a.x)) / d,
y = ((this.traveled * b.y) + (t * a.y)) / d,
z = ((this.traveled * b.z) + (t * a.z)) / d;
pos = position(x, y, z);
//console.log(this.verts[0]+' traveling to vertex '+(this.linked.length+1)+' of '+this.verts.length+' ('+this.traveled+' of '+this.distances[this.linked.length]+')');
points.push([pos.x, pos.y]);
}
this.drawLine(points);
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (1)');
this.stage = 3;
this.finished = true;
}
break;
// FADE OUT STAGE
case 2:
if (this.verts.length > 1) {
if (this.fade < linkFade) {
this.fade++;
// Render full link between all vertices and fade over time
points = [];
var alpha = (1 - (this.fade / linkFade)) * linkOpacity;
for (i = 0; i < this.verts.length; i++) {
p = particles[this.verts[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
this.drawLine(points, alpha);
}
else {
//console.log(this.verts[0]+' moving to stage 3 (2a)');
this.stage = 3;
this.finished = true;
}
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (2b)');
this.stage = 3;
this.finished = true;
}
break;
// FINISHED STAGE
case 3:
default:
this.finished = true;
break;
}
};
Link.prototype.drawLine = function(points, alpha) {
if (typeof alpha !== 'number') alpha = linkOpacity;
if (points.length > 1 && alpha > 0) {
//console.log(this.verts[0]+': Drawing line '+alpha);
context.globalAlpha = alpha;
context.beginPath();
for (var i = 0; i < points.length-1; i++) {
context.moveTo(points[i][0], points[i][1]);
context.lineTo(points[i+1][0], points[i+1][1]);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
context.globalAlpha = 1;
}
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
//value = simplex.noise2D(nScale * cosA + nScale, nScale * sinA + nScale),
//rad = nRad + value;
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
When I entered 'https://codepen.io/hduffin1/pen/QOMZJg', 'delaunay.js' is included in the setting.
Add the following script and it should work.
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
/**
* Stars
* Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
* by Cory Hughart - http://coryhughart.com
*/
// Settings
var particleCount = 40,
flareCount = 10,
motion = 0.05,
tilt = 0.05,
color = '#FFEED4',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 1, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.05,
renderParticles = true,
renderParticleGlare = true,
renderFlares = true,
renderLinks = true,
renderMesh = false,
flicker = true,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 1;
var canvas = document.getElementById('stars'),
//orbits = document.getElementById('orbits'),
context = canvas.getContext('2d'),
mouse = { x: 0, y: 0 },
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {x: 0, y: 0},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Fade in background
/*
var background = document.getElementById('background'),
bgImg = new Image(),
bgURL = '/img/background.jpg';
bgImg.onload = function() {
//console.log('background loaded');
background.style.backgroundImage = 'url("'+bgURL+'")';
background.className += ' loaded';
}
bgImg.src = bgURL;
*/
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x*c, p.y*c]);
}
//console.log(JSON.stringify(points));
// Delaunay triangulation
//var Delaunay = require('delaunay-fast');
vertices = Delaunay.triangulate(points);
//console.log(JSON.stringify(vertices));
// Create an array of "triangles" (groups of 3 indices)
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
//console.log(JSON.stringify(triangles));
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
//console.log(JSON.stringify(particles));
if (renderFlares) {
// Create flare positions
for (i = 0; i < flareCount; i++) {
flares.push(new Flare());
}
}
// Motion mode
//if (Modernizr && Modernizr.deviceorientation) {
if ('ontouchstart' in document.documentElement && window.DeviceOrientationEvent) {
console.log('Using device orientation');
window.addEventListener('deviceorientation', function(e) {
mouse.x = (canvas.clientWidth / 2) - ((e.gamma / 90) * (canvas.clientWidth / 2) * 2);
mouse.y = (canvas.clientHeight / 2) - ((e.beta / 90) * (canvas.clientHeight / 2) * 2);
//console.log('Center: x:'+(canvas.clientWidth/2)+' y:'+(canvas.clientHeight/2));
//console.log('Orientation: x:'+mouse.x+' ('+e.gamma+') y:'+mouse.y+' ('+e.beta+')');
}, true);
}
else {
// Mouse move listener
console.log('Using mouse movement');
document.body.addEventListener('mousemove', function(e) {
//console.log('moved');
mouse.x = e.clientX;
mouse.y = e.clientY;
});
}
// Random motion
if (randomMotion) {
//var SimplexNoise = require('simplex-noise');
//var simplex = new SimplexNoise();
}
// Animation loop
(function animloop(){
requestAnimFrame(animloop);
resize();
render();
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
//console.log('NOISE x:'+nPos.x+' y:'+nPos.y);
}
// Clear
context.clearRect(0, 0, canvas.width, canvas.height);
if (blurSize > 0) {
context.shadowBlur = blurSize;
context.shadowColor = color;
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
if (renderMesh) {
// Render all lines
context.beginPath();
for (var v = 0; v < vertices.length-1; v++) {
// Splits the array into triplets
if ((v + 1) % 3 === 0) { continue; }
var p1 = particles[vertices[v]],
p2 = particles[vertices[v+1]];
//console.log('Line: '+p1.x+','+p1.y+'->'+p2.x+','+p2.y);
var pos1 = position(p1.x, p1.y, p1.z),
pos2 = position(p2.x, p2.y, p2.z);
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
}
if (renderLinks) {
// Possibly start a new link
if (random(0, linkChance) == linkChance) {
var length = random(linkLengthMin, linkLengthMax);
var start = random(0, particles.length-1);
startLink(start, length);
}
// Render existing links
// Iterate in reverse so that removing items doesn't affect the loop
for (var l = links.length-1; l >= 0; l--) {
if (links[l] && !links[l].finished) {
links[l].render();
}
else {
delete links[l];
}
}
}
if (renderFlares) {
// Render flares
for (var j = 0; j < flareCount; j++) {
flares[j].render();
}
}
/*
if (orbitTilt) {
var tiltX = -(((canvas.clientWidth / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * tilt),
tiltY = (((canvas.clientHeight / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * tilt);
orbits.style.transform = 'rotateY('+tiltX+'deg) rotateX('+tiltY+'deg)';
}
*/
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
function startLink(vertex, length) {
//console.log('LINK from '+vertex+' (length '+length+')');
links.push(new Link(vertex, length));
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0,4);
this.color = color;
this.opacity = random(0.1,1,true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
if (flicker) {
var newVal = random(-0.5, 0.5, true);
this.flicker += (newVal - this.flicker) / flickerSmoothing;
if (this.flicker > 0.5) this.flicker = 0.5;
if (this.flicker < -0.5) this.flicker = -0.5;
o += this.flicker;
if (o > 1) o = 1;
if (o < 0) o = 0;
}
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
/*
context.ellipse(pos.x, pos.y, r * 30, r, 90 * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
*/
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
var Flare = function() {
this.x = random(-0.25, 1.25, true);
this.y = random(-0.25, 1.25, true);
this.z = random(0,2);
this.color = color;
this.opacity = random(0.001, 0.01, true);
};
Flare.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * flareSizeMultiplier) + flareSizeBase) * (sizeRatio() / 1000);
// Feathered circles
/*
var grad = context.createRadialGradient(x+r,y+r,0,x+r,y+r,r);
grad.addColorStop(0, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(0.8, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = grad;
context.beginPath();
context.fillRect(x, y, r*2, r*2);
context.closePath();
*/
context.beginPath();
context.globalAlpha = this.opacity;
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fillStyle = this.color;
context.fill();
context.closePath();
context.globalAlpha = 1;
};
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
Link.prototype.render = function() {
// Stages:
// 0. Vertex collection
// 1. Render line reaching from vertex to vertex
// 2. Fade out
// 3. Finished (delete me)
var i, p, pos, points;
switch (this.stage) {
// VERTEX COLLECTION STAGE
case 0:
// Grab the last member of the link
var last = particles[this.verts[this.verts.length-1]];
//console.log(JSON.stringify(last));
if (last && last.neighbors && last.neighbors.length > 0) {
// Grab a random neighbor
var neighbor = last.neighbors[random(0, last.neighbors.length-1)];
// If we haven't seen that particle before, add it to the link
if (this.verts.indexOf(neighbor) == -1) {
this.verts.push(neighbor);
}
// If we have seen that particle before, we'll just wait for the next frame
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (0)');
this.stage = 3;
this.finished = true;
}
if (this.verts.length >= this.length) {
// Calculate all distances at once
for (i = 0; i < this.verts.length-1; i++) {
var p1 = particles[this.verts[i]],
p2 = particles[this.verts[i+1]],
dx = p1.x - p2.x,
dy = p1.y - p2.y,
dist = Math.sqrt(dx*dx + dy*dy);
this.distances.push(dist);
}
//console.log('Distances: '+JSON.stringify(this.distances));
//console.log('verts: '+this.verts.length+' distances: '+this.distances.length);
//console.log(this.verts[0]+' moving to stage 1');
this.stage = 1;
}
break;
// RENDER LINE ANIMATION STAGE
case 1:
if (this.distances.length > 0) {
points = [];
//var a = 1;
// Gather all points already linked
for (i = 0; i < this.linked.length; i++) {
p = particles[this.linked[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
var linkSpeedRel = linkSpeed * 0.00001 * canvas.width;
this.traveled += linkSpeedRel;
var d = this.distances[this.linked.length-1];
// Calculate last point based on linkSpeed and distance travelled to next point
if (this.traveled >= d) {
this.traveled = 0;
// We've reached the next point, add coordinates to array
//console.log(this.verts[0]+' reached vertex '+(this.linked.length+1)+' of '+this.verts.length);
this.linked.push(this.verts[this.linked.length]);
p = particles[this.linked[this.linked.length-1]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
if (this.linked.length >= this.verts.length) {
//console.log(this.verts[0]+' moving to stage 2 (1)');
this.stage = 2;
}
}
else {
// We're still travelling to the next point, get coordinates at travel distance
// http://math.stackexchange.com/a/85582
var a = particles[this.linked[this.linked.length-1]],
b = particles[this.verts[this.linked.length]],
t = d - this.traveled,
x = ((this.traveled * b.x) + (t * a.x)) / d,
y = ((this.traveled * b.y) + (t * a.y)) / d,
z = ((this.traveled * b.z) + (t * a.z)) / d;
pos = position(x, y, z);
//console.log(this.verts[0]+' traveling to vertex '+(this.linked.length+1)+' of '+this.verts.length+' ('+this.traveled+' of '+this.distances[this.linked.length]+')');
points.push([pos.x, pos.y]);
}
this.drawLine(points);
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (1)');
this.stage = 3;
this.finished = true;
}
break;
// FADE OUT STAGE
case 2:
if (this.verts.length > 1) {
if (this.fade < linkFade) {
this.fade++;
// Render full link between all vertices and fade over time
points = [];
var alpha = (1 - (this.fade / linkFade)) * linkOpacity;
for (i = 0; i < this.verts.length; i++) {
p = particles[this.verts[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
this.drawLine(points, alpha);
}
else {
//console.log(this.verts[0]+' moving to stage 3 (2a)');
this.stage = 3;
this.finished = true;
}
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (2b)');
this.stage = 3;
this.finished = true;
}
break;
// FINISHED STAGE
case 3:
default:
this.finished = true;
break;
}
};
Link.prototype.drawLine = function(points, alpha) {
if (typeof alpha !== 'number') alpha = linkOpacity;
if (points.length > 1 && alpha > 0) {
//console.log(this.verts[0]+': Drawing line '+alpha);
context.globalAlpha = alpha;
context.beginPath();
for (var i = 0; i < points.length-1; i++) {
context.moveTo(points[i][0], points[i][1]);
context.lineTo(points[i+1][0], points[i+1][1]);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
context.globalAlpha = 1;
}
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
//value = simplex.noise2D(nScale * cosA + nScale, nScale * sinA + nScale),
//rad = nRad + value;
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #31102F;
background: radial-gradient(ellipse at center, #31102f 0%, #280b29 100%);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 16rem;
height: 100vh;
z-index: 1;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<canvas id="stars" width="300" height="300"></canvas>
So I am not sure if my question is more of a programming question, more of a pure math question, or equally both.
Short version: I have a rotating sphere in webgl, but it isn't rotating the way I want it to. I think what I want is to rotate the sphere about an axis, and then rotate that axis about a second axis.
So I have downloaded and tweaked some javascript webgl code that renders an icosahedron and can animate its rotation along x, y, or z axes, either individually or simultaneously in a composite rotation group. I have some sense of how it is working.
I've also read (such as here) that any rotation group of Euler angles actually just yields some new, net, single axis of rotation. Which means I can't get the animation I want by just using the rotations of those three axes.
So I think what I need is a new kind of rotation, that rotates the axis of rotation itself. But I am at a loss as for how to apply that to the matrix transformation.
Would that be implemented as a separate transformation?
Would it be included into the geometry of the y axis rotation?
Would it be easier to implement by rotating the camera around the x axis?
Should I cross-post this to math stack exchange for help with the pure math?
I would appreciate any help, whether high level conceptual suggestions or detailed code suggestions.
Below is the working snippet I'm currently using. It is rotating about the y axis, and I'd like that axis of rotation to rotate around the x axis. But that's not the same as just doing a rotation group around the x and y axis simultaneously - that just yields a single, composite rotation axis of the line y = x.
var App = (function () {
function App(canvas) {
this._canvas = canvas;
this._ctx = canvas.getContext('webgl');
this._ctx.viewport(0, 0, canvas.width, canvas.height);
this._canvas.setAttribute('width', this._canvas.clientWidth.toString());
this._canvas.setAttribute('height', this._canvas.clientHeight.toString());
this._config =
{
DrawMode: this._ctx.TRIANGLES,
Quality: 3,
ZoomLevel: -4,
Rotation: {
X: 0.0000,
Y: 0.0100,
Z: 0.0000
}
};
}
App.prototype._setData = function () {
var ctx = this._ctx;
var icosahedron = new Icosahedron3D(this._config.Quality);
this._vertices = icosahedron.Points.reduce(function (a, b, i) { return i === 1 ? [a.x, a.y, a.z, b.x, b.y, b.z] : a.concat([b.x, b.y, b.z]); });
this._indices = icosahedron.TriangleIndices;
this._colors = this._generateColors(this._vertices);
var vertex_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, vertex_buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(this._vertices), ctx.STATIC_DRAW);
var color_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, color_buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(this._colors), ctx.STATIC_DRAW);
var index_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, index_buffer);
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(this._indices), ctx.STATIC_DRAW);
return {
vertex: vertex_buffer,
color: color_buffer,
index: index_buffer
};
};
App.prototype._generateColors = function (vertices) {
var colors = [];
for (var i = 0; i < vertices.length; i += 3) {
var cvalue = 0;
var testvalue = 0;
if (vertices[i] >= 0) testvalue++;
if (vertices[i+1] >= 0) testvalue++;
if (vertices[i+2] >= 0) testvalue++;
else testvalue = 0;
if (testvalue > 0) cvalue = 1;
colors.push(cvalue);
colors.push(cvalue);
colors.push(cvalue);
}
return colors;
}
App.prototype._animate = function (proj_matrix, view_matrix, mov_matrix) {
var _this = this;
var ctx = this._ctx;
var rotThetas = this._config.Rotation;
var time_old = 0;
var zoomLevel_old = 0;
var execAnimation = function (time) {
var dt = time - time_old;
time_old = time;
for (var axis in rotThetas) {
var theta = rotThetas[axis];
if (theta > 0.0 || theta < 0.0) {
Matrix[("Rotate" + axis)](mov_matrix, dt * theta);
}
}
if (Math.abs(_this._config.ZoomLevel - zoomLevel_old) >= 0.01) {
view_matrix[14] = view_matrix[14] + (zoomLevel_old * -1) + _this._config.ZoomLevel;
zoomLevel_old = _this._config.ZoomLevel;
}
ctx.enable(ctx.DEPTH_TEST);
ctx.depthFunc(ctx.LEQUAL);
ctx.clearDepth(1.0);
ctx.viewport(0.0, 0.0, _this._canvas.width, _this._canvas.height);
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT);
ctx.uniformMatrix4fv(_this._shader.Pmatrix, false, proj_matrix);
ctx.uniformMatrix4fv(_this._shader.Vmatrix, false, view_matrix);
ctx.uniformMatrix4fv(_this._shader.Mmatrix, false, mov_matrix);
ctx.drawElements(_this._config.DrawMode, _this._indices.length, ctx.UNSIGNED_SHORT, 0);
window.requestAnimationFrame(execAnimation);
};
execAnimation(0);
};
App.prototype.Draw = function () {
var buffers = this._setData();
this._shader = App.UseQuarternionShaderProgram(this._ctx, buffers.vertex, buffers.color);
var proj_matrix = new Float32Array(Matrix.GetProjection(40, this._canvas.width / this._canvas.height, 1, 100));
var view_matrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
var mov_matrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
this._animate(proj_matrix, view_matrix, mov_matrix);
};
App.UseQuarternionVertShader = function (context) {
var vertCode = "\n\t\t\tattribute vec3 position;\n\t\t\tattribute highp vec3 aVertexNormal;\n\t\t\t\n\t\t\tuniform mat4 Pmatrix;\n\t\t\tuniform mat4 Vmatrix;\n\t\t\tuniform mat4 Mmatrix;\n\n\t\t\tattribute vec4 color;\n\t\t\tvarying lowp vec4 vColor;\n\n\t\t\tvarying vec3 vLightWeighting;\n\t\t\t\n\t\t\tuniform vec3 uAmbientColor;\n\t\t\tuniform vec3 uPointLightingLocation;\n\t\t\tuniform vec3 uPointLightingColor;\n\n\t\t\tvoid main(void) {\n\t\t\t\tvec4 mvPosition = Mmatrix * vec4(position, 1.);\n\t\t\t\tgl_Position = Pmatrix*Vmatrix*mvPosition;\n\t\t\t\tgl_PointSize = 4.0;\n\t\t\t\tvColor = color;\n\n\t\t\t\tvec3 lightDirection = normalize(uPointLightingLocation - mvPosition.xyz);\n\t\t\t\tvec3 transformedNormal = vec3(Vmatrix) * aVertexNormal;\n\t\t\t\tfloat directionalLightWeighting = max(dot(transformedNormal, lightDirection), 0.0);\n\t\t\t\tvLightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;\n\t\t\t}";
var vertShader = context.createShader(context.VERTEX_SHADER);
context.shaderSource(vertShader, vertCode);
context.compileShader(vertShader);
return vertShader;
};
App.UseVariableFragShader = function (context) {
var fragCode = "\n\t\t\tprecision mediump float;\n\t\t\tvarying lowp vec4 vColor;\n\t\t\tvarying vec3 vLightWeighting;\n\t\t\tvoid main(void) {\n\t\t\t\tgl_FragColor = vec4(vColor.rgb, 1.);\n\t\t\t}";
var fragShader = context.createShader(context.FRAGMENT_SHADER);
context.shaderSource(fragShader, fragCode);
context.compileShader(fragShader);
return fragShader;
};
App.UseQuarternionShaderProgram = function (ctx, vertex_buffer, color_buffer) {
var vertShader = App.UseQuarternionVertShader(ctx);
var fragShader = App.UseVariableFragShader(ctx);
var shaderProgram = ctx.createProgram();
ctx.attachShader(shaderProgram, vertShader);
ctx.attachShader(shaderProgram, fragShader);
ctx.linkProgram(shaderProgram);
var Pmatrix = ctx.getUniformLocation(shaderProgram, "Pmatrix");
var Vmatrix = ctx.getUniformLocation(shaderProgram, "Vmatrix");
var Mmatrix = ctx.getUniformLocation(shaderProgram, "Mmatrix");
ctx.bindBuffer(ctx.ARRAY_BUFFER, vertex_buffer);
var position = ctx.getAttribLocation(shaderProgram, "position");
ctx.vertexAttribPointer(position, 3, ctx.FLOAT, false, 0, 0);
ctx.enableVertexAttribArray(position);
ctx.bindBuffer(ctx.ARRAY_BUFFER, color_buffer);
var color = ctx.getAttribLocation(shaderProgram, "color");
ctx.vertexAttribPointer(color, 3, ctx.FLOAT, false, 0, 0);
ctx.enableVertexAttribArray(color);
ctx.useProgram(shaderProgram);
var ambientColor = ctx.getUniformLocation(shaderProgram, "uAmbientColor");
var pointLightingLocation = ctx.getUniformLocation(shaderProgram, "uPointLightingLocation");
var pointLightingColor = ctx.getUniformLocation(shaderProgram, "uPointLightingColor");
ctx.uniform3f(ambientColor, 0.2, 0.2, 0.2);
ctx.uniform3f(pointLightingLocation, 0.0, 0.0, -20.0);
ctx.uniform3f(pointLightingColor, 0.8, 0.8, 0.8);
return {
Pmatrix: Pmatrix,
Vmatrix: Vmatrix,
Mmatrix: Mmatrix,
ShaderProgram: shaderProgram
};
};
return App;
})();
var Matrix = (function () {
function Matrix() {
}
Matrix.GetProjection = function (angle, a, zMin, zMax) {
var ang = Math.tan((angle * .5) * Math.PI / 180);
return [
0.5 / ang, 0, 0, 0,
0, 0.5 * a / ang, 0, 0,
0, 0, -(zMax + zMin) / (zMax - zMin), -1,
0, 0, (-2 * zMax * zMin) / (zMax - zMin), 0
];
};
Matrix.RotateX = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv1 = m[1], mv5 = m[5], mv9 = m[9];
m[1] = m[1] * c - m[2] * s;
m[5] = m[5] * c - m[6] * s;
m[9] = m[9] * c - m[10] * s;
m[2] = m[2] * c + mv1 * s;
m[6] = m[6] * c + mv5 * s;
m[10] = m[10] * c + mv9 * s;
};
Matrix.RotateY = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c * m[0] + s * m[2];
m[4] = c * m[4] + s * m[6];
m[8] = c * m[8] + s * m[10];
m[2] = c * m[2] - s * mv0;
m[6] = c * m[6] - s * mv4;
m[10] = c * m[10] - s * mv8;
};
Matrix.RotateZ = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c * m[0] - s * m[1];
m[4] = c * m[4] - s * m[5];
m[8] = c * m[8] - s * m[9];
m[1] = c * m[1] + s * mv0;
m[5] = c * m[5] + s * mv4;
m[9] = c * m[9] + s * mv8;
};
Matrix.Translate = function (a, b, c) {
var d = b[0], e = b[1], s = b[2];
if (!c || a == c) {
a[12] = a[0] * d + a[4] * e + a[8] * s + a[12];
a[13] = a[1] * d + a[5] * e + a[9] * s + a[13];
a[14] = a[2] * d + a[6] * e + a[10] * s + a[14];
a[15] = a[3] * d + a[7] * e + a[11] * s + a[15];
return a;
}
var g = a[0], f = a[1], h = a[2], i = a[3], j = a[4], k = a[5], l = a[6], o = a[7], m = a[8], n = a[9], p = a[10], r = a[11];
c[0] = g;
c[1] = f;
c[2] = h;
c[3] = i;
c[4] = j;
c[5] = k;
c[6] = l;
c[7] = o;
c[8] = m;
c[9] = n;
c[10] = p;
c[11] = r;
c[12] = g * d + j * e + m * s + a[12];
c[13] = f * d + k * e + n * s + a[13];
c[14] = h * d + l * e + p * s + a[14];
c[15] = i * d + o * e + r * s + a[15];
return c;
};
;
return Matrix;
})();
var Icosahedron3D = (function () {
function Icosahedron3D(quality) {
this._quality = quality;
this._calculateGeometry();
}
Icosahedron3D.prototype._calculateGeometry = function () {
this.Points = [];
this.TriangleIndices = [];
this._middlePointIndexCache = {};
this._index = 0;
var t = (1.0 + Math.sqrt(5.0)) / 2.0;
this._addVertex(-1, t, 0);
this._addVertex(1, t, 0);
this._addVertex(-1, -t, 0);
this._addVertex(1, -t, 0);
this._addVertex(0, -1, t);
this._addVertex(0, 1, t);
this._addVertex(0, -1, -t);
this._addVertex(0, 1, -t);
this._addVertex(t, 0, -1);
this._addVertex(t, 0, 1);
this._addVertex(-t, 0, -1);
this._addVertex(-t, 0, 1);
this._addFace(0, 11, 5);
this._addFace(0, 5, 1);
this._addFace(0, 1, 7);
this._addFace(0, 7, 10);
this._addFace(0, 10, 11);
this._addFace(1, 5, 9);
this._addFace(5, 11, 4);
this._addFace(11, 10, 2);
this._addFace(10, 7, 6);
this._addFace(7, 1, 8);
this._addFace(3, 9, 4);
this._addFace(3, 4, 2);
this._addFace(3, 2, 6);
this._addFace(3, 6, 8);
this._addFace(3, 8, 9);
this._addFace(4, 9, 5);
this._addFace(2, 4, 11);
this._addFace(6, 2, 10);
this._addFace(8, 6, 7);
this._addFace(9, 8, 1);
this._refineVertices();
};
Icosahedron3D.prototype._addVertex = function (x, y, z) {
var length = Math.sqrt(x * x + y * y + z * z);
this.Points.push({
x: x / length,
y: y / length,
z: z / length
});
return this._index++;
};
Icosahedron3D.prototype._addFace = function (x, y, z) {
this.TriangleIndices.push(x);
this.TriangleIndices.push(y);
this.TriangleIndices.push(z);
};
Icosahedron3D.prototype._refineVertices = function () {
for (var i = 0; i < this._quality; i++) {
var faceCount = this.TriangleIndices.length;
for (var face = 0; face < faceCount; face += 3) {
var x1 = this.TriangleIndices[face];
var y1 = this.TriangleIndices[face + 1];
var z1 = this.TriangleIndices[face + 2];
var x2 = this._getMiddlePoint(x1, y1);
var y2 = this._getMiddlePoint(y1, z1);
var z2 = this._getMiddlePoint(z1, x1);
this._addFace(x1, x2, z2);
this._addFace(y1, y2, x2);
this._addFace(z1, z2, y2);
this._addFace(x2, y2, z2);
}
}
};
Icosahedron3D.prototype._getMiddlePoint = function (p1, p2) {
var firstIsSmaller = p1 < p2;
var smallerIndex = firstIsSmaller ? p1 : p2;
var greaterIndex = firstIsSmaller ? p2 : p1;
var key = (smallerIndex << 32) + greaterIndex;
var p = this._middlePointIndexCache[key];
if (p !== undefined)
p;
var point1 = this.Points[p1];
var point2 = this.Points[p2];
var middle = {
x: (point1.x + point2.x) / 2.0,
y: (point1.y + point2.y) / 2.0,
z: (point1.z + point2.z) / 2.0,
};
var i = this._addVertex(middle.x, middle.y, middle.z);
this._middlePointIndexCache[key] = i;
return i;
};
return Icosahedron3D;
})();
(function () {
var app = new App(document.getElementById('canvas'));
app.Draw();
})();
<body style="background-color: rgb(55,55,55);">
<canvas id="canvas" style="position: absolute;top:0;left:0;width:100%;height:100%;" />
</body>
So the simplest solution seems to be the notion of moving the view instead of the object.
To do this, I keep the existing rotation axis information which is applied to the mov matrix unchanged, but added a new triple to provide rotation axis information to be applied to the view matrix:
var App = (function () {
function App(canvas) {
this._canvas = canvas;
this._ctx = canvas.getContext('webgl');
this._ctx.viewport(0, 0, canvas.width, canvas.height);
this._canvas.setAttribute('width', this._canvas.clientWidth.toString());
this._canvas.setAttribute('height', this._canvas.clientHeight.toString());
this._config =
{
DrawMode: this._ctx.TRIANGLES,
Quality: 3,
ZoomLevel: -4,
MovRotation: {
X: 0.00000,
Y: 0.01000,
Z: 0.00000
},
ViewRotation: {
X: 0.00100,
Y: 0.00000,
Z: 0.00000
}
};
}
App.prototype._setData = function () {
var ctx = this._ctx;
var icosahedron = new Icosahedron3D(this._config.Quality);
this._vertices = icosahedron.Points.reduce(function (a, b, i) { return i === 1 ? [a.x, a.y, a.z, b.x, b.y, b.z] : a.concat([b.x, b.y, b.z]); });
this._indices = icosahedron.TriangleIndices;
this._colors = this._generateColors(this._vertices);
var vertex_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, vertex_buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(this._vertices), ctx.STATIC_DRAW);
var color_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, color_buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(this._colors), ctx.STATIC_DRAW);
var index_buffer = ctx.createBuffer();
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, index_buffer);
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(this._indices), ctx.STATIC_DRAW);
return {
vertex: vertex_buffer,
color: color_buffer,
index: index_buffer
};
};
App.prototype._generateColors = function (vertices) {
var colors = [];
for (var i = 0; i < vertices.length; i += 3) {
var cvalue = 0;
var testvalue = 0;
if (vertices[i] >= 0) testvalue++;
if (vertices[i+1] >= 0) testvalue++;
if (vertices[i+2] >= 0) testvalue++;
else testvalue = 0;
if (testvalue > 0) cvalue = 1;
colors.push(cvalue);
colors.push(cvalue);
colors.push(cvalue);
}
return colors;
}
App.prototype._animate = function (proj_matrix, view_matrix, mov_matrix) {
var _this = this;
var ctx = this._ctx;
var movThetas = this._config.MovRotation;
var viewThetas = this._config.ViewRotation
var time_old = 0;
var zoomLevel_old = 0;
var execAnimation = function (time) {
var dt = time - time_old;
time_old = time;
for (var m_axis in movThetas) {
var theta = movThetas[m_axis];
if (theta > 0.0 || theta < 0.0) {
Matrix[("Rotate" + m_axis)](mov_matrix, dt * theta);
}
}
for (var v_axis in viewThetas) {
var theta = viewThetas[v_axis];
if (theta > 0.0 || theta < 0.0) {
Matrix[("Rotate" + v_axis)](view_matrix, dt * theta);
}
}
if (Math.abs(_this._config.ZoomLevel - zoomLevel_old) >= 0.01) {
view_matrix[14] = view_matrix[14] + (zoomLevel_old * -1) + _this._config.ZoomLevel;
zoomLevel_old = _this._config.ZoomLevel;
}
ctx.enable(ctx.DEPTH_TEST);
ctx.depthFunc(ctx.LEQUAL);
ctx.clearDepth(1.0);
ctx.viewport(0.0, 0.0, _this._canvas.width, _this._canvas.height);
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT);
ctx.uniformMatrix4fv(_this._shader.Pmatrix, false, proj_matrix);
ctx.uniformMatrix4fv(_this._shader.Vmatrix, false, view_matrix);
ctx.uniformMatrix4fv(_this._shader.Mmatrix, false, mov_matrix);
ctx.drawElements(_this._config.DrawMode, _this._indices.length, ctx.UNSIGNED_SHORT, 0);
window.requestAnimationFrame(execAnimation);
};
execAnimation(0);
};
App.prototype.Draw = function () {
var buffers = this._setData();
this._shader = App.UseQuarternionShaderProgram(this._ctx, buffers.vertex, buffers.color);
var proj_matrix = new Float32Array(Matrix.GetProjection(40, this._canvas.width / this._canvas.height, 1, 100));
var view_matrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
var mov_matrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
this._animate(proj_matrix, view_matrix, mov_matrix);
};
App.UseQuarternionVertShader = function (context) {
var vertCode = "\n\t\t\tattribute vec3 position;\n\t\t\tattribute highp vec3 aVertexNormal;\n\t\t\t\n\t\t\tuniform mat4 Pmatrix;\n\t\t\tuniform mat4 Vmatrix;\n\t\t\tuniform mat4 Mmatrix;\n\n\t\t\tattribute vec4 color;\n\t\t\tvarying lowp vec4 vColor;\n\n\t\t\tvarying vec3 vLightWeighting;\n\t\t\t\n\t\t\tuniform vec3 uAmbientColor;\n\t\t\tuniform vec3 uPointLightingLocation;\n\t\t\tuniform vec3 uPointLightingColor;\n\n\t\t\tvoid main(void) {\n\t\t\t\tvec4 mvPosition = Mmatrix * vec4(position, 1.);\n\t\t\t\tgl_Position = Pmatrix*Vmatrix*mvPosition;\n\t\t\t\tgl_PointSize = 4.0;\n\t\t\t\tvColor = color;\n\n\t\t\t\tvec3 lightDirection = normalize(uPointLightingLocation - mvPosition.xyz);\n\t\t\t\tvec3 transformedNormal = vec3(Vmatrix) * aVertexNormal;\n\t\t\t\tfloat directionalLightWeighting = max(dot(transformedNormal, lightDirection), 0.0);\n\t\t\t\tvLightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;\n\t\t\t}";
var vertShader = context.createShader(context.VERTEX_SHADER);
context.shaderSource(vertShader, vertCode);
context.compileShader(vertShader);
return vertShader;
};
App.UseVariableFragShader = function (context) {
var fragCode = "\n\t\t\tprecision mediump float;\n\t\t\tvarying lowp vec4 vColor;\n\t\t\tvarying vec3 vLightWeighting;\n\t\t\tvoid main(void) {\n\t\t\t\tgl_FragColor = vec4(vColor.rgb, 1.);\n\t\t\t}";
var fragShader = context.createShader(context.FRAGMENT_SHADER);
context.shaderSource(fragShader, fragCode);
context.compileShader(fragShader);
return fragShader;
};
App.UseQuarternionShaderProgram = function (ctx, vertex_buffer, color_buffer) {
var vertShader = App.UseQuarternionVertShader(ctx);
var fragShader = App.UseVariableFragShader(ctx);
var shaderProgram = ctx.createProgram();
ctx.attachShader(shaderProgram, vertShader);
ctx.attachShader(shaderProgram, fragShader);
ctx.linkProgram(shaderProgram);
var Pmatrix = ctx.getUniformLocation(shaderProgram, "Pmatrix");
var Vmatrix = ctx.getUniformLocation(shaderProgram, "Vmatrix");
var Mmatrix = ctx.getUniformLocation(shaderProgram, "Mmatrix");
ctx.bindBuffer(ctx.ARRAY_BUFFER, vertex_buffer);
var position = ctx.getAttribLocation(shaderProgram, "position");
ctx.vertexAttribPointer(position, 3, ctx.FLOAT, false, 0, 0);
ctx.enableVertexAttribArray(position);
ctx.bindBuffer(ctx.ARRAY_BUFFER, color_buffer);
var color = ctx.getAttribLocation(shaderProgram, "color");
ctx.vertexAttribPointer(color, 3, ctx.FLOAT, false, 0, 0);
ctx.enableVertexAttribArray(color);
ctx.useProgram(shaderProgram);
var ambientColor = ctx.getUniformLocation(shaderProgram, "uAmbientColor");
var pointLightingLocation = ctx.getUniformLocation(shaderProgram, "uPointLightingLocation");
var pointLightingColor = ctx.getUniformLocation(shaderProgram, "uPointLightingColor");
ctx.uniform3f(ambientColor, 0.2, 0.2, 0.2);
ctx.uniform3f(pointLightingLocation, 0.0, 0.0, -20.0);
ctx.uniform3f(pointLightingColor, 0.8, 0.8, 0.8);
return {
Pmatrix: Pmatrix,
Vmatrix: Vmatrix,
Mmatrix: Mmatrix,
ShaderProgram: shaderProgram
};
};
return App;
})();
var Matrix = (function () {
function Matrix() {
}
Matrix.GetProjection = function (angle, a, zMin, zMax) {
var ang = Math.tan((angle * .5) * Math.PI / 180);
return [
0.5 / ang, 0, 0, 0,
0, 0.5 * a / ang, 0, 0,
0, 0, -(zMax + zMin) / (zMax - zMin), -1,
0, 0, (-2 * zMax * zMin) / (zMax - zMin), 0
];
};
Matrix.RotateX = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv1 = m[1], mv5 = m[5], mv9 = m[9];
m[1] = m[1] * c - m[2] * s;
m[5] = m[5] * c - m[6] * s;
m[9] = m[9] * c - m[10] * s;
m[2] = m[2] * c + mv1 * s;
m[6] = m[6] * c + mv5 * s;
m[10] = m[10] * c + mv9 * s;
};
Matrix.RotateY = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c * m[0] + s * m[2];
m[4] = c * m[4] + s * m[6];
m[8] = c * m[8] + s * m[10];
m[2] = c * m[2] - s * mv0;
m[6] = c * m[6] - s * mv4;
m[10] = c * m[10] - s * mv8;
};
Matrix.RotateZ = function (m, angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
var mv0 = m[0], mv4 = m[4], mv8 = m[8];
m[0] = c * m[0] - s * m[1];
m[4] = c * m[4] - s * m[5];
m[8] = c * m[8] - s * m[9];
m[1] = c * m[1] + s * mv0;
m[5] = c * m[5] + s * mv4;
m[9] = c * m[9] + s * mv8;
};
Matrix.Translate = function (a, b, c) {
var d = b[0], e = b[1], s = b[2];
if (!c || a == c) {
a[12] = a[0] * d + a[4] * e + a[8] * s + a[12];
a[13] = a[1] * d + a[5] * e + a[9] * s + a[13];
a[14] = a[2] * d + a[6] * e + a[10] * s + a[14];
a[15] = a[3] * d + a[7] * e + a[11] * s + a[15];
return a;
}
var g = a[0], f = a[1], h = a[2], i = a[3], j = a[4], k = a[5], l = a[6], o = a[7], m = a[8], n = a[9], p = a[10], r = a[11];
c[0] = g;
c[1] = f;
c[2] = h;
c[3] = i;
c[4] = j;
c[5] = k;
c[6] = l;
c[7] = o;
c[8] = m;
c[9] = n;
c[10] = p;
c[11] = r;
c[12] = g * d + j * e + m * s + a[12];
c[13] = f * d + k * e + n * s + a[13];
c[14] = h * d + l * e + p * s + a[14];
c[15] = i * d + o * e + r * s + a[15];
return c;
};
;
return Matrix;
})();
var Icosahedron3D = (function () {
function Icosahedron3D(quality) {
this._quality = quality;
this._calculateGeometry();
}
Icosahedron3D.prototype._calculateGeometry = function () {
this.Points = [];
this.TriangleIndices = [];
this._middlePointIndexCache = {};
this._index = 0;
var t = (1.0 + Math.sqrt(5.0)) / 2.0;
this._addVertex(-1, t, 0);
this._addVertex(1, t, 0);
this._addVertex(-1, -t, 0);
this._addVertex(1, -t, 0);
this._addVertex(0, -1, t);
this._addVertex(0, 1, t);
this._addVertex(0, -1, -t);
this._addVertex(0, 1, -t);
this._addVertex(t, 0, -1);
this._addVertex(t, 0, 1);
this._addVertex(-t, 0, -1);
this._addVertex(-t, 0, 1);
this._addFace(0, 11, 5);
this._addFace(0, 5, 1);
this._addFace(0, 1, 7);
this._addFace(0, 7, 10);
this._addFace(0, 10, 11);
this._addFace(1, 5, 9);
this._addFace(5, 11, 4);
this._addFace(11, 10, 2);
this._addFace(10, 7, 6);
this._addFace(7, 1, 8);
this._addFace(3, 9, 4);
this._addFace(3, 4, 2);
this._addFace(3, 2, 6);
this._addFace(3, 6, 8);
this._addFace(3, 8, 9);
this._addFace(4, 9, 5);
this._addFace(2, 4, 11);
this._addFace(6, 2, 10);
this._addFace(8, 6, 7);
this._addFace(9, 8, 1);
this._refineVertices();
};
Icosahedron3D.prototype._addVertex = function (x, y, z) {
var length = Math.sqrt(x * x + y * y + z * z);
this.Points.push({
x: x / length,
y: y / length,
z: z / length
});
return this._index++;
};
Icosahedron3D.prototype._addFace = function (x, y, z) {
this.TriangleIndices.push(x);
this.TriangleIndices.push(y);
this.TriangleIndices.push(z);
};
Icosahedron3D.prototype._refineVertices = function () {
for (var i = 0; i < this._quality; i++) {
var faceCount = this.TriangleIndices.length;
for (var face = 0; face < faceCount; face += 3) {
var x1 = this.TriangleIndices[face];
var y1 = this.TriangleIndices[face + 1];
var z1 = this.TriangleIndices[face + 2];
var x2 = this._getMiddlePoint(x1, y1);
var y2 = this._getMiddlePoint(y1, z1);
var z2 = this._getMiddlePoint(z1, x1);
this._addFace(x1, x2, z2);
this._addFace(y1, y2, x2);
this._addFace(z1, z2, y2);
this._addFace(x2, y2, z2);
}
}
};
Icosahedron3D.prototype._getMiddlePoint = function (p1, p2) {
var firstIsSmaller = p1 < p2;
var smallerIndex = firstIsSmaller ? p1 : p2;
var greaterIndex = firstIsSmaller ? p2 : p1;
var key = (smallerIndex << 32) + greaterIndex;
var p = this._middlePointIndexCache[key];
if (p !== undefined)
p;
var point1 = this.Points[p1];
var point2 = this.Points[p2];
var middle = {
x: (point1.x + point2.x) / 2.0,
y: (point1.y + point2.y) / 2.0,
z: (point1.z + point2.z) / 2.0,
};
var i = this._addVertex(middle.x, middle.y, middle.z);
this._middlePointIndexCache[key] = i;
return i;
};
return Icosahedron3D;
})();
(function () {
var app = new App(document.getElementById('canvas'));
app.Draw();
})();
<body style="background-color: rgb(55,55,55);">
<canvas id="canvas" style="position: absolute;top:0;left:0;width:100%;height:100%;" />
</body>
I'm trying to give each of the falling objects a gradient to make them look like shiny gold tickets.
I have a Codepen
I forked the pen from another repo and all i've changed in the background colour and the ticket colour.
This is the part of the code that control the colour. How do I add the gradient?
var colorThemes = [
function() {
//return color(200 * random()|0, 200 * random()|0, 200 * random()|0);
return color(218,165,32);
}, function() {
var black = 200 * random()|0; return color(200, black, black);
}, function() {
var black = 200 * random()|0; return color(black, 200, black);
}, function() {
var black = 200 * random()|0; return color(black, black, 200);
}, function() {
return color(200, 100, 200 * random()|0);
}, function() {
return color(200 * random()|0, 200, 200);
}, function() {
var black = 256 * random()|0; return color(black, black, black);
}, function() {
return colorThemes[random() < .5 ? 1 : 2]();
}, function() {
return colorThemes[random() < .5 ? 3 : 5]();
}, function() {
return colorThemes[random() < .5 ? 2 : 4]();
}
];
function color(r, g, b) {
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
'use strict';
// If set to true, the user must press
// UP UP DOWN ODWN LEFT RIGHT LEFT RIGHT A B
// to trigger the confetti with a random color theme.
// Otherwise the confetti constantly falls.
var onlyOnKonami = false;
$(function() {
// Globals
var $window = $(window)
, random = Math.random
, cos = Math.cos
, sin = Math.sin
, PI = Math.PI
, PI2 = PI * 2
, timer = undefined
, frame = undefined
, confetti = [];
// Settings
var konami = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
, pointer = 0;
var particles = 150
, spread = 10
, sizeMin = 3
, sizeMax = 40 - sizeMin
, eccentricity = 10
, deviation = 100
, dxThetaMin = -.1
, dxThetaMax = -dxThetaMin - dxThetaMin
, dyMin = .13
, dyMax = .18
, dThetaMin = .4
, dThetaMax = .7 - dThetaMin;
var colorThemes = [
function() {
//return color(200 * random()|0, 200 * random()|0, 200 * random()|0);
return color(218,165,32);
}, function() {
var black = 200 * random()|0; return color(200, black, black);
}, function() {
var black = 200 * random()|0; return color(black, 200, black);
}, function() {
var black = 200 * random()|0; return color(black, black, 200);
}, function() {
return color(200, 100, 200 * random()|0);
}, function() {
return color(200 * random()|0, 200, 200);
}, function() {
var black = 256 * random()|0; return color(black, black, black);
}, function() {
return colorThemes[random() < .5 ? 1 : 2]();
}, function() {
return colorThemes[random() < .5 ? 3 : 5]();
}, function() {
return colorThemes[random() < .5 ? 2 : 4]();
}
];
function color(r, g, b) {
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
// Cosine interpolation
function interpolation(a, b, t) {
return (1-cos(PI*t))/2 * (b-a) + a;
}
// Create a 1D Maximal Poisson Disc over [0, 1]
var radius = 1/eccentricity, radius2 = radius+radius;
function createPoisson() {
// domain is the set of points which are still available to pick from
// D = union{ [d_i, d_i+1] | i is even }
var domain = [radius, 1-radius], measure = 1-radius2, spline = [0, 1];
while (measure) {
var dart = measure * random(), i, l, interval, a, b, c, d;
// Find where dart lies
for (i = 0, l = domain.length, measure = 0; i < l; i += 2) {
a = domain[i], b = domain[i+1], interval = b-a;
if (dart < measure+interval) {
spline.push(dart += a-measure);
break;
}
measure += interval;
}
c = dart-radius, d = dart+radius;
// Update the domain
for (i = domain.length-1; i > 0; i -= 2) {
l = i-1, a = domain[l], b = domain[i];
// c---d c---d Do nothing
// c-----d c-----d Move interior
// c--------------d Delete interval
// c--d Split interval
// a------b
if (a >= c && a < d)
if (b > d) domain[l] = d; // Move interior (Left case)
else domain.splice(l, 2); // Delete interval
else if (a < c && b > c)
if (b <= d) domain[i] = c; // Move interior (Right case)
else domain.splice(i, 0, c, d); // Split interval
}
// Re-measure the domain
for (i = 0, l = domain.length, measure = 0; i < l; i += 2)
measure += domain[i+1]-domain[i];
}
return spline.sort();
}
// Create the overarching container
var container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.height = '0';
container.style.overflow = 'visible';
container.style.zIndex = '9999';
// Confetto constructor
function Confetto(theme) {
this.frame = 0;
this.outer = document.createElement('div');
this.inner = document.createElement('div');
this.outer.appendChild(this.inner);
var outerStyle = this.outer.style, innerStyle = this.inner.style;
outerStyle.position = 'absolute';
outerStyle.width = (sizeMin + sizeMax * random()) + 'px';
outerStyle.height = (sizeMin + sizeMax * random()) + 'px';
innerStyle.width = '100%';
innerStyle.height = '100%';
innerStyle.backgroundColor = theme();
outerStyle.perspective = '100px';
outerStyle.transform = 'rotate(' + (360 * random()) + 'deg)';
this.axis = 'rotate3D(' +
cos(360 * random()) + ',' +
cos(360 * random()) + ',0,';
this.theta = 360 * random();
this.dTheta = dThetaMin + dThetaMax * random();
innerStyle.transform = this.axis + this.theta + 'deg)';
this.x = $window.width() * random();
this.y = -deviation;
this.dx = sin(dxThetaMin + dxThetaMax * random());
this.dy = dyMin + dyMax * random();
outerStyle.left = this.x + 'px';
outerStyle.top = this.y + 'px';
// Create the periodic spline
this.splineX = createPoisson();
this.splineY = [];
for (var i = 1, l = this.splineX.length-1; i < l; ++i)
this.splineY[i] = deviation * random();
this.splineY[0] = this.splineY[l] = deviation * random();
this.update = function(height, delta) {
this.frame += delta;
this.x += this.dx * delta;
this.y += this.dy * delta;
this.theta += this.dTheta * delta;
// Compute spline and convert to polar
var phi = this.frame % 7777 / 7777, i = 0, j = 1;
while (phi >= this.splineX[j]) i = j++;
var rho = interpolation(
this.splineY[i],
this.splineY[j],
(phi-this.splineX[i]) / (this.splineX[j]-this.splineX[i])
);
phi *= PI2;
outerStyle.left = this.x + rho * cos(phi) + 'px';
outerStyle.top = this.y + rho * sin(phi) + 'px';
innerStyle.transform = this.axis + this.theta + 'deg)';
return this.y > height+deviation;
};
}
function poof() {
if (!frame) {
// Append the container
document.body.appendChild(container);
// Add confetti
var theme = colorThemes[onlyOnKonami ? colorThemes.length * random()|0 : 0]
, count = 0;
(function addConfetto() {
if (onlyOnKonami && ++count > particles)
return timer = undefined;
var confetto = new Confetto(theme);
confetti.push(confetto);
container.appendChild(confetto.outer);
timer = setTimeout(addConfetto, spread * random());
})(0);
// Start the loop
var prev = undefined;
requestAnimationFrame(function loop(timestamp) {
var delta = prev ? timestamp - prev : 0;
prev = timestamp;
var height = $window.height();
for (var i = confetti.length-1; i >= 0; --i) {
if (confetti[i].update(height, delta)) {
container.removeChild(confetti[i].outer);
confetti.splice(i, 1);
}
}
if (timer || confetti.length)
return frame = requestAnimationFrame(loop);
// Cleanup
document.body.removeChild(container);
frame = undefined;
});
}
}
$window.keydown(function(event) {
pointer = konami[pointer] === event.which
? pointer+1
: +(event.which === konami[0]);
if (pointer === konami.length) {
pointer = 0;
poof();
}
});
if (!onlyOnKonami) poof();
});
html {
height: 100%;
}
body {
background: #d09d42;
background: linear-gradient(to bottom, #efc466, #d09d42);
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You need to modify method Confetto() to change the color of the divI've added the following javascript after innerStyle.backgroundColor = theme():
function Confetto(){
//Code
innerStyle.backgroundColor = theme()
innerStyle.background = "linear-gradient(to right, " + theme() + " , yellow)";
innerStyle.border = "thick solid #FFFB00";
innerStyle.borderWidth = "thin";
//Rest of the code
}
Check out this :
// JavaScript source code
'use strict';
// If set to true, the user must press
// UP UP DOWN ODWN LEFT RIGHT LEFT RIGHT A B
// to trigger the confetti with a random color theme.
// Otherwise the confetti constantly falls.
var onlyOnKonami = false;
$(function () {
// Globals
var $window = $(window),
random = Math.random,
cos = Math.cos,
sin = Math.sin,
PI = Math.PI,
PI2 = PI * 2,
timer = undefined,
frame = undefined,
confetti = [];
// Settings
var konami = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65],
//var konami = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
pointer = 0;
var particles = 10,
spread = 10,
sizeMin = 3,
sizeMax = 40 - sizeMin,
eccentricity = 10,
deviation = 100,
dxThetaMin = -.1,
dxThetaMax = -dxThetaMin - dxThetaMin,
dyMin = .13,
dyMax = .18,
dThetaMin = .4,
dThetaMax = .7 - dThetaMin;
var colorThemes = [
function () {
//return color(200 * random()|0, 200 * random()|0, 200 * random()|0);
return color(218, 165, 32);
},
function () {
var black = 200 * random() | 0;
return color(200, black, black);
},
function () {
var black = 200 * random() | 0;
return color(black, 200, black);
},
function () {
var black = 200 * random() | 0;
return color(black, black, 200);
},
function () {
return color(200, 100, 200 * random() | 0);
},
function () {
return color(200 * random() | 0, 200, 200);
},
function () {
var black = 256 * random() | 0;
return color(black, black, black);
},
function () {
return colorThemes[random() < .5 ? 1 : 2]();
},
function () {
return colorThemes[random() < .5 ? 3 : 5]();
},
function () {
return colorThemes[random() < .5 ? 2 : 4]();
}
];
function color(r, g, b) {
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
// Cosine interpolation
function interpolation(a, b, t) {
return (1 - cos(PI * t)) / 2 * (b - a) + a;
}
// Create a 1D Maximal Poisson Disc over [0, 1]
var radius = 1 / eccentricity,
radius2 = radius + radius;
function createPoisson() {
// domain is the set of points which are still available to pick from
// D = union{ [d_i, d_i+1] | i is even }
var domain = [radius, 1 - radius],
measure = 1 - radius2,
spline = [0, 1];
while (measure) {
var dart = measure * random(),
i, l, interval, a, b, c, d;
// Find where dart lies
for (i = 0, l = domain.length, measure = 0; i < l; i += 2) {
a = domain[i], b = domain[i + 1], interval = b - a;
if (dart < measure + interval) {
spline.push(dart += a - measure);
break;
}
measure += interval;
}
c = dart - radius, d = dart + radius;
// Update the domain
for (i = domain.length - 1; i > 0; i -= 2) {
l = i - 1, a = domain[l], b = domain[i];
// c---d c---d Do nothing
// c-----d c-----d Move interior
// c--------------d Delete interval
// c--d Split interval
// a------b
if (a >= c && a < d)
if (b > d) domain[l] = d; // Move interior (Left case)
else domain.splice(l, 2); // Delete interval
else if (a < c && b > c)
if (b <= d) domain[i] = c; // Move interior (Right case)
else domain.splice(i, 0, c, d); // Split interval
}
// Re-measure the domain
for (i = 0, l = domain.length, measure = 0; i < l; i += 2)
measure += domain[i + 1] - domain[i];
}
return spline.sort();
}
// Create the overarching container
var container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.height = '0';
container.style.overflow = 'visible';
container.style.zIndex = '9999';
// Confetto constructor
function Confetto(theme) {
this.frame = 0;
this.outer = document.createElement('div');
this.inner = document.createElement('div');
this.outer.appendChild(this.inner);
var outerStyle = this.outer.style,
innerStyle = this.inner.style;
outerStyle.position = 'absolute';
outerStyle.width = (sizeMin + sizeMax * random()) + 'px';
outerStyle.height = (sizeMin + sizeMax * random()) + 'px';
innerStyle.width = '100%';
innerStyle.height = '100%';
var f = theme();
innerStyle.backgroundColor = theme();
innerStyle.background = "linear-gradient(to right, " + theme() + " , yellow)";
innerStyle.border = "thick solid #FFFB00";
innerStyle.borderWidth = "thin";
outerStyle.perspective = '100px';
outerStyle.transform = 'rotate(' + (360 * random()) + 'deg)';
this.axis = 'rotate3D(' +
cos(360 * random()) + ',' +
cos(360 * random()) + ',0,';
this.theta = 360 * random();
this.dTheta = dThetaMin + dThetaMax * random();
innerStyle.transform = this.axis + this.theta + 'deg)';
this.x = $window.width() * random();
this.y = -deviation;
this.dx = sin(dxThetaMin + dxThetaMax * random());
this.dy = dyMin + dyMax * random();
outerStyle.left = this.x + 'px';
outerStyle.top = this.y + 'px';
// Create the periodic spline
this.splineX = createPoisson();
this.splineY = [];
for (var i = 1, l = this.splineX.length - 1; i < l; ++i)
this.splineY[i] = deviation * random();
this.splineY[0] = this.splineY[l] = deviation * random();
this.update = function (height, delta) {
this.frame += delta;
this.x += this.dx * delta;
this.y += this.dy * delta;
this.theta += this.dTheta * delta;
// Compute spline and convert to polar
var phi = this.frame % 7777 / 7777,
i = 0,
j = 1;
while (phi >= this.splineX[j]) i = j++;
var rho = interpolation(
this.splineY[i],
this.splineY[j],
(phi - this.splineX[i]) / (this.splineX[j] - this.splineX[i])
);
phi *= PI2;
outerStyle.left = this.x + rho * cos(phi) + 'px';
outerStyle.top = this.y + rho * sin(phi) + 'px';
innerStyle.transform = this.axis + this.theta + 'deg)';
return this.y > height + deviation;
};
}
function poof() {
if (!frame) {
// Append the container
document.body.appendChild(container);
// Add confetti
var theme = colorThemes[onlyOnKonami ? colorThemes.length * random() | 0 : 0],
count = 0;
(function addConfetto() {
if (onlyOnKonami && ++count > particles)
return timer = undefined;
var confetto = new Confetto(theme);
confetti.push(confetto);
container.appendChild(confetto.outer);
timer = setTimeout(addConfetto, spread * random());
})(0);
// Start the loop
var prev = undefined;
requestAnimationFrame(function loop(timestamp) {
var delta = prev ? timestamp - prev : 0;
prev = timestamp;
var height = $window.height();
for (var i = confetti.length - 1; i >= 0; --i) {
if (confetti[i].update(height, delta)) {
container.removeChild(confetti[i].outer);
confetti.splice(i, 1);
}
}
if (timer || confetti.length)
return frame = requestAnimationFrame(loop);
// Cleanup
document.body.removeChild(container);
frame = undefined;
});
}
}
$window.keydown(function (event) {
pointer = konami[pointer] === event.which ?
pointer + 1 :
+(event.which === konami[0]);
if (pointer === konami.length) {
pointer = 0;
poof();
}
});
if (!onlyOnKonami) poof();
});
html {
height: 100%;
}
body {
background: #d09d42;
background: linear-gradient(to bottom, #efc466, #d09d42);
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
So I wanted to stream things that aren't only mp3 files.
Things like youtube for example.
Ignore the rest of the code other than the audio part of it. This is a visualizer that I'm revamping from a dude that made a really nice audio visualizer.
How would I go about making it play youtube files and direct links that are streaming links. What I wanted to do was add a list of random songs so it would stream a song off of every single one rather than me uploading 100 songs to my domain and wasting space.
(function() {
var ALPHA, AudioAnalyser, COLORS, MP3_PATH, NUM_BANDS, NUM_PARTICLES, Particle, SCALE, SIZE, SMOOTHING, SPEED, SPIN;
NUM_PARTICLES = 150;
NUM_BANDS = 128;
SMOOTHING = 0.5;
var MP3_PATH = ['website.com/Song.mp3', 'website.com/Song2.mp3', 'website.com/Song3.mp3'];
SCALE = {
MIN: 5.0,
MAX: 20.0
};
SPEED = {
MIN: 0.2,
MAX: 1.0
};
ALPHA = {
MIN: 0.8,
MAX: 0.9
};
SPIN = {
MIN: 0.001,
MAX: 0.005
};
SIZE = {
MIN: 0.5,
MAX: 0.90
};
COLORS = ['#69D2E7', '#1B676B', '#BEF202', '#EBE54D', '#00CDAC', '#1693A5', '#F9D423', '#FF4E50', '#E7204E', '#0CCABA', '#FF006F'];
AudioAnalyser = (function() {
AudioAnalyser.AudioContext = self.AudioContext || self.webkitAudioContext;
AudioAnalyser.enabled = AudioAnalyser.AudioContext != null;
function AudioAnalyser(audio, numBands, smoothing) {
var src;
this.audio = audio != null ? audio : new Audio();
this.numBands = numBands != null ? numBands : 256;
this.smoothing = smoothing != null ? smoothing : 0.3;
if (typeof this.audio === 'string') {
src = this.audio;
this.audio = new Audio();
this.audio.crossOrigin = "anonymous";
this.audio.controls = true;
this.audio.src = src;
}
this.context = new AudioAnalyser.AudioContext();
this.jsNode = this.context.createScriptProcessor(2048, 1, 1);
this.analyser = this.context.createAnalyser();
this.analyser.smoothingTimeConstant = this.smoothing;
this.analyser.fftSize = this.numBands * 2;
this.bands = new Uint8Array(this.analyser.frequencyBinCount);
this.audio.addEventListener('canplay', (function(_this) {
return function() {
_this.source = _this.context.createMediaElementSource(_this.audio);
_this.source.connect(_this.analyser);
_this.analyser.connect(_this.jsNode);
_this.jsNode.connect(_this.context.destination);
_this.source.connect(_this.context.destination);
return _this.jsNode.onaudioprocess = function() {
_this.analyser.getByteFrequencyData(_this.bands);
if (!_this.audio.paused) {
return typeof _this.onUpdate === "function" ? _this.onUpdate(_this.bands) : void 0;
}
};
};
})(this));
}
AudioAnalyser.prototype.start = function() {
return this.audio.play();
};
AudioAnalyser.prototype.stop = function() {
return this.audio.pause();
};
return AudioAnalyser;
})();
Particle = (function() {
function Particle(x1, y1) {
this.x = x1 != null ? x1 : 0;
this.y = y1 != null ? y1 : 0;
this.reset();
}
Particle.prototype.reset = function() {
this.level = 1 + floor(random(4));
this.scale = random(SCALE.MIN, SCALE.MAX);
this.alpha = random(ALPHA.MIN, ALPHA.MAX);
this.speed = random(SPEED.MIN, SPEED.MAX);
this.color = random(COLORS);
this.size = random(SIZE.MIN, SIZE.MAX);
this.spin = random(SPIN.MAX, SPIN.MAX);
this.band = floor(random(NUM_BANDS));
if (random() < 0.5) {
this.spin = -this.spin;
}
this.smoothedScale = 0.0;
this.smoothedAlpha = 0.0;
this.decayScale = 0.0;
this.decayAlpha = 0.0;
this.rotation = random(TWO_PI);
return this.energy = 0.0;
};
Particle.prototype.move = function() {
this.rotation += this.spin;
return this.y -= this.speed * this.level;
};
Particle.prototype.draw = function(ctx) {
var alpha, power, scale;
power = exp(this.energy);
scale = this.scale * power;
alpha = this.alpha * this.energy * 1.5;
this.decayScale = max(this.decayScale, scale);
this.decayAlpha = max(this.decayAlpha, alpha);
this.smoothedScale += (this.decayScale - this.smoothedScale) * 0.3;
this.smoothedAlpha += (this.decayAlpha - this.smoothedAlpha) * 0.3;
this.decayScale *= 0.985;
this.decayAlpha *= 0.975;
ctx.save();
ctx.beginPath();
ctx.translate(this.x + cos(this.rotation * this.speed) * 250, this.y);
ctx.rotate(this.rotation);
ctx.scale(this.smoothedScale * this.level, this.smoothedScale * this.level);
ctx.moveTo(this.size * 0.5, 0);
ctx.lineTo(this.size * -0.5, 0);
ctx.lineWidth = 1;
ctx.lineCap = 'round';
ctx.globalAlpha = this.smoothedAlpha / this.level;
ctx.strokeStyle = this.color;
ctx.stroke();
return ctx.restore();
};
return Particle;
})();
Sketch.create({
particles: [],
setup: function() {
var analyser, error, i, intro, j, particle, ref, warning, x, y;
for (i = j = 0, ref = NUM_PARTICLES - 1; j <= ref; i = j += 1) {
x = random(this.width);
y = random(this.height * 2);
particle = new Particle(x, y);
particle.energy = random(particle.band / 350);
this.particles.push(particle);
}
if (AudioAnalyser.enabled) {
try {
analyser = new AudioAnalyser(random(MP3_PATH), NUM_BANDS, SMOOTHING);
analyser.onUpdate = (function(_this) {
return function(bands) {
var k, len, ref1, results;
ref1 = _this.particles;
results = [];
for (k = 0, len = ref1.length; k < len; k++) {
particle = ref1[k];
results.push(particle.energy = bands[particle.band] / 350);
}
return results;
};
})(this);
analyser.start();
document.body.appendChild(analyser.audio);
intro = document.getElementById('intro');
intro.style.display = 'none';
if (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)) {
warning = document.getElementById('warning2');
return warning.style.display = 'block';
}
} catch (_error) {
error = _error;
}
} else {
warning = document.getElementById('warning1');
return warning.style.display = 'block';
}
},
draw: function() {
var j, len, particle, ref, results;
this.globalCompositeOperation = 'lighter';
ref = this.particles;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
particle = ref[j];
if (particle.y < -particle.size * particle.level * particle.scale * 2) {
particle.reset();
particle.x = random(this.width);
particle.y = this.height + particle.size * particle.scale * particle.level;
}
particle.move();
results.push(particle.draw(this));
}
return results;
}
});
}).call(this);