Skip to content

Instantly share code, notes, and snippets.

@shshaw
Last active December 22, 2016 21:30
Show Gist options
  • Save shshaw/ed60855e9cec28d7d067725f8a9fae23 to your computer and use it in GitHub Desktop.
Save shshaw/ed60855e9cec28d7d067725f8a9fae23 to your computer and use it in GitHub Desktop.
⌨️ 3D Text Input ⌨️
<script>console.clear();</script>
const backgroundColor = 0xFFFFFF;
const textColor = 0x2F24C1;
const cursorColor = 0x3FD1CB;
const highlightColor = 0x3FD1CB;
/*////////////////////////////////////////*/
var renderCalls = [];
function render () {
requestAnimationFrame( render );
renderCalls.forEach((callback)=>{ callback(); });
}
render();
/*////////////////////////////////////////*/
var scene = new THREE.Scene();
scene.fog = new THREE.Fog(backgroundColor, 30, 300);
var camera = new THREE.PerspectiveCamera( 80, window.innerWidth / window.innerHeight, 0.1, 800 );
camera.position.z = 30;
camera.position.x = 5;
camera.position.y = -2;
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( backgroundColor );//0x );
renderer.toneMapping = THREE.LinearToneMapping;
renderer.toneMappingExposure = Math.pow( 0.94, 5.0 );
// if ( window.innerWidth > 500 ) {
// renderer.shadowMap.enabled = true;
// renderer.shadowMap.type = THREE.PCFShadowMap;
// }
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false );
document.body.appendChild( renderer.domElement);
function renderScene(){ renderer.render( scene, camera ); }
renderCalls.push(renderScene);
/*////////////////////////////////////////*/
var pointer = {
x: 0,
y: 0,
};
document.addEventListener('mousemove', pointerMove);
document.addEventListener('touchmove', pointerMove);
document.body.addEventListener('mouseleave', pointerReset);
document.body.addEventListener('touchcancel', pointerReset);
function pointerReset(e){
pointer.x = 0.5;
pointer.y = 0.5;
}
function pointerMove(e){
let pos = e.touches ? e.touches[0] : e;
pointer.x = pos.clientX / window.innerWidth;
pointer.y = pos.clientY / window.innerWidth;
};
let mousePos = { x: 0.25, y: 0.5 };
function trackMouse(e){
let pointer = e.touches ? e.touches[0] : e;
mousePos.x = ( pointer.clientX / window.innerWidth );
mousePos.y = ( pointer.clientY / window.innerHeight );
};
function ease(current,target,ease){ return current + (target - current) * ( ease || 0.2 ); }
// var center = new THREE.Vector3(0,0,0);
// function updateCamera(){
// mousePos._x = ease(mousePos._x || 0.5, mousePos.x, 0.06);
// mousePos._y = ease(mousePos._y || 0.5, mousePos.y, 0.06);
// scene.rotation.y = (mousePos._x - 0.5) * Math.PI/4;
// scene.rotation.x = (mousePos._y - 0.5) * Math.PI/4;
// // scene.position.x = -(12 * (mousePos._x - 0.5) * 2);
// // scene.position.y = (12 * (mousePos._y - 0.5) * 2);
// //scene.lookAt(center);
// // camera.lookAt( new THREE.Vector3(
// // (10 * (mousePos._x - 0.5) * 2),
// // -(10 * (mousePos._y - 0.5) * 2),
// // 0
// // ));
// }
// updateCamera();
// window.addEventListener('mousemove', trackMouse);
// renderCalls.push(updateCamera);
/*////////////////////////////////////////*/
let orbit = new THREE.OrbitControls(camera, renderer.domElement);
// orbit.enableRotate = false;
// orbit.enablePan = false;
//orbit.enableKeys = true;
orbit.zoomSpeed = 0.6;
orbit.minDistance = 10;
/*////////////////////////////////////////*/
var ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
var hemiLight = new THREE.HemisphereLight( 0xFFF7EB, 0xEBF7FD, 0.3 );
scene.add( hemiLight );
var light = new THREE.SpotLight( 0xffffff );
light.position.y = 40;
light.position.x = 0;
light.position.z = 200;
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 1;
light.shadow.camera.far = 800;
light.shadow.camera.fov = 40;
light.power = 1.5;
scene.add(light);
renderCalls.push(()=>{
light.position.copy(camera.position);
});
//var axisHelper = new THREE.AxisHelper( 30 );
//scene.add( axisHelper );
// The X axis is red. The Y axis is green. The Z axis is blue.
/*////////////////////////////////////////*/
// http://codepen.io/shshaw/pen/LbaKpa?editors=0010
var characters = {
"0":[[0,0],[1,0],[2,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[0,4],[1,4],[2,4]],
"1":[[1,0],[0,1],[1,1],[1,2],[1,3],[1,4]],
"2":[[0,0],[1,0],[2,1],[1,2],[0,3],[0,4],[1,4],[2,4]],
"3":[[0,0],[1,0],[2,1],[1,2],[2,2],[2,3],[0,4],[1,4],[2,4]],
"4":[[0,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[2,3],[2,4]],
"5":[[0,0],[1,0],[2,0],[0,1],[0,2],[1,2],[2,2],[2,3],[0,4],[1,4]],
"6":[[0,0],[1,0],[2,0],[0,1],[0,2],[1,2],[2,2],[0,3],[2,3],[0,4],[1,4],[2,4]],
"7":[[0,0],[1,0],[2,0],[2,1],[2,2],[1,3],[1,4]],
"8":[[0,0],[1,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[0,3],[2,3],[0,4],[1,4],[2,4]],
"9":[[0,0],[1,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[2,3],[0,4],[1,4],[2,4]],
"Z":[[0,0],[1,0],[2,0],[2,1],[1,2],[0,3],[0,4],[1,4],[2,4]],
"Y":[[0,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[2,3],[0,4],[1,4]],
"X":[[0,0],[2,0],[0,1],[2,1],[1,2],[0,3],[2,3],[0,4],[2,4]],
"W":[[0,0],[4,0],[0,1],[2,1],[4,1],[0,2],[2,2],[4,2],[0,3],[2,3],[4,3],[1,4],[3,4]],
"V":[[0,0],[2,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[1,4]],
"U":[[0,0],[2,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[0,4],[1,4],[2,4]],
"T":[[0,0],[1,0],[2,0],[1,1],[1,2],[1,3],[1,4]],
"S":[[1,0],[2,0],[0,1],[1,2],[2,3],[0,4],[1,4]],
"R":[[0,0],[1,0],[0,1],[2,1],[0,2],[2,2],[0,3],[1,3],[0,4],[2,4]],
"Q":[[1,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[1,4],[2,4],[3,4]],
"P":[[0,0],[1,0],[0,1],[2,1],[0,2],[1,2],[2,2],[0,3],[0,4]],
"O":[[1,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[1,4]],
"N":[[0,0],[1,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[0,4],[2,4]],
"M":[[0,0],[1,0],[2,0],[3,0],[0,1],[2,1],[4,1],[0,2],[2,2],[4,2],[0,3],[2,3],[4,3],[0,4],[4,4]],
"L":[[0,0],[0,1],[0,2],[0,3],[0,4],[1,4],[2,4]],
"K":[[0,0],[2,0],[0,1],[2,1],[0,2],[1,2],[0,3],[2,3],[0,4],[2,4]],
"J":[[2,0],[2,1],[0,2],[2,2],[0,3],[2,3],[1,4]],
"I":[[0,0],[1,0],[2,0],[1,1],[1,2],[1,3],[0,4],[1,4],[2,4]],
"H":[[0,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[0,3],[2,3],[0,4],[2,4]],
"G":[[1,0],[2,0],[0,1],[0,2],[2,2],[0,3],[2,3],[0,4],[1,4],[2,4]],
"F":[[0,0],[1,0],[2,0],[0,1],[0,2],[1,2],[0,3],[0,4]],
"E":[[1,0],[2,0],[0,1],[0,2],[1,2],[0,3],[0,4],[1,4],[2,4]],
"D":[[0,0],[1,0],[0,1],[2,1],[0,2],[2,2],[0,3],[2,3],[0,4],[1,4]],
"C":[[1,0],[2,0],[0,1],[0,2],[0,3],[1,4],[2,4]],
"B":[[0,0],[1,0],[0,1],[2,1],[0,2],[1,2],[0,3],[2,3],[0,4],[1,4]],
"A":[[1,0],[2,0],[0,1],[2,1],[0,2],[1,2],[2,2],[0,3],[2,3],[0,4],[2,4]],
"}":[[0,0],[1,0],[1,1],[2,2],[1,3],[0,4],[1,4]],
"{":[[1,0],[2,0],[1,1],[0,2],[1,3],[1,4],[2,4]],
"]":[[0,0],[1,0],[1,1],[1,2],[1,3],[0,4],[1,4]],
"[":[[0,0],[1,0],[0,1],[0,2],[0,3],[0,4],[1,4]],
")":[[0,0],[1,1],[1,2],[1,3],[0,4]],
"(":[[1,0],[0,1],[0,2],[0,3],[1,4]],
"—":[[0,2],[1,2],[2,2],[3,2]],
"–":[[0,2],[1,2],[2,2]],
"-":[[0,2],[1,2]],
"`":[[0,0],[1,1]],
"\"":[[0,0],[1,0],[3,0],[4,0],[1,1],[4,1],[0,2],[3,2]],
"\\":[[0,0],[0,1],[1,2],[2,3],[2,4]],
",":[[1,3],[0,4]],
":":[[0,1],[0,3]],
"^":[[2,0],[1,1],[3,1],[0,2],[4,2]],
"!":[[0,0],[0,1],[0,2],[0,4]],
"=":[[0,1],[1,1],[2,1],[0,3],[1,3],[2,3]],
"|":[[0,0],[0,1],[0,2],[0,3],[0,4]],
".":[[0,4]],
"%":[[0,0],[1,0],[4,0],[0,1],[1,1],[3,1],[2,2],[1,3],[3,3],[4,3],[0,4],[3,4],[4,4]],
"'":[[0,0],[1,0],[1,1],[0,2]],
"?":[[0,0],[1,0],[2,0],[2,1],[1,2],[1,4]],
";":[[0,1],[0,3],[0,4]],
"/":[[2,0],[2,1],[1,2],[0,3],[0,4]],
">":[[0,0],[1,1],[2,2],[1,3],[0,4]],
"_":[[0,4],[1,4],[2,4]],
"+":[[1,1],[0,2],[1,2],[2,2],[1,3]],
"<":[[2,0],[1,1],[0,2],[1,3],[2,4]],
"~":[[1,0],[3,0],[0,1],[2,1]],
}
/*////////////////////////////////////////*/
var poxelGeometry = new THREE.BoxGeometry( 1, 1, 1 );
var poxelMaterial = new THREE.MeshPhongMaterial({
color: 0xFF0000, //color || '#F00',
shininess: 60
});
function Poxel(material){
THREE.Mesh.call( this, poxelGeometry, poxelMaterial);
this.castShadow = true;
this.receiveShadow = true;
return this;
}
CustomBounce.create("myBounce", {strength: 0.1, squash:0 });
Poxel.prototype = Object.assign(Object.create(THREE.Mesh.prototype), {
constructor: Poxel,
transitionOut(speed){
speed = speed || 0.4 + Math.random() * 0.2;
let tl = new TimelineLite({
onStart: ()=> { this.animating = true; },
onComplete: ()=> { this.animating = false; }
});
tl.to(this.position, speed, {
z: -80,
delay: Math.random(),
ease: 'Power2.easeIn' //Bounce.easeOut
},0);
this.material = this.material.clone();
this.material.transparent = true;
tl.to(this.material, speed * 0.6, {
opacity: 0,
ease: 'Linear.easeNone'
},speed * 0.6);
return tl;
},
transitionIn(speed){
speed = speed || 0.4 + Math.random() * 0.35;
return TweenLite.from(this.position, speed, {
z: 80,
ease: 'myBounce',
onStart: ()=> { this.animating = true; },
onComplete: ()=> { this.animating = false; }
},0);
},
});
/*////////////////////////////////////////*/
function Character(name,opts){
THREE.Object3D.call(this);
this.name = name;
for (var key in opts){ this[key] = opts[key]; }
if ( !opts || !opts.poxelMaterial ) {
this.poxelMaterial = new THREE.MeshPhongMaterial({
color: this.color, //color || '#F00',
transparent: true,
emissive: this.color,
emissiveIntensity: 0.6,
opacity: 0.95,
shininess: 120
});
}
this.buildPoxels(name);
}
Character.prototype = Object.assign(Object.create(THREE.Object3D.prototype), {
constructor: Character,
color: textColor, //0x454545,
buildPoxels(name){
let pixels = characters[name];
let i = pixels.length;
let width = 0;
let height = 0;
while( i-- ){
let pixel = pixels[i];
let pixelCube = new Poxel();
pixelCube.material = this.poxelMaterial;
width = pixel[0] > width ? pixel[0] : width;
height = pixel[1] > height ? pixel[1] : height;
pixelCube.position.set(
pixel[0],// - (this.spriteWidth/2),
-pixel[1],// + (this.spriteHeight/2),
0
);
this.add(pixelCube);
}
this.width = width+1;
this.height = height+1;
},
transitionIn(delay, onComplete){
onComplete = onComplete || function(){};
let tl = new TimelineLite({
onStart: ()=>{ this.animating = true; },
onComplete: ()=>{
this.animating = false;
onComplete.call(this,this);
},
delay: delay
});
this.traverseVisible((child)=>{
if ( child !== this ) { tl.add(child.transitionIn(),0); }
});
return tl;
},
transitionOut(delay, onComplete){
onComplete = onComplete || function(){};
//this.matrixAutoUpdate = false;
// this.position.copy(this.getWorldPosition());
// this.parent.remove(this);
// scene.add(this);
let tl = new TimelineLite({
onStart: ()=>{ this.animating = true; },
onComplete: ()=>{
this.animating = false;
onComplete.call(this,this);
},
delay: delay
});
this.traverseVisible((child)=>{
if ( child !== this ) { tl.add(child.transitionOut(),0); }
});
return tl;
}
});
/*////////////////////////////////////////*/
function ease(current,target,ease){ return current + (target - current) * ( ease || 0.2 ); }
var container = new THREE.Group();
scene.add(container);
var el = document.createElement('div');
document.body.appendChild(el);
var inputter = new Vue({
el: el,
data: ()=>({
message: '',
defaultMessage: "Start\nTyping",
characters: [],
offsetX: 0,
offsetY: 0,
group: new THREE.Group(),
cursor: null
}),
template: '<textarea ref="input" type="text" v-model:value="message" style="position: absolute; bottom: 0; left: 0; opacity: 0;" />',
mounted(){
this.message = ( location.hash && decodeURI(location.hash.slice(1)) ) || this.defaultMessage;
this.$refs.input.focus();
//renderCalls.push(()=>{ this.$refs.input.focus(); });
this.$nextTick(()=>{ this.$refs.input.select(); });
document.addEventListener('focus', e=>{
console.log('click!');
this.$refs.input.focus();
});
// Cursor for tracking position in textarea
this.cursor = new Character('|',{
poxelMaterial: new THREE.MeshPhongMaterial({
color: cursorColor,
emissive: cursorColor,
emissiveIntensity: 0.6,
transparent: true,
opacity: 0.6
})
});
this.cursor.visible = false;
TweenMax.to({},0.7,{
onRepeat: ()=>{
if ( !this.cursor.animate ) {
return;
} else {
this.cursor.visible = !this.cursor.visible;
}
},
repeat: -1
});
this.cursor.traverse((child)=>{ child.attractive = false });
this.cursor.position.z = -1.1;
this.group.add(this.cursor);
// text highlight when selection is made
this.highlight = new Poxel();
this.highlight.material = new THREE.MeshPhongMaterial({
color: highlightColor,
emissive: highlightColor,
emissiveIntensity: 0.6,
transparent: true,
opacity: 0.6
});
this.highlight.position.z = -2;
container.add(this.highlight);
container.add(this.group);
// Keep some dimension to the group.
let poxel = new Poxel();
poxel.visible = false;
this.group.add(poxel);
// for sizing calculations
this.helper = new THREE.BoundingBoxHelper(this.group, 0xff0000);
this.helper.visible = false;
container.add(this.helper);
console.log(this.helper);
renderCalls.push( this.update );
},
watch: {
message: function(val, oldVal){
val = val && val.toUpperCase();
oldVal = oldVal && oldVal.toUpperCase();
if ( val === oldVal ) { return; }
let len = Math.max(val.length, oldVal.length);
let i = 0;
let delay = 0;
let tl = new TimelineLite();
let oldChars = this.characters;
let newChars = val.split('');
for (; i< len; i++){
let char = newChars[i];
let oldChar = ( oldChars[i] && oldChars[i].name || oldChars[i] );
if ( oldChar !== char ) {
//console.log('not equal', i, char, oldChar);
if ( oldChar ) { tl.add(this.removeChar(oldChars[i], i), delay += 0.05 ); }
if ( char ) { tl.add(this.addChar(char, i), delay += 0.05 ); }
}
}
//this.cursor.visible = true;
},
},
methods: {
addChar(char, i){
let character = char.toUpperCase();
let tl = function(){};
if ( characters[character] ) {
character = new Character(character);
tl = character.transitionIn(null);
this.group.add(character);
}
this.characters[i] = character;
let pos = this.getPosition(i);
if ( character.position ) {
character.position.x = pos[0] - character.width;
character.position.y = pos[1];
}
return tl;
},
removeChar(char, i){
delete this.characters[i];
if ( char && char.transitionOut ) {
return char.transitionOut(null,(char)=>{
if ( char.parent ) { char.parent.remove(char); }
});
} else { return function(){} }
},
getPosition(index){
let offsetX = 0;
let offsetY = 0;
this.characters.forEach((char,i)=>{
if ( index !== null && i > index ) { return false; }
if ( char.match && char.match(/[\n\r]/) ) {
offsetY -= 6;
offsetX = 0;
} else if ( char.width ) {
offsetX += (char.width + 1);
} else {
offsetX += 4;
}
});
this.offsetX = offsetX;
this.offsetY = offsetY;
return [offsetX, offsetY];
},
updateCursor(){
let start = this.$refs.input.selectionStart;
let end = this.$refs.input.selectionEnd;
let cursorPos = this.getPosition(start-1);
let cursorX = cursorPos[0] + 1;
let cursorY = cursorPos[1];
let scaleX = 1;
let scaleY = 1;
if ( start !== end ) {
let cursorEnd = this.getPosition(end);
//this.cursor.position.x -= start-end;
cursorX = cursorEnd[0] - ((cursorEnd[0] - cursorPos[0])/2);
//scaleX = cursorEnd[0] - cursorPos[0]; //start-end;
if ( cursorEnd[1] !== cursorPos[1] ) {
//cursorY = (cursorEnd[1]) + ( cursorEnd[1] - cursorPos[1] ) / 2;
//scaleY = ( cursorEnd[1] - cursorPos[1] ) / 3; //start-end;
}
this.cursor.visible = false;
this.cursor.animate = false;
this.updateHighlight();
} else {
this.highlight.visible = false;
this.cursor.animate = true;
}
this.cursor.position.x = ease(this.cursor.position.x, cursorX, 0.2);
this.cursor.position.y = ease(this.cursor.position.y, cursorY, 0.5);
this.cursor.scale.x = ease( this.cursor.scale.x, scaleX, 0.2);
this.cursor.scale.y = ease( this.cursor.scale.y, scaleY, 0.2);
},
updateHighlight(){
this.highlight.visible = true;
this.highlight.position.copy(this.group.position);
this.highlight.scale.x = Math.abs(this.helper.box.max.x) + Math.abs(this.helper.box.min.x);
this.highlight.position.x += this.highlight.scale.x/2;
this.highlight.scale.y = Math.abs(this.helper.box.max.y) + Math.abs(this.helper.box.min.y);
this.highlight.position.y -= this.highlight.scale.y/2;
this.highlight.position.z -= 2;
},
update(){
this.helper.position.copy(this.group.position);
this.helper.position.z = -1;
this.helper.update();
this.updateCursor();
let centerX = (( this.helper.box.max.x + this.helper.box.min.x ))/2;
let centerY = (( this.helper.box.max.y + this.helper.box.min.y ))/1.5;
//console.log(this.helper.size());//this.highlight.scale.x =
this.group.position.x = ease(this.group.position.x, (- this.offsetX) - centerX, 0.05);
this.group.position.y = ease(this.group.position.y, -this.offsetY - centerY, 0.05);
this.group.position.z = ease(this.group.position.z, -this.characters.length / 2, 0.05);
}
}
});
/*////////////////////////////////////////*/
var renderCalls = [];
function render () {
requestAnimationFrame( render );
renderCalls.forEach((callback)=>{ callback(); });
}
render();
/*////////////////////////////////////////*/
var scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xEEEEEE, 30, 300);
var camera = new THREE.PerspectiveCamera( 80, window.innerWidth / window.innerHeight, 0.1, 800 );
camera.position.z = 30;
camera.position.y = -2;
var renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xFFFFFF );//0x );
renderer.toneMapping = THREE.LinearToneMapping;
renderer.toneMappingExposure = Math.pow( 0.94, 5.0 );
// if ( window.innerWidth > 500 ) {
// renderer.shadowMap.enabled = true;
// renderer.shadowMap.type = THREE.PCFShadowMap;
// }
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false );
document.body.appendChild( renderer.domElement);
function renderScene(){ renderer.render( scene, camera ); }
renderCalls.push(renderScene);
/*////////////////////////////////////////*/
function convertImage(src,callback){
var img = new Image();
img.onload = callback || buildBoxes;
img.src = ( src.target ? src.target.result : src );
}
// Convert image to canvas ImageData
function imageToData(img, width, height, offsetX, offsetY) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
offsetX = offsetX || 0;
offsetY = offsetY || 0;
width = width || img.width;
height = height || img.height;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, offsetX, offsetY);
return ctx.getImageData(0,0,width,height);
}
function getPixels(img) {
var pixels = [],
data = img.data,
len = data.length,
w = img.width,
h = img.height,
x = 0,
y = 0,
i = 0;
for (; i < len; i+= 4) {
if ( data[i+3] > 0 ) {
x = (i / 4) % w;
y = Math.floor((i / 4) / w);
pixels.push([x,y]);
}
}
return pixels;
}
function getColors(img) {
var colors = {},
data = img.data,
len = data.length,
w = img.width,
h = img.height,
x = 0,
y = 0,
i = 0,
color;
for (; i < len; i+= 4) {
if ( data[i+3] > 0 ) {
color = 'rgb('+data[i]+','+data[i+1]+','+data[i+2]+')' //,'+data[i+3]/255+')';
if ( !colors[color] ) {
colors[color] = [];
colors[color].r = data[i];
colors[color].g = data[i+1];
colors[color].b = data[i+2];
colors[color].a = data[i+3] / 255;
colors[color].y = 0.299*data[i] + 0.587*data[i+1] + 0.114*data[i+2];
}
x = (i / 4) % w;
y = Math.floor((i / 4) / w);
colors[color].push([x,y]);
}
}
return colors;
}
/*////////////////////////////////////////*/
var poxelGeometry = new THREE.BoxGeometry( 1, 1, 1 );
function Poxel(color){
var material = new THREE.MeshPhongMaterial({
color: 0xFF0000, //color || '#F00',
shininess: 30
});
THREE.Mesh.call( this, poxelGeometry, material);
this.castShadow = true;
this.receiveShadow = true;
return this;
}
CustomBounce.create("myBounce", {strength: 0.2, squash:0 });
Poxel.prototype = Object.assign(Object.create(THREE.Mesh.prototype), {
constructor: Poxel,
transitionOut(speed){
speed = speed || 0.3 + Math.random() * 0.3;
let tl = new TimelineLite({
onStart: ()=> { this.animating = true; },
onComplete: ()=> { this.animating = false; }
});
this.material = this.material.clone();
this.material.transparent = true;
tl.to(this.material, speed * 0.5, {
opacity: 0,
delay: speed * 0.5,
ease: 'Power1.easeIn'
});
tl.to(this.position, speed, {
z: -100,
delay: Math.random(),
ease: 'Power3.easeIn' //Bounce.easeOut
},0);
return tl;
},
transitionIn(speed){
speed = speed || 0.4 + Math.random() * 0.3;
return TweenLite.from(this.position, speed, {
z: 100,
ease: 'myBounce',
onStart: ()=> { this.animating = true; },
onComplete: ()=> { this.animating = false; }
},0);
}
});
/*////////////////////////////////////////*/
let characterGroup = new THREE.Group();
scene.add(characterGroup);
var font = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJQAAAAFAQMAAACZyFdxAAAABlBMVEUmRckAAADvxClRAAAAAXRSTlMAQObYZgAAAG5JREFUeAFjyMnJy3q04rOwYO/VwNCiM+vevXvAsKqro8tpxapVKwOmhoaKKa1oWrWA4U3XmXVeJ1atXukxNVRdye3do3cLQOpWeQHVabZMDQ10alJasmgBw5qcF6+erOuaJBsxpyus6J3Ok3cPACq+NLD+sVySAAAAAElFTkSuQmCC';
let fontImage = new Image();
fontImage.src = font;
function Character(name,opts){
THREE.Object3D.call(this);
this.name = name;
for (var key in opts){ this[key] = opts[key]; }
this.buildPoxels();
}
Character.prototype = Object.assign(Object.create(THREE.Object3D.prototype), {
constructor: Character,
spriteWidth: 3,
spriteHeight: 5,
spriteOffset: 0,
spriteImg: fontImage,
buildPoxels(img){
img = img || this.spriteImg;
let data = imageToData(img, this.spriteWidth, this.spriteHeight, -this.spriteOffset);
let pixels = getPixels(data);
let i = pixels.length;
let poxel = new Poxel('#F00');
while( i-- ){
let pixel = pixels[i];
let pixelCube = poxel.clone();
pixelCube.position.set(
pixel[0],// - (this.spriteWidth/2),
-pixel[1],// + (this.spriteHeight/2),
0
);
this.add(pixelCube);
}
},
transitionIn(delay, onComplete){
onComplete = onComplete || function(){};
let tl = new TimelineLite({
onStart: ()=>{ this.animating = true; },
onComplete: ()=>{
this.animating = false;
onComplete.call(this,this);
},
delay: delay
});
this.traverseVisible((child)=>{
if ( child !== this ) {
tl.add(child.transitionIn(),0);
}
});
return tl;
},
transitionOut(delay, onComplete){
onComplete = onComplete || function(){};
let tl = new TimelineLite({
onStart: ()=>{ this.animating = true; },
onComplete: ()=>{
this.animating = false;
onComplete.call(this,this);
},
delay: delay
});
this.traverseVisible((child)=>{
if ( child !== this ) {
tl.add(child.transitionOut(),0);
}
});
return tl;
}
});
/*////////////////////////////////////////*/
var characters = {
A: { spriteOffset: 0 },
B: { spriteOffset: 4 },
C: { spriteOffset: 8 },
D: { spriteOffset: 12 },
E: { spriteOffset: 16 },
F: { spriteOffset: 20 },
G: { spriteOffset: 24 },
H: { spriteOffset: 28 },
I: { spriteOffset: 32 },
J: { spriteOffset: 36 },
K: { spriteOffset: 40 },
L: { spriteOffset: 44 },
M: { spriteOffset: 48, spriteWidth: 5 },
N: { spriteOffset: 54 },
O: { spriteOffset: 58 },
P: { spriteOffset: 62 },
Q: { spriteOffset: 66, spriteWidth: 4 },
R: { spriteOffset: 71 },
S: { spriteOffset: 75 },
T: { spriteOffset: 79 },
U: { spriteOffset: 83 },
V: { spriteOffset: 87 },
W: { spriteOffset: 91, spriteWidth: 5 },
X: { spriteOffset: 97 },
Y: { spriteOffset: 101 },
Z: { spriteOffset: 105 },
1: { spriteOffset: 109, spriteWidth: 2, },
2: { spriteOffset: 112 },
3: { spriteOffset: 116 },
4: { spriteOffset: 120 },
5: { spriteOffset: 124 },
6: { spriteOffset: 128 },
7: { spriteOffset: 132 },
8: { spriteOffset: 136 },
9: { spriteOffset: 140 },
0: { spriteOffset: 144 },
SPACE: { spriteOffset: 148 }
};
// convertImage(font, function(){
// let img = this;
// this;
// let offset = 0;
// let i = 0;
// let tl = new TimelineMax({ repeat: -1, yoyo: true, paused: true });
// for (var key in characters){
// offset = characters[key].spriteOffset;
// //let char = new Character(key, characters[key])
// //characters[key] = char;
// //scene.add(char);
// let char2 = makeCharacter(key);
// char2.position.x = offset;
// char2.position.y = -6;
// scene.add(char2);
// //tl.add(char2.transitionIn(i * 0.2),0);
// i++;
// }
// console.log(tl.duration());
// // tl.to(scene.position, tl.duration() + 0.3, {
// // x: -offset,
// // delay: 0.3,
// // ease: Power1.easeInOut
// // },0);
// tl.play();
// });
function makeCharacter(key){
//offset = characters[key].spriteOffset;
let char = new Character(key, characters[key]);
return char;
}
/*////////////////////////////////////////*/
// var pointer = {
// x: 0,
// y: 0,
// };
// document.addEventListener('mousemove', pointerMove);
// document.addEventListener('touchmove', pointerMove);
// document.body.addEventListener('mouseleave', pointerReset);
// document.body.addEventListener('touchcancel', pointerReset);
// function pointerReset(e){
// pointer.x = 0.5;
// pointer.y = 0.5;
// }
// function pointerMove(e){
// let pos = e.touches ? e.touches[0] : e;
// pointer.x = pos.clientX / window.innerWidth;
// pointer.y = pos.clientY / window.innerWidth;
// };
// let mousePos = { x: 0.5, y: 0.5, _x: 0.5, _y: 0.5 };
// function trackMouse(e){
// let pointer = e.touches ? e.touches[0] : e;
// mousePos.x = ( pointer.clientX / window.innerWidth );
// mousePos.y = ( pointer.clientY / window.innerHeight );
// };
// function ease(current,target,ease){ return current + (target - current) * ( ease || 0.2 ); }
// function updateCamera(){
// mousePos._x = ease(mousePos._x || 0.5, mousePos.x, 0.06);
// mousePos._y = ease(mousePos._y || 0.5, mousePos.y, 0.06);
// camera.position.x = (-25 * (mousePos._x - 0.5) * 2);
// camera.position.y = (25 * (mousePos._y - 0.5) * 2);
// camera.lookAt(new THREE.Vector3(0,0,0));
// }
// updateCamera();
// window.addEventListener('mousemove', trackMouse);
// renderCalls.push(updateCamera);
/*////////////////////////////////////////*/
/*////////////////////////////////////////*/
let orbit = new THREE.OrbitControls(camera, renderer.domElement);
// orbit.enableRotate = false;
// orbit.enablePan = false;
// orbit.enableKeys = false;
orbit.zoomSpeed = 0.6;
orbit.minDistance = 10;
/*////////////////////////////////////////*/
var ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
var hemiLight = new THREE.HemisphereLight( 0xFFF7EB, 0xEBF7FD, 0.3 );
scene.add( hemiLight );
var light = new THREE.SpotLight( 0xffffff );
light.position.y = 40;
light.position.x = 0;
light.position.z = 200;
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 1;
light.shadow.camera.far = 800;
light.shadow.camera.fov = 40;
light.power = 2.5;
scene.add(light);
renderCalls.push(()=>{
light.position.copy(camera.position);
});
var light2 = new THREE.PointLight( 0xffffff );
light2.position.y = 40;
light2.position.x = 0;
light2.position.z = 200;
light2.castShadow = true;
light2.power = 1.5;
scene.add(light);
var axisHelper = new THREE.AxisHelper( 30 );
scene.add( axisHelper );
// The X axis is red. The Y axis is green. The Z axis is blue.
/*////////////////////////////////////////*/
/*
let controls = {
radius: 4,
amount: 4.5,
type: 'attract',
methods: {
attract: function(object, pos){
let dist = distance(object.position, pos);
object.__position = object.__position || object.position.clone();
object.position.z = ( dist < controls.radius ? controls.amount * (controls.radius-dist)/controls.radius / object.scale.z : 0 );
},
// loosely based on http://jsfiddle.net/225cb9d7/3/
repel: function(object, pos){
let x0 = object.position.x;
let y0 = object.position.y;
let diffX = pos.x - x0;
let diffY = pos.y - y0;
let dist = Math.sqrt( diffX * diffX + diffY * diffY);// + diffZ * diffZ);
// Save copy of original position
object.__position = object.__position || object.position.clone();
let powerx = x0 - ( diffX / dist) * (controls.amount/2) / dist;
let powery = y0 - ( diffY / dist) * (controls.amount/2) / dist;
let forcex = ( object.__position.x - x0); //(forcex + ( object.__position.x - x0) / 2) / 2.1;
let forcey = ( object.__position.y - y0); //(forcey + ( object.__position.y - y0) / 2) / 2.1;
object.position.x = powerx + forcex;
object.position.y = powery + forcey;
object.position.z = ( dist < controls.radius ? -controls.amount * 2 * (controls.radius-dist)/controls.radius / object.scale.z : 0 );
}
}
};
var gui = new dat.GUI();
gui.add(controls, 'type', Object.keys(controls.methods));
gui.add(controls, 'radius', 1, 20).step(0.5);
gui.add(controls, 'amount', 0.25, 20).step(0.25);
gui.domElement.style.zIndex = 20;
/*////////////////////////////////////////*/
/*
function distance(pos1, pos2){
let diffX = pos2.x - pos1.x,
diffY = pos2.y - pos1.y;//,
//diffZ = pos2.z - pos1.z;
return Math.sqrt( diffX * diffX + diffY * diffY);// + diffZ * diffZ);
}
let mouse = new THREE.Vector3();
let raycaster = new THREE.Raycaster();
const TWOPI = Math.PI * 2;
document.addEventListener('mousemove',click);
document.addEventListener('touchmove',click);
function click(e){
if ( animating || animatingOut || !group ) { return; }
let pointer = e.touches ? e.touches : [e];
for (let i = 0, len = pointer.length; i < len; i++){
let event = pointer[i];
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
mouse.unproject( camera );
let dir = mouse.sub( camera.position ).normalize();
let dist = -camera.position.z / dir.z;
let pos = camera.position.clone().add( dir.multiplyScalar( dist ) );
group.traverseVisible((object)=>{
if ( object !== group ) {
controls.methods[controls.type](object, pos);
}
});
}
}
/*////////////////////////////////////////*/
function ease(current,target,ease){ return current + (target - current) * ( ease || 0.2 ); }
var el = document.createElement('div');
document.body.appendChild(el);
var inputter = new Vue({
el: el,
data: {
message: '',
chars: [],
},
template: `<textarea ref="input" type="text" v-model:value="message" style="position: absolute; bottom: 0; left: 0;" />`,
mounted(){
this.$refs.input.focus();
this.message = 'Hello';
renderCalls.push(()=>{
characterGroup.position.x = ease(characterGroup.position.x, -this.getPosition() + 3, 0.1);
});
},
watch: {
message: function(val, oldVal){
let delay = 0;
let len = val.length;
let i = oldVal.length;
let tl = new TimelineLite();
if ( len > i ) {
let arr = val.split('');
for (; i < len; i++){
tl.add(this.addChar(arr[i]) || function(){}, delay += 0.05 );
}
} else {
for (; i > len; i--){
let char = this.chars.pop();
tl.add(this.removeChar(char) || function(){}, delay += 0.1 );
}
}
// let dur = Math.max(tl.duration(), 0.4); //, 1);
// tl.to(scene.position, dur * 1.1, {
// x: ,
// ease: 'Power2.easeInOut'
// },dur * 0.1);
},
},
methods: {
addChar(char, delay){
if ( char === ' ' ) { char = 'SPACE'; }
char = char.toUpperCase();
console.log(char);
if ( characters[char] ) {
let character = makeCharacter(char);
character.position.x = this.getPosition();
let tl = character.transitionIn(delay);
characterGroup.add(character);
this.chars.push(character);
return tl;
}
},
removeChar(char, delay){
if ( char && char.transitionOut ) {
return char.transitionOut(delay,(char)=>{ characterGroup.remove(char); });
}
},
getPosition(){
let offset = 0;
this.chars.forEach((char)=>{
offset += char.spriteWidth + 1;
});
return offset;
}
// keydown(e){
// let char = e.key.toUpperCase(); //String.fromCharCode( e.which ).toUpperCase();
// if ( characters[char] ) {
// let character = characters[char].clone();
// character.position.x = this.message.length;
// //character.position.y = -5;
// character.transitionIn();
// scene.add(character);
// console.log(char, this.message);
// }
// }
}
})
<script src="http://codepen.io/shshaw/pen/epmrgO"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/CustomEase.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/CustomBounce.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
canvas { display: block; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment