1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

Mesh events : mouseover, click (close #1)

This commit is contained in:
Kevin Levron 2021-02-22 00:44:29 +01:00
parent adf64ecb6f
commit 9fbe5696be
4 changed files with 120 additions and 15 deletions

View File

@ -1,17 +1,24 @@
<template> <template>
<Renderer ref="renderer"> <Renderer ref="renderer" mouse-over click>
<Camera :position="{ z: 10 }" /> <Camera :position="{ z: 10 }" />
<Scene> <Scene>
<PointLight :position="{ y: 50, z: 50 }" /> <PointLight :position="{ y: 50, z: 50 }" />
<Box ref="box" :size="1" :rotation="{ y: Math.PI / 4, z: Math.PI / 4 }"> <Group>
<LambertMaterial /> <Box ref="box" @hover="boxHover" @click="boxClick" :size="1" :rotation="{ y: Math.PI / 4, z: Math.PI / 4 }">
</Box> <LambertMaterial :color="boxColor" />
</Box>
</Group>
</Scene> </Scene>
</Renderer> </Renderer>
</template> </template>
<script> <script>
export default { export default {
data() {
return {
boxColor: '#ffffff',
};
},
mounted() { mounted() {
const renderer = this.$refs.renderer; const renderer = this.$refs.renderer;
const box = this.$refs.box.mesh; const box = this.$refs.box.mesh;
@ -19,5 +26,14 @@ export default {
box.rotation.x += 0.01; box.rotation.x += 0.01;
}); });
}, },
methods: {
boxHover({ over }) {
if (over) this.boxColor = '#ff0000';
else this.boxColor = '#ffffff';
},
boxClick() {
console.log(arguments, 'click');
},
},
}; };
</script> </script>

View File

@ -8,6 +8,8 @@ export default {
autoClear: { type: Boolean, default: true }, autoClear: { type: Boolean, default: true },
mouseMove: { type: [Boolean, String], default: false }, mouseMove: { type: [Boolean, String], default: false },
mouseRaycast: { type: Boolean, default: false }, mouseRaycast: { type: Boolean, default: false },
mouseOver: { type: Boolean, default: false },
click: { type: Boolean, default: false },
orbitCtrl: { type: [Boolean, Object], default: false }, orbitCtrl: { type: [Boolean, Object], default: false },
resize: { type: [Boolean, String], default: true }, resize: { type: [Boolean, String], default: true },
shadow: Boolean, shadow: Boolean,
@ -37,6 +39,8 @@ export default {
orbit_ctrl: this.orbitCtrl, orbit_ctrl: this.orbitCtrl,
mouse_move: this.mouseMove, mouse_move: this.mouseMove,
mouse_raycast: this.mouseRaycast, mouse_raycast: this.mouseRaycast,
mouse_over: this.mouseOver,
click: this.click,
resize: this.resize, resize: this.resize,
width: this.width, width: this.width,
height: this.height, height: this.height,

View File

@ -21,6 +21,8 @@ export default function useThree() {
orbit_ctrl: false, orbit_ctrl: false,
mouse_move: false, mouse_move: false,
mouse_raycast: false, mouse_raycast: false,
mouse_over: false,
click: false,
resize: true, resize: true,
width: 0, width: 0,
height: 0, height: 0,
@ -44,6 +46,9 @@ export default function useThree() {
const mousePlane = new Plane(new Vector3(0, 0, 1), 0); const mousePlane = new Plane(new Vector3(0, 0, 1), 0);
const raycaster = new Raycaster(); const raycaster = new Raycaster();
// raycast objects
const intersectObjects = [];
// returned object // returned object
const obj = { const obj = {
conf, conf,
@ -62,6 +67,7 @@ export default function useThree() {
onAfterInit, onAfterInit,
onAfterResize, offAfterResize, onAfterResize, offAfterResize,
onBeforeRender, offBeforeRender, onBeforeRender, offBeforeRender,
addIntersectObject, removeIntersectObject,
}; };
/** /**
@ -103,6 +109,7 @@ export default function useThree() {
window.addEventListener('resize', onResize); window.addEventListener('resize', onResize);
} }
conf.mouse_move = conf.mouse_move || conf.mouse_over;
if (conf.mouse_move) { if (conf.mouse_move) {
if (conf.mouse_move === 'body') { if (conf.mouse_move === 'body') {
obj.mouse_move_element = document.body; obj.mouse_move_element = document.body;
@ -113,6 +120,10 @@ export default function useThree() {
obj.mouse_move_element.addEventListener('mouseleave', onMouseleave); obj.mouse_move_element.addEventListener('mouseleave', onMouseleave);
} }
if (conf.click) {
obj.renderer.domElement.addEventListener('click', onClick);
}
afterInitCallbacks.forEach(c => c()); afterInitCallbacks.forEach(c => c());
return true; return true;
@ -171,6 +182,25 @@ export default function useThree() {
obj.composer.render(); 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 * remove listeners
*/ */
@ -181,37 +211,76 @@ export default function useThree() {
obj.mouse_move_element.removeEventListener('mousemove', onMousemove); obj.mouse_move_element.removeEventListener('mousemove', onMousemove);
obj.mouse_move_element.removeEventListener('mouseleave', onMouseleave); obj.mouse_move_element.removeEventListener('mouseleave', onMouseleave);
} }
obj.renderer.domElement.removeEventListener('click', onClick);
if (obj.orbitCtrl) obj.orbitCtrl.dispose(); if (obj.orbitCtrl) obj.orbitCtrl.dispose();
this.renderer.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 * mousemove listener
*/ */
function onMousemove(e) { function onMousemove(e) {
mouse.x = (e.clientX / size.width) * 2 - 1; mouse.x = (e.clientX / size.width) * 2 - 1;
mouse.y = -(e.clientY / size.height) * 2 + 1; mouse.y = -(e.clientY / size.height) * 2 + 1;
updateMouseV3(); onMousechange(e);
} }
/** /**
* mouseleave listener * mouseleave listener
*/ */
function onMouseleave(e) { function onMouseleave(e) {
mouse.x = 0; // mouse.x = 0;
mouse.y = 0; // mouse.y = 0;
updateMouseV3(); onMousechange(e);
} }
/** /**
* get 3d mouse position * mouse change
*/ */
function updateMouseV3() { function onMousechange(e) {
if (conf.mouse_raycast) { if (conf.mouse_over || conf.mouse_raycast) {
obj.camera.getWorldDirection(mousePlane.normal);
mousePlane.normal.normalize();
raycaster.setFromCamera(mouse, obj.camera); 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);
}
}
}
} }
} }

View File

@ -12,6 +12,8 @@ export default {
scale: Object, scale: Object,
castShadow: Boolean, castShadow: Boolean,
receiveShadow: Boolean, receiveShadow: Boolean,
onHover: Function,
onClick: Function,
}, },
// can't use setup because it will not be used in sub components // can't use setup because it will not be used in sub components
// setup() {}, // setup() {},
@ -29,7 +31,10 @@ export default {
}, },
unmounted() { unmounted() {
// console.log('Mesh 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.geometry) this.geometry.dispose();
if (this.material && !this.materialId) this.material.dispose(); if (this.material && !this.materialId) this.material.dispose();
}, },
@ -39,6 +44,17 @@ export default {
this.material = this.three.materials[this.materialId]; this.material = this.three.materials[this.materialId];
} }
this.mesh = new Mesh(this.geometry, this.material); 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.bindProps();
this.parent.add(this.mesh); this.parent.add(this.mesh);
this.$emit('ready'); this.$emit('ready');