init and that it
This commit is contained in:
commit
66916e55fc
9 changed files with 392 additions and 0 deletions
8
README.md
Normal file
8
README.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
A short experiment on Parent/Child interaction with Web Components. Creates Web Components for various ThreeJS base classes
|
||||||
|
|
||||||
|
Renderer
|
||||||
|
Scene
|
||||||
|
Group
|
||||||
|
Light
|
||||||
|
BoxGeometry
|
||||||
|
SphereGeometry
|
77
ThreeBaseGeometry.js
Normal file
77
ThreeBaseGeometry.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
|
||||||
|
const attrs_dimensions = [ 'height', 'width', 'depth' ];
|
||||||
|
const attrs_position = [ 'x', 'y', 'z' ];
|
||||||
|
const attrs_rotation = [ 'rx', 'ry', 'rz' ];
|
||||||
|
|
||||||
|
export class BaseGeometryElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
var attrs = BaseGeometryElement.observedAttributes;
|
||||||
|
|
||||||
|
for(let i in attrs) {
|
||||||
|
Object.defineProperty(this, attrs[i], {
|
||||||
|
get: function() {
|
||||||
|
return this.getAttribute(attrs[i]);
|
||||||
|
},
|
||||||
|
set: function(v) {
|
||||||
|
return this.setAttribute(attrs[i], v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return [
|
||||||
|
'color',
|
||||||
|
'height', 'width', 'depth',
|
||||||
|
'x', 'y', 'z',
|
||||||
|
'rx', 'ry', 'rz',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
parentO3DCallback() {}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
var parentEl = this.parentElement.closest('three-group, three-scene');
|
||||||
|
|
||||||
|
if(!parentEl.ready) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
parentEl.addEventListener('ready', function() {
|
||||||
|
var o3d = parentEl.nodeName == "THREE-GROUP" ? parentEl.group : parentEl.scene;
|
||||||
|
self.parentO3DCallback(o3d);
|
||||||
|
|
||||||
|
self.ready = true;
|
||||||
|
self.dispatchEvent(new CustomEvent("ready"));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var o3d = parentEl.nodeName == "THREE-GROUP" ? parentEl.group : parentEl.scene;
|
||||||
|
this.parentO3DCallback(o3d);
|
||||||
|
this.ready = true;
|
||||||
|
this.dispatchEvent(new CustomEvent("ready"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old_value, value) {
|
||||||
|
if(!this.ready) return;
|
||||||
|
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
if(attrs_dimensions.includes(name)) {
|
||||||
|
obj = this.geometry[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attrs_position.includes(name)) {
|
||||||
|
obj = this.mesh.position[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attrs_rotation.includes(name)) {
|
||||||
|
console.log(name);
|
||||||
|
obj = this.mesh.rotation[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[name] = value;
|
||||||
|
}
|
||||||
|
}
|
20
ThreeCube.js
Normal file
20
ThreeCube.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import { BaseGeometryElement } from './ThreeBaseGeometry.js';
|
||||||
|
|
||||||
|
const attrs_position = [ 'x', 'y', 'z' ];
|
||||||
|
const attrs_dimensions = [ 'height', 'width', 'depth' ];
|
||||||
|
|
||||||
|
export class CubeElement extends BaseGeometryElement {
|
||||||
|
parentO3DCallback(parent) {
|
||||||
|
var scene = this.closest('three-scene');
|
||||||
|
|
||||||
|
this.geometry = new THREE.BoxGeometry(this.width, this.height, this.depth); // width, height, depth
|
||||||
|
this.material = new THREE.MeshLambertMaterial({ color: this.color });
|
||||||
|
this.mesh = new THREE.Mesh(this.geometry, this.material);
|
||||||
|
this.mesh.position.set(this.x, this.y, this.z); // Optional, 0,0,0 is the default
|
||||||
|
|
||||||
|
parent.add(this.mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-cube', CubeElement);
|
29
ThreeGroup.js
Normal file
29
ThreeGroup.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import { BaseGeometryElement } from './ThreeBaseGeometry.js';
|
||||||
|
|
||||||
|
export class GroupElement extends BaseGeometryElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
parentO3DCallback(parent) {
|
||||||
|
this.group = new THREE.Group();
|
||||||
|
this.group.position.set(this.getAttribute('x'), this.getAttribute('y'), this.getAttribute('z'));
|
||||||
|
this.group.rotation.set(this.getAttribute('rx'), this.getAttribute('ry'), this.getAttribute('rz'));
|
||||||
|
|
||||||
|
parent.add(this.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old, value) {
|
||||||
|
if(this.group && [ 'x', 'y', 'z' ].includes(name)) {
|
||||||
|
this.group.position[name] = parseInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.group, name);
|
||||||
|
if(this.group && [ 'rx', 'ry', 'rz' ].includes(name)) {
|
||||||
|
this.group.rotation[name] = parseInt(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-group', GroupElement);
|
37
ThreeLight.js
Normal file
37
ThreeLight.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import { ThreeRendererElement } from './ThreeRendererElement.js';
|
||||||
|
|
||||||
|
export class LightElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.light = new THREE.DirectionalLight(this.getAttribute('color') || 0xfff, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return [ "color" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old_value, value) {
|
||||||
|
if(name == 'color') {
|
||||||
|
this.light.color = new THREE.Color(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
var scene = this.closest('three-scene');
|
||||||
|
|
||||||
|
if(!scene.ready) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
scene.addEventListener('ready', function() {
|
||||||
|
scene.scene.add(self.light);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
scene.scene.add(this.light);
|
||||||
|
}
|
||||||
|
this.ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-light', LightElement);
|
113
ThreeRendererElement.js
Normal file
113
ThreeRendererElement.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
|
||||||
|
THREE.Object3D.DEFAULT_UP.set(0,0,1);
|
||||||
|
|
||||||
|
export class ThreeRendererElement extends HTMLCanvasElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
var parent_boundingbox = this.parentElement.getBoundingClientRect();
|
||||||
|
this.height = parent_boundingbox.height;
|
||||||
|
this.width = parent_boundingbox.width;
|
||||||
|
|
||||||
|
this.camera_persp = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
|
||||||
|
//this.camera_ortho = new THREE.OrthographicCamera( this.width / - 2, this.width / 2, this.height / 2, this.height / - 2, 1, 1000 );
|
||||||
|
|
||||||
|
this.camera = this.camera_persp;
|
||||||
|
this.camera.position.x = 0;
|
||||||
|
this.camera.position.y = 0;
|
||||||
|
this.camera.position.z = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
var light = this.light = new THREE.AmbientLight(0xab1f2c, 1);
|
||||||
|
this.scene.add(light);
|
||||||
|
|
||||||
|
var point_light = this.point_light = new THREE.DirectionalLight(0xffffff, 0.5);
|
||||||
|
this.point_light.position.set(0, 0, 10);
|
||||||
|
this.scene.add(point_light);
|
||||||
|
*/
|
||||||
|
|
||||||
|
var renderer = this.renderer = new THREE.WebGLRenderer({
|
||||||
|
canvas: this
|
||||||
|
});
|
||||||
|
var bb = this.parentElement.getBoundingClientRect();
|
||||||
|
this.renderer.setSize(bb.width, bb.height);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// ground plane
|
||||||
|
const planeSize = 40;
|
||||||
|
const loader = new THREE.TextureLoader();
|
||||||
|
const texture = loader.load('checker.png');
|
||||||
|
texture.wrapS = THREE.RepeatWrapping;
|
||||||
|
texture.wrapT = THREE.RepeatWrapping;
|
||||||
|
texture.magFilter = THREE.NearestFilter;
|
||||||
|
const repeats = planeSize / 2;
|
||||||
|
texture.repeat.set(repeats, repeats);
|
||||||
|
|
||||||
|
const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
|
||||||
|
const planeMat = new THREE.MeshPhongMaterial({
|
||||||
|
map: texture,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
});
|
||||||
|
const plane_mesh = new THREE.Mesh(planeGeo, planeMat);
|
||||||
|
window.plane_mesh = plane_mesh;
|
||||||
|
//plane_mesh.rotation.x = Math.PI * -.5;
|
||||||
|
this.scene.add(plane_mesh);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// XXX applWHY?
|
||||||
|
this.do_animate = this.do_animate.bind(this);
|
||||||
|
this.do_animate();
|
||||||
|
|
||||||
|
// TODO improve value checking, current src="" will be true
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.childObserver = new MutationObserver(function(changes) {
|
||||||
|
})
|
||||||
|
|
||||||
|
this.childObserver.observe(this, {
|
||||||
|
attributes: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return [ ];
|
||||||
|
}
|
||||||
|
|
||||||
|
do_animate() {
|
||||||
|
// TODO handle pausing
|
||||||
|
requestAnimationFrame(this.do_animate);
|
||||||
|
|
||||||
|
if(this.active_scene) {
|
||||||
|
this.renderer.render(this.active_scene, this.camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, old_value, value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedCallack() {
|
||||||
|
console.log('parsedCallack');
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRenderer() {
|
||||||
|
this.scenes = this.querySelectorAll('three-scene');
|
||||||
|
this.active_scene = this.scenes[this.scenes.length - 1].scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
if(document.readyState != "complete") {
|
||||||
|
document.addEventListener("DOMContentLoaded", this.setupRenderer.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent("renderer-ready", {
|
||||||
|
cancelable: false
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-renderer', ThreeRendererElement, { extends: 'canvas' });
|
34
ThreeScene.js
Normal file
34
ThreeScene.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import { ThreeRendererElement } from './ThreeRendererElement.js';
|
||||||
|
|
||||||
|
export class SceneElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rendererConnected() {
|
||||||
|
console.log('fjlkjsdflksjdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
findRenderer() {
|
||||||
|
var p = this;
|
||||||
|
while((p = p.parentElement) !== undefined)
|
||||||
|
if(p instanceof ThreeRendererElement) return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.scene = new THREE.Scene();
|
||||||
|
this.scene.background = new THREE.Color(this.getAttribute('background-color') || 0xababab);
|
||||||
|
if(!(this.parentElement instanceof ThreeRendererElement)) {
|
||||||
|
throw Error("Parent not ThreeRendererElement");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parentElement.addEventListener("connected", this.rendererConnected.bind(this));
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent('ready'));
|
||||||
|
this.ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-scene', SceneElement);
|
18
ThreeSphere.js
Normal file
18
ThreeSphere.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import { BaseGeometryElement } from './ThreeBaseGeometry.js';
|
||||||
|
|
||||||
|
const attrs_position = [ 'x', 'y', 'z' ];
|
||||||
|
const attrs_dimensions = [ 'height', 'width', 'depth' ];
|
||||||
|
|
||||||
|
export class SphereElement extends BaseGeometryElement {
|
||||||
|
parentO3DCallback(parent) {
|
||||||
|
this.geometry = new THREE.SphereGeometry(this.getAttribute('radius')); // width, height, depth
|
||||||
|
this.material = new THREE.MeshLambertMaterial({ color: this.color });
|
||||||
|
this.mesh = new THREE.Mesh(this.geometry, this.material);
|
||||||
|
this.mesh.position.set(this.x, this.y, this.z); // Optional, 0,0,0 is the default
|
||||||
|
|
||||||
|
parent.add(this.mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('three-sphere', SphereElement);
|
56
index.html
Normal file
56
index.html
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://esm.sh/three';
|
||||||
|
import './ThreeRendererElement.js'
|
||||||
|
import './ThreeCube.js';
|
||||||
|
import './ThreeGroup.js';
|
||||||
|
import './ThreeLight.js';
|
||||||
|
import './ThreeScene.js';
|
||||||
|
import './ThreeSphere.js';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var group = document.querySelector('three-group');
|
||||||
|
var sphere = document.querySelector('three-sphere');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframe position {
|
||||||
|
from {
|
||||||
|
x: 140;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
x: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
three-cube.left {
|
||||||
|
animation-duration: 3s;
|
||||||
|
animation-name: position;
|
||||||
|
x: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<canvas is=three-renderer>
|
||||||
|
<three-scene name="main">
|
||||||
|
<three-light color="#ee00aa"></three-light>
|
||||||
|
<three-cube color="#ffffff" height=40 width=90 depth=10 x=0 y=40 z=-200 focus></three-cube>
|
||||||
|
<three-sphere color="#a0a00f" radius=30 x=0 y=40 z=-200 focus></three-sphere>
|
||||||
|
|
||||||
|
<three-group x=35 y=20 rz=30>
|
||||||
|
<three-cube color="#ffffff" height=40 width=90 depth=10 x=0 y=40 z=-200 focus></three-cube>
|
||||||
|
<three-sphere color="#a0a00f" radius=30 x=0 y=40 z=-200 focus></three-sphere>
|
||||||
|
</three-group>
|
||||||
|
</three-scene>
|
||||||
|
</canvas>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue