Last active
October 19, 2021 14:26
-
-
Save jeff-silva/d8b191142b6765dea724f6c6e0b9d108 to your computer and use it in GitHub Desktop.
ThreeJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as THREE from 'https://unpkg.com/three@0.127.0/build/three.module.js'; | |
import { CSS3DRenderer, CSS3DObject, CSS3DSprite } from 'https://unpkg.com/three@0.127.0/examples/jsm/renderers/CSS3DRenderer.js'; | |
export default class { | |
settings = { | |
rendererType: 'WebGLRenderer', | |
renderer: { | |
antialias: true, | |
alpha: true, | |
}, | |
}; | |
constructor(attributes={}) { | |
// Create methods and attributes | |
let attrs = Object.getOwnPropertyNames(Object.getPrototypeOf(this)); | |
for(let i in attributes) { | |
if (attrs.indexOf(i)>=0 || typeof this[i]!='undefined') continue; | |
this[i] = attributes[i]; | |
delete attributes[i]; | |
} | |
if (!this.el) return; | |
this.width = this.el.offsetWidth; | |
this.height = this.el.offsetHeight; | |
this.clock = new THREE.Clock(); | |
this.events = this.events||[]; | |
this.settings = this.jsonMerge(this.settings, attributes.settings||{}); | |
// Before create | |
this.runEvents('beforeCreate', {}); | |
// Scene | |
if (!this.scene) { | |
this.scene = new THREE.Scene(); | |
} | |
// Camera | |
if (!this.camera) { | |
this.camera = new THREE.PerspectiveCamera(50, this.width/this.height, .1, 1000); | |
this.camera.position.y = 1; | |
} | |
// Grid Helper | |
if (this.settings.gridHelper) { | |
this.settings.gridHelper = Object.assign({ | |
size: 10, | |
divisions: 10, | |
}, this.settings.gridHelper||{}); | |
this.gridHelper = new THREE.GridHelper(this.settings.gridHelper.size, this.settings.gridHelper.divisions); | |
this.scene.add(this.gridHelper); | |
} | |
// Renderer | |
this.renderer = this.renderer || (() => { | |
let renderer; | |
if (this.settings.rendererType=='CSS3DRenderer') { | |
renderer = new CSS3DRenderer(); | |
} | |
else { | |
renderer = new THREE.WebGLRenderer(this.settings.renderer); | |
renderer.setPixelRatio(this.width/this.height); | |
} | |
this.el.appendChild(renderer.domElement); | |
renderer.setSize(this.width, this.height); | |
return renderer; | |
})(); | |
window.addEventListener('resize', (ev) => { | |
this.width = this.el.offsetWidth; | |
this.height = this.el.offsetHeight; | |
this.camera.aspect = this.width / this.height; | |
this.camera.updateProjectionMatrix(); | |
this.renderer.setSize(this.width, this.height); | |
}); | |
// App focused | |
this.focused = false; | |
['click'].forEach(evt => { | |
window.addEventListener(evt, (ev) => { | |
this.focused = this.el.contains(ev.target); | |
}); | |
}); | |
// Mouse events | |
this.mouse = {}; | |
['mousemove', 'click', 'keyup', 'wheel'].forEach(evt => { | |
this.el.addEventListener(evt, (ev) => { | |
this.mouse = { | |
type: ev.type, | |
x: ev.layerX, | |
y: ev.layerY, | |
lastX: (this.mouse.x||0), | |
lastY: (this.mouse.y||0), | |
speed: 0, | |
buttons: ev.buttons, | |
ctrlKey: ev.ctrlKey, | |
shiftKey: ev.shiftKey, | |
altKey: ev.altKey, | |
}; | |
this.runEvents(evt, ev); | |
}); | |
}); | |
['keyup', 'keydown'].forEach(evt => { | |
window.addEventListener(evt, ev => { | |
this.runEvents(evt, ev); | |
this.runEvents(`${evt}:${ev.key.toLowerCase()}`, ev); | |
}); | |
}); | |
this.runEvents('created', {}); | |
this.animate(); | |
} | |
animate() { | |
requestAnimationFrame(this.animate.bind(this)); | |
this.render(); | |
} | |
render() { | |
this.runEvents('beforeUpdate', {}); | |
this.renderer.render(this.scene, this.camera); | |
this.runEvents('updated', {}); | |
} | |
on(type, callback) { | |
this.events.push({type, callback}); | |
} | |
runEvents() { | |
var args = [].slice.call(arguments); | |
let type = args.shift(); | |
this.events.forEach(evt => { | |
if (evt.type!=type) return; | |
evt.callback.apply(this, [this, ...args]); | |
}); | |
} | |
raycastMouseIntersects(ev) { | |
let mouse = new THREE.Vector2(); | |
mouse.x = ( ev.layerX / this.width ) * 2 - 1; | |
mouse.y = - ( ev.layerY / this.height ) * 2 + 1; | |
let raycaster = new THREE.Raycaster(); | |
raycaster.setFromCamera(mouse, this.camera); | |
return raycaster.intersectObjects(this.scene.children); | |
} | |
jsonMerge(item1, item2) { | |
let _isObject = function(item) { return (item && typeof item === 'object' && !Array.isArray(item)); }; | |
if (_isObject(item1) && _isObject(item2)) { | |
item1 = Object.assign({}, item1, item2); | |
for(let i in item1) { | |
item1[i] = this.jsonMerge(item1[i], item2[i]); | |
} | |
} | |
return item1; | |
} | |
} | |
/* | |
el: HTML element target | |
settings.gridHelper: Grid Helper. Accepts false, true or json {size:10, divisions:10} | |
settings.renderer: Renderer settings | |
https://threejs.org/docs/#api/en/renderers/WebGLRenderer | |
How to use: | |
import App from 'App.js'; | |
new App({ | |
el: document.querySelector('.element'), | |
events: [ | |
{type:'create', callback: (app, ev) => { | |
console.log('create'); | |
}}, | |
{type:'update', callback: (app, ev) => { | |
console.log('update'); | |
}}, | |
{type:'click', callback: (app, ev) => { | |
console.log('click'); | |
}}, | |
{type:'update', callback: (app, ev) => { | |
console.log('update'); | |
}}, | |
], | |
}); | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Document</title> | |
</head> | |
<body> | |
<div id="app" style="height:400px; border:solid 1px #e5e5e5;"></div> | |
<script type="module"> | |
import * as THREE from 'https://cdn.skypack.dev/three'; | |
import { OrbitControls } from 'https://cdn.skypack.dev/three@/examples/jsm/controls/OrbitControls.js'; | |
class App { | |
constructor(params={}) { | |
params = { | |
el: "body", | |
...params | |
}; | |
if (typeof params.el=='string') { | |
params.el = document.querySelector(params.el); | |
} | |
this.el = params.el; | |
this.width = this.el.offsetWidth; | |
this.height = this.el.offsetHeight; | |
this.scene = Object.assign(new THREE.Scene(), { | |
background: new THREE.Color(0xeeffff), | |
}); | |
this.camera = new THREE.PerspectiveCamera(40, this.width/this.height, 1, 100); | |
this.renderer = new THREE.WebGLRenderer( { antialias: true } ); | |
// this.renderer.setPixelRatio(window.devicePixelRatio); | |
this.renderer.setSize(this.width, this.height); | |
this.renderer.outputEncoding = THREE.sRGBEncoding; | |
this.el.appendChild(this.renderer.domElement); | |
this.create(); | |
setInterval(() => { | |
requestAnimationFrame(time => { | |
this.update(); | |
this.renderer.render(this.scene, this.camera); | |
}); | |
}, 1000/60); | |
window.addEventListener('resize', ev => { | |
this.width = this.el.offsetWidth; | |
this.height = this.el.offsetHeight; | |
this.camera.aspect = this.width/this.height; | |
this.camera.updateProjectionMatrix(); | |
this.renderer.setSize(this.width, this.height); | |
}); | |
} | |
create() { | |
this.camera.position.x = 5; | |
this.camera.position.y = 5; | |
this.controls = new OrbitControls(this.camera, this.renderer.domElement); | |
this.scene.add(new THREE.GridHelper(10, 10)); | |
const geometry = new THREE.TorusKnotGeometry(1, .2, 64, 8); | |
const material = new THREE.MeshBasicMaterial({ | |
color: 0x000044, | |
}); | |
this.scene.add(this.torusKnot = new THREE.Mesh(geometry, material)); | |
} | |
update() { | |
this.torusKnot.rotation.y += .02; | |
} | |
} | |
new App({el: "#app"}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment