diff --git a/src/components/examples/Example1.vue b/src/components/examples/Example1.vue
index 1bc2b7a..250af89 100644
--- a/src/components/examples/Example1.vue
+++ b/src/components/examples/Example1.vue
@@ -1,17 +1,24 @@
-
+
-
-
-
+
+
+
+
+
diff --git a/src/core/Renderer.js b/src/core/Renderer.js
index f07d814..ed0639d 100644
--- a/src/core/Renderer.js
+++ b/src/core/Renderer.js
@@ -8,6 +8,8 @@ export default {
autoClear: { type: Boolean, default: true },
mouseMove: { type: [Boolean, String], default: false },
mouseRaycast: { type: Boolean, default: false },
+ mouseOver: { type: Boolean, default: false },
+ click: { type: Boolean, default: false },
orbitCtrl: { type: [Boolean, Object], default: false },
resize: { type: [Boolean, String], default: true },
shadow: Boolean,
@@ -37,6 +39,8 @@ export default {
orbit_ctrl: this.orbitCtrl,
mouse_move: this.mouseMove,
mouse_raycast: this.mouseRaycast,
+ mouse_over: this.mouseOver,
+ click: this.click,
resize: this.resize,
width: this.width,
height: this.height,
diff --git a/src/core/useThree.js b/src/core/useThree.js
index 1da7255..eceb8cc 100644
--- a/src/core/useThree.js
+++ b/src/core/useThree.js
@@ -21,6 +21,8 @@ export default function useThree() {
orbit_ctrl: false,
mouse_move: false,
mouse_raycast: false,
+ mouse_over: false,
+ click: false,
resize: true,
width: 0,
height: 0,
@@ -44,6 +46,9 @@ export default function useThree() {
const mousePlane = new Plane(new Vector3(0, 0, 1), 0);
const raycaster = new Raycaster();
+ // raycast objects
+ const intersectObjects = [];
+
// returned object
const obj = {
conf,
@@ -62,6 +67,7 @@ export default function useThree() {
onAfterInit,
onAfterResize, offAfterResize,
onBeforeRender, offBeforeRender,
+ addIntersectObject, removeIntersectObject,
};
/**
@@ -103,6 +109,7 @@ export default function useThree() {
window.addEventListener('resize', onResize);
}
+ conf.mouse_move = conf.mouse_move || conf.mouse_over;
if (conf.mouse_move) {
if (conf.mouse_move === 'body') {
obj.mouse_move_element = document.body;
@@ -113,6 +120,10 @@ export default function useThree() {
obj.mouse_move_element.addEventListener('mouseleave', onMouseleave);
}
+ if (conf.click) {
+ obj.renderer.domElement.addEventListener('click', onClick);
+ }
+
afterInitCallbacks.forEach(c => c());
return true;
@@ -171,6 +182,25 @@ export default function useThree() {
obj.composer.render();
}
+ /**
+ * add intersect object
+ */
+ function addIntersectObject(o) {
+ if (intersectObjects.indexOf(o) === -1) {
+ intersectObjects.push(o);
+ }
+ }
+
+ /**
+ * remove intersect object
+ */
+ function removeIntersectObject(o) {
+ const i = intersectObjects.indexOf(o);
+ if (i !== -1) {
+ intersectObjects.splice(i, 1);
+ }
+ }
+
/**
* remove listeners
*/
@@ -181,37 +211,76 @@ export default function useThree() {
obj.mouse_move_element.removeEventListener('mousemove', onMousemove);
obj.mouse_move_element.removeEventListener('mouseleave', onMouseleave);
}
+ obj.renderer.domElement.removeEventListener('click', onClick);
if (obj.orbitCtrl) obj.orbitCtrl.dispose();
this.renderer.dispose();
}
+ /**
+ * click listener
+ */
+ function onClick(e) {
+ mouse.x = (e.clientX / size.width) * 2 - 1;
+ mouse.y = -(e.clientY / size.height) * 2 + 1;
+ raycaster.setFromCamera(mouse, obj.camera);
+ const objects = raycaster.intersectObjects(intersectObjects);
+ for (let i = 0; i < objects.length; i++) {
+ const o = objects[i].object;
+ if (o.onClick) o.onClick(e);
+ }
+ }
+
/**
* mousemove listener
*/
function onMousemove(e) {
mouse.x = (e.clientX / size.width) * 2 - 1;
mouse.y = -(e.clientY / size.height) * 2 + 1;
- updateMouseV3();
+ onMousechange(e);
}
/**
* mouseleave listener
*/
function onMouseleave(e) {
- mouse.x = 0;
- mouse.y = 0;
- updateMouseV3();
+ // mouse.x = 0;
+ // mouse.y = 0;
+ onMousechange(e);
}
/**
- * get 3d mouse position
+ * mouse change
*/
- function updateMouseV3() {
- if (conf.mouse_raycast) {
- obj.camera.getWorldDirection(mousePlane.normal);
- mousePlane.normal.normalize();
+ function onMousechange(e) {
+ if (conf.mouse_over || conf.mouse_raycast) {
raycaster.setFromCamera(mouse, obj.camera);
- raycaster.ray.intersectPlane(mousePlane, mouseV3);
+
+ if (conf.mouse_raycast) {
+ // get mouse 3d position
+ obj.camera.getWorldDirection(mousePlane.normal);
+ mousePlane.normal.normalize();
+ raycaster.ray.intersectPlane(mousePlane, mouseV3);
+ }
+
+ if (conf.mouse_over) {
+ const onObjects = raycaster.intersectObjects(intersectObjects);
+ const offObjects = [...intersectObjects];
+ for (let i = 0; i < onObjects.length; i++) {
+ const o = onObjects[i].object;
+ if (!o.hover && o.onHover) {
+ o.hover = true;
+ o.onHover(true);
+ }
+ offObjects.splice(offObjects.indexOf(o), 1);
+ }
+ for (let i = 0; i < offObjects.length; i++) {
+ const o = offObjects[i];
+ if (o.hover && o.onHover) {
+ o.hover = false;
+ o.onHover(false);
+ }
+ }
+ }
}
}
diff --git a/src/meshes/Mesh.js b/src/meshes/Mesh.js
index a737591..18b325b 100644
--- a/src/meshes/Mesh.js
+++ b/src/meshes/Mesh.js
@@ -12,6 +12,8 @@ export default {
scale: Object,
castShadow: Boolean,
receiveShadow: Boolean,
+ onHover: Function,
+ onClick: Function,
},
// can't use setup because it will not be used in sub components
// setup() {},
@@ -29,7 +31,10 @@ export default {
},
unmounted() {
// console.log('Mesh unmounted');
- if (this.mesh) this.parent.remove(this.mesh);
+ if (this.mesh) {
+ this.three.removeIntersectObject(this.mesh);
+ this.parent.remove(this.mesh);
+ }
if (this.geometry) this.geometry.dispose();
if (this.material && !this.materialId) this.material.dispose();
},
@@ -39,6 +44,17 @@ export default {
this.material = this.three.materials[this.materialId];
}
this.mesh = new Mesh(this.geometry, this.material);
+
+ if (this.onHover) {
+ this.mesh.onHover = (over) => { this.onHover({ component: this, over }); };
+ this.three.addIntersectObject(this.mesh);
+ }
+
+ if (this.onClick) {
+ this.mesh.onClick = (e) => { this.onClick({ component: this, event: e }); };
+ this.three.addIntersectObject(this.mesh);
+ }
+
this.bindProps();
this.parent.add(this.mesh);
this.$emit('ready');