Skip to content

Instantly share code, notes, and snippets.

@ja-k-e
Last active August 29, 2015 14:26
Show Gist options
  • Select an option

  • Save ja-k-e/00d66a66ad0cc43458ed to your computer and use it in GitHub Desktop.

Select an option

Save ja-k-e/00d66a66ad0cc43458ed to your computer and use it in GitHub Desktop.
Interactive Crib v1.0
<canvas id=canvas></canvas>
<p>
Crib v1.0<br>
<small>
rotate: click + drag | zoom: scroll
</small>
</p>

Interactive Crib v1.0

THREE.js FTW. My friend and I designed and built a crib. This is its core functionality minus some additional supports.

A Pen by Jake Albaugh on CodePen.

License.

var crib = new Crib();
crib.init();
// refresh crib size
var refreshCrib = debounce(function() {
crib.refresh();
crib.renderer.domElement.className = "";
}, 250);
var fadeCrib = function() {
if(crib.renderer.domElement.className == "") {
crib.renderer.domElement.className = "fade-out";
}
};
window.addEventListener('resize', refreshCrib);
window.addEventListener('resize', fadeCrib);
//
// crib design
//
function Crib() {
// return relative value for inches
function inch(i) {
return i / 12;
}
return {
// THREE scene object
scene: undefined,
// THREE camera object
camera: undefined,
// THREE renderer
renderer: undefined,
// THREE light objects
directionalLight: undefined,
ambientLight: undefined,
hemisphereLight: undefined,
// container is our master wrapper for animation
container: undefined,
// reset size and positioning
refresh: function() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize( window.innerWidth, window.innerHeight );
},
// instantiation methods
set: {
scene: function() {
return new THREE.Scene();
},
container: function() {
return new THREE.Object3D();
},
camera: function() {
return new THREE.PerspectiveCamera(
80, // perspective
window.innerWidth / window.innerHeight, // aspect ratio
0.001, // near clip
1000 // far clip
)
},
renderer: function() {
return new THREE.WebGLRenderer({
canvas: document.getElementById("canvas")
});
},
directionalLight: function() {
return new THREE.DirectionalLight( 0x999999 );
},
ambientLight: function() {
return new THREE.AmbientLight( 0x666666 );
},
hemisphereLight: function() {
return new THREE.HemisphereLight({
skyColorHex: "#fff",
groundColorHex: "#000",
intensity: 1
});
}
},
init: function() {
this.scene = this.set.scene();
this.container = this.set.container();
this.camera = this.set.camera();
this.renderer = this.set.renderer();
this.directionalLight = this.set.directionalLight(),
this.ambientLight = this.set.ambientLight();
this.hemisphereLight = this.set.hemisphereLight();
var scene = this.scene,
container = this.container,
camera = this.camera,
renderer = this.renderer,
directionalLight = this.directionalLight,
ambientLight = this.ambientLight,
hemisphereLight = this.hemisphereLight;
// add container to scene
scene.add(container);
// adjust camera
camera.position.z = 5;
// scale the renderer
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor( 0x222222, 1);
// add renderer to the DOM
document.body.appendChild(renderer.domElement);
// initialize the lights
directionalLight.position = camera.position;
scene.add(directionalLight);
//ambientLight.position = camera.position;
//scene.add(ambientLight);
hemisphereLight.position = camera.position;
scene.add(hemisphereLight);
// container rotations
container.rotation.y = 0;
container.rotation.x = 0;
// container position
container.position.y = inch(-20);
// calling each segment
var front = this.length( inch(-15.25), "#FFBB00"),
back = this.length( inch( 13.75), "#C69100"),
left = this.depth( inch( 26.75), "#00A383"),
right = this.depth( inch(-26.25), "#007E65"),
floor = this.floor( "#440468");
// add the floor
floor.init(this.container);
// add the front
front.init(this.container);
// add the back
back.init(this.container);
// add the left
left.init(this.container);
// add the right
right.init(this.container);
var floor_percentage = 0;
// render loop
var render = function () {
requestAnimationFrame(render);
renderer.render(scene, camera);
floor.animate(floor_percentage++ % 1000 / 1000);
//container.rotation.y += inch(0.05);
//container.rotation.x += 0.005;
};
// call first render
render();
//
// positioning events
//
var hold = false;
var prev = false;
var canvas = document.getElementById("canvas");
canvas.addEventListener("mousedown", function(e) {
prev = {x: e.screenX, y: e.screenY}
hold = true;
}, false);
canvas.addEventListener("mouseup", function() {
hold = false;
}, false);
canvas.addEventListener("mousemove", function(e) {
if(hold){
var y = e.screenY,
x = e.screenX;
container.rotation.x += (y - prev.y) / 500;
container.rotation.y += (x - prev.x) / 500;
prev = {x: x, y: y};
}
}, false);
var max_zoom_out = 5;
canvas.addEventListener("mousewheel", function(e) {
var delta = e.deltaY,
d;
if (delta < 0) {
d = Math.max(-200, delta) / 200;
} else {
d = Math.min(200, delta) / 200;
}
if ((d < 0 && container.position.z > -5) || (d >= 0 && container.position.z < 3)) container.position.z += d;
});
},
//
// length side
//
length: function(z, color) {
return {
obj: undefined,
pos: { x:inch(2.5), y:inch(20), z:z },
dim: { x:inch(56.5), y:inch(40), z:inch(0.75) },
material: new THREE.MeshPhongMaterial({
color: new THREE.Color(color),
wrapAround: true
}),
chunk: function(width,length,x,y) {
var dim = { x:width, y:length, z:inch(0.75)};
var pos = {
x: this.dim.x / -2 + dim.x / 2 + x,
y: this.dim.y / -2 + dim.y / 2 + y,
z: 0
};
var geo = new THREE.BoxGeometry( dim.x, dim.y, dim.z );
var chunk = new THREE.Mesh(geo);
chunk.position.set(pos.x,pos.y,pos.z);
chunk.updateMatrix();
return chunk;
},
init: function(container) {
this.obj = new THREE.Object3D();
container.add(this.obj);
// adjust vertical position of group
this.obj.position.set(this.pos.x,this.pos.y,this.pos.z);
//
// total geometry for merging shapes
//
var total_geometry = new THREE.Geometry();
//
// chunks
//
var bottom = this.chunk(inch(52),inch(7),0,0);
total_geometry.merge(bottom.geometry, bottom.matrix);
var top = this.chunk(inch(52),inch(3),0,inch(37));
total_geometry.merge(top.geometry, top.matrix);
var left = this.chunk(inch(2.5),inch(30),0,inch(7));
total_geometry.merge(left.geometry, left.matrix);
var right = this.chunk(inch(2.5),inch(30),inch(52 - 2.5),inch(7));
total_geometry.merge(right.geometry, right.matrix);
// shaft around negative space
for (var i = 0; i < 12; i++) {
var shaft = this.chunk(
inch(1.7), inch(30),
inch(4.5 + ((3.75) * i)),
inch(7)
);
total_geometry.merge(shaft.geometry, shaft.matrix);
}
// left top hook
var hook_lt = this.chunk(inch(2.3),inch(3),inch(-2.3),inch(32));
total_geometry.merge(hook_lt.geometry, hook_lt.matrix);
// left middle hook
var hook_lm = this.chunk(inch(2.3),inch(3),inch(-2.3),inch(20));
total_geometry.merge(hook_lm.geometry, hook_lm.matrix);
// left bottom hook
var hook_lb = this.chunk(inch(2.3),inch(3),inch(-2.3),inch(8));
total_geometry.merge(hook_lb.geometry, hook_lb.matrix);
// right top hook
var hook_rt = this.chunk(inch(2.3),inch(3),inch(52),inch(32));
total_geometry.merge(hook_rt.geometry, hook_rt.matrix);
// right middle hook
var hook_rm = this.chunk(inch(2.3),inch(3),inch(52),inch(20));
total_geometry.merge(hook_rm.geometry, hook_rm.matrix);
// right bottom hook
var hook_rb = this.chunk(inch(2.3),inch(3),inch(52),inch(8));
total_geometry.merge(hook_rb.geometry, hook_rb.matrix);
var combined = new THREE.Mesh(total_geometry, this.material);
this.obj.add(combined);
}
}
},
//
// depth side
//
depth: function(x, color) {
return {
obj: undefined,
pos: { x:x, y:inch(20), z:inch(-0.75) },
dim: { x:inch(0.75), y:inch(40), z:inch(32) },
material: new THREE.MeshPhongMaterial({
color: new THREE.Color(color),
wrapAround: true
}),
chunk: function(width,height,y,z) {
var dim = { x:inch(0.75), y:height, z:width};
var pos = {
x: 0,
y: this.dim.y / -2 + dim.y / 2 + y,
z: this.dim.z / -2 + dim.z / 2 + z
};
var geo = new THREE.BoxGeometry( dim.x, dim.y, dim.z );
var chunk = new THREE.Mesh(geo);
chunk.position.set(pos.x,pos.y,pos.z);
chunk.updateMatrix();
return chunk;
},
init: function(container) {
this.obj = new THREE.Object3D();
container.add(this.obj);
// adjust vertical position of group
this.obj.position.set(this.pos.x,this.pos.y,this.pos.z);
//
// total geometry for merging shapes
//
var total_geometry = new THREE.Geometry();
//
// chunks
//
function holePattern(x) {
return [
[inch(1),inch(40),0, x], // left runner
[inch(0.9),inch(6.75),0,x + inch(1)], // bottom
[inch(0.9),inch(8.75),inch(10),x + inch(1)], // middle bottom
[inch(0.9),inch(8.75),inch(22),x + inch(1)], // middle top
[inch(0.9),inch(6),inch(34),x + inch(1)], // top
[inch(1),inch(40),0, x + inch(1.9)]//, // right runner
];
}
function slotPattern(x) {
return [
[inch(1.25),inch(40),0, x], // left runner
[inch(3),inch(7),0,x + inch(1)], // bottom chunk
[inch(2),inch(0.875),inch(7),x + inch(1)], // bottom notch
[inch(2),inch(9),inch(9), x + inch(2)], // middle bottom chunk
[inch(1),inch(0.975),inch(18), x + inch(2)], // middle bottom notch
[inch(2),inch(8),inch(20), x + inch(2)], // middle top chunk
[inch(1),inch(0.975),inch(28), x + inch(2)], // middle top notch
[inch(3),inch(10),inch(30), x + inch(1)], // top
[inch(1.25),inch(40),0, x + inch(4)] // right runner
];
}
var left_holes = holePattern(inch(0));
var right_holes = holePattern(inch(32 - 2.9));
for (var h = 0; h < left_holes.length; h++) {
var left = this.chunk.apply(this, left_holes[h]);
var right = this.chunk.apply(this, right_holes[h]);
total_geometry.merge(left.geometry, left.matrix);
total_geometry.merge(right.geometry, right.matrix);
}
var left_slot = slotPattern(inch(2.875));
var right_slot = slotPattern(inch(23.875));
for (var s = 0; s < left_slot.length; s++) {
var left = this.chunk.apply(this, left_slot[s]);
var right = this.chunk.apply(this, right_slot[s]);
total_geometry.merge(left.geometry, left.matrix);
total_geometry.merge(right.geometry, right.matrix);
}
var main = this.chunk(inch(16.5),inch(33),inch(7),inch(8));
total_geometry.merge(main.geometry, main.matrix);
var combined = new THREE.Mesh(total_geometry, this.material);
this.obj.add(combined);
}
}
},
//
// the floor structure
//
floor: function(color) {
return {
obj: undefined,
pos: { x:0, y:inch(7), z:0 },
dim: { x:inch(52), y:inch(0.75), z:inch(26) },
material: new THREE.MeshPhongMaterial({
color: new THREE.Color(color),
wrapAround: true
}),
path: undefined, /// animation path
base: function() {
var geo = new THREE.BoxGeometry(this.dim.x, this.dim.y, this.dim.z);
var base = new THREE.Mesh(geo, this.material)
base.updateMatrix();
return base;
},
notch: function(width,length,x,z) {
var dim = { x:width, y:inch(0.75), z:length};
var pos = {
x: this.dim.x / -2 + dim.x / 2 + x,
y: 0,
z: this.dim.z / -2 + dim.z / 2 + z
};
var geo = new THREE.BoxGeometry( dim.x, dim.y, dim.z );
var notch = new THREE.Mesh(geo, this.material);
notch.position.set(pos.x,pos.y,pos.z);
notch.updateMatrix();
return notch;
},
animate: function(percent) {
var plot = this.path[Math.floor(this.path.length * percent)];
this.obj.position.z = plot.z;
this.obj.position.y = plot.y;
},
animationPath: function() {
var inc = inch(0.1),
path = [],
points = [
[inch(0.3), inch(7.375)],
[inch(0.3), inch(8.375)],
[inch(-1.5), inch(8.375)],
[inch(-1.5), inch(19.375)],
[inch(0.3), inch(19.375)],
[inch(0.3), inch(18.375)],
[inch(0.3), inch(19.375)],
[inch(-1.5), inch(19.375)],
[inch(-1.5), inch(29.375)],
[inch(0.3), inch(29.375)],
[inch(0.3), inch(28.375)],
[inch(0.3), inch(29.375)],
[inch(-1.5), inch(29.375)],
[inch(-1.5), inch(8.375)],
[inch(0.3), inch(8.375)],
[inch(0.3), inch(7.375)]
],
pauses = [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0];
for(var i = 0; i < points.length; i++) {
// if not the last
if (i + 1 < points.length) {
var axis = (points[i][0] != points[i+1][0]) ? 'x' : 'y';
// set initial points
var z = points[i][0];
var y = points[i][1];
// add pause frames
if (pauses[i] > 0) {
for (var r = 0; r < 50; r++) path.push({z: z, y: y });
}
// if traveling along the y axis
if(axis == 'y') {
// get the direction we are moving
var dir = (points[i][1] < points[i+1][1]) ? 'up' : 'down';
// if up, we are waiting for inc value to go over next plot
if (dir == 'up') {
while(y < points[i+1][1]) {
y += inc;
path.push({z: z, y: y });
}
}
// if down, we are waiting for inc value to go beneath next plot
else {
while(y > points[i+1][1]) {
y -= inc;
path.push({z: z, y: y });
}
}
// if traveling on z axis
} else {
// get the direction we are moving
var dir = (points[i][0] < points[i+1][0]) ? 'right' : 'left';
// if right, we are waiting for inc value to go over next plot
if (dir == 'right') {
while(z < points[i+1][0]) {
z += inc;
path.push({z: z, y: y });
}
}
// if left, we are waiting for inc value to go beneath next plot
else {
while(z > points[i+1][0]) {
z -= inc;
path.push({z: z, y: y });
}
}
}
}
}
return path;
},
init: function(container) {
this.obj = new THREE.Object3D();
container.add(this.obj);
// adjust vertical position of group
this.obj.position.set(this.pos.x,this.pos.y,this.pos.z);
this.path = this.animationPath();
//
// total geometry for merging shapes
//
var total_geometry = new THREE.Geometry();
//
// base
//
var base = this.base();
total_geometry.merge(base.geometry, base.matrix);
//
// horizontal notches
//
for (var i = 0; i < 13; i++) {
// front long notches
var notch_front = this.notch(
inch(1.875),
inch(0.75),
inch(2.75 + (3.75 * i)),
inch(26)
);
// back short notches
var notch_back = this.notch(
inch(1.875),
inch(3.125),
inch(2.75 + (3.75 * i)),
inch(-3.125)
);
total_geometry.merge(notch_front.geometry, notch_front.matrix);
total_geometry.merge(notch_back.geometry, notch_back.matrix);
}
//
// side notches
//
// left bottom
var left_bottom = this.notch(inch(2.25),inch(0.75),inch(-2.25),inch(1.9));
total_geometry.merge(left_bottom.geometry, left_bottom.matrix);
// left top
var left_top = this.notch(inch(2.25),inch(0.75),inch(-2.25),inch(22.9));
total_geometry.merge(left_top.geometry, left_top.matrix);
// right bottom
var right_bottom = this.notch(inch(2.25),inch(0.75),inch(52),inch(1.9));
total_geometry.merge(right_bottom.geometry, right_bottom.matrix);
// right top
var right_top = this.notch(inch(2.25),inch(0.75),inch(52),inch(22.9));
total_geometry.merge(right_top.geometry, right_top.matrix);
var combined = new THREE.Mesh(total_geometry, this.material);
this.obj.add(combined);
}
}
}
}
}
// david walsh debounce
// http://davidwalsh.name/javascript-debounce-function
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
jakealbaughSignature("light");
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
canvas {
position: absolute;
top: 0; bottom: 0; left: 0;
width:100%;
transition: opacity 500ms ease-out;
&.fade-out {
transition-duration: 100ms;
opacity: 0;
}
}
p {
z-index: 1;
user-select: none;
color: #999;
position: absolute;
top: 10px; left: 0;
margin: 0;
width: 100%;
text-align: center;
}
// disable bounce
html {
overflow: hidden;
height: 100%;
}
body {
background: #222;
height: 100%;
overflow: auto;
cursor: move;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment