Giter Club home page Giter Club logo

three.interactive's Introduction

THREE.Interactive

NPM Package

Fast and simple interaction manager for THREE.js for enabling pointer, mouse and touch events on 3D objects.

Note: When using ReactJS I can highly recommend react-three-fiber, which has built-in interaction support. For pure THREE.js projects, this little library can be very useful though.

ESM only. Currently no CJS version is built.

How it works:

  • Interactive Objects (THREE.Object3D) are added to the InteractionManager, which fires instances of InteractiveEvent.

  • Differenciates between mouseover/mouseout (closest objects) and mouseenter/mouseleave (all objects) events.

  • Intersections are sorted by distance to the camera and the events are dispatched in that order (closest first). If InteractiveEvent.stopPropagation() is called, the event won't fire again on other objects.

Alternative to three.interaction.

Collaborations and improvements are welcome.

Examples

  • Simple: Basic example
  • Auto Add: Auto-add example, still beta
  • Depth: Overlapping objects example
  • glTF: Hover/click gltf objects example

Usage

yarn add three.interactive

or

npm install three.interactive
  1. Include script:
import { InteractionManager } from 'three.interactive';
  1. Create an InteractionManager instance
const interactionManager = new InteractionManager(
  renderer,
  camera,
  renderer.domElement
);
  1. Add object to InteractionManager
interactionManager.add(cube);
  1. Add event listener to object
cube.addEventListener('click', (event) => {});
  1. Call InteractionManager.update() on each render
interactionManager.update();

Simple example

import * as THREE from 'three';
import { InteractionManager } from 'three.interactive';

const container = document.createElement('div');
container.setAttribute('id', 'container');
document.body.appendChild(container);

const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0.0, 0.0, 10.0);

const interactionManager = new InteractionManager(
  renderer,
  camera,
  renderer.domElement
);

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial();

const cube = new THREE.Mesh(geometry, material);
cube.addEventListener('mouseover', (event) => {
  event.target.material.color.set(0xff0000);
  document.body.style.cursor = 'pointer';
});
cube.addEventListener('mouseout', (event) => {
  event.target.material.color.set(0xffffff);
  document.body.style.cursor = 'default';
});
cube.addEventListener('mousedown', (event) => {
  event.target.scale.set(1.1, 1.1, 1.1);
});
cube.addEventListener('click', (event) => {
  event.target.scale.set(1.0, 1.0, 1.0);
});
scene.add(cube);
interactionManager.add(cube);

const animate = (time) => {
  requestAnimationFrame(animate);

  interactionManager.update();

  renderer.render(scene, camera);
};

animate();

API

InteractionManager class

new InteractionManager(renderer, camera, renderer.domElement [, { autoAdd: false, scene, bindEventsOnBodyElement: true } ])

Constructor of InteractionManager instance; if the autoAdd option (still beta) is used, there is no need for adding objects to InteractionManager manually and calling interactionManager.update(); In this mode, the scene needs to be provided in the options.

Members:

Member Type  Default  Description
treatTouchEventsAsMouseEvents boolean true Whether touch events should fire as mouse events

Methods:

Method Description
add(object, childNames = []) Add object(s), optionally select only children of object by their names
remove(object, childNames = []) Remove object(s), optionally select only children of object by their names
update() Update InteractionManager on each render
dispose() Dispose InteractionManager

InteractionManagerOptions class

new InteractionManagerOptions({ autoAdd: false, scene, bindEventsOnBodyElement: true })

Constructor of InteractionManagerOptions instance

InteractiveEvent class

Members:

Member Type  Default  Description
cancelBubble boolean false Whether events should continue to bubble
coords THREE.Vector2 Mouse/touch coords
distance Number Distance of intersected point from camera
intersected boolean Whether object is still intersected
originalEvent Event object Original event, if available (MouseEvent, TouchEvent or PointerEvent)
target THREE.Object3D Target object
type string event type: 'click', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend', 'pointerdown', 'pointerup', 'pointermove'

Methods:

Method Description
stopPropagation Stop bubbling of event (cancelBubble), e.g. when only the object closest to the camera is supposed to fire an event

Editing source

In order to edit the source code, run:

yarn start

And open http://127.0.0.1:8000/ in your browers.

The files in the build folder will automatically be rebuilt when the files in the src folder are modified.

License

MIT licensed

Created by Markus Lerner & contributors

three.interactive's People

Contributors

khaiknievel avatar markuslerner avatar restless-khai avatar stefnotch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

three.interactive's Issues

The `type` property is a little bit confusing

The document says

type (string) – event type: 'click', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend', 'pointerdown', 'pointerup'

So it seems like it is possible to have a type to be a pointerdown or a pointerup. But after reading the codes I have found that it is impossible to be like that, because the pointer events use the mouse events' handlers (code is here), and the mouse events just fire mouseup and mousedown.

So it is a little bit confusing, this is what you think:

// A cube can have a "pointerdown" event because the document says so.
cube.addEventListener('pointerdown', (event) => {
  // ...
});

but this is what you actually have to do:

// Well I just have to use "mousedown" event.
// But this handler has been registered as "pointerdown" actually.
cube.addEventListener('mousedown', (event) => {
  // ...
});

So this is my suggestion:

  1. Get rid of the pointerdown and pointerup from the document.
  2. Fire pointerdown and pointerup when PointerEvent is supported.

Best wishes

Intuitive Design

We are excited to find your great library. We noticed the documentation mentions https://github.com/jasonChen1982/three.interaction.js
which is really intuitive lib, it just drops in and worked until it stopped being maintained.

We noticed that the api for three.interactive is almost identical, with one exception,
this lib requires:

interactionManager.add(cube);

With three.interaction this repeating interactionManager.add(object) call isn't necessary, so using it felt intuitive and easy throughout the application. Meanwhile three.interactive requires passing reference to the manager around everywhere and calling add() for every object? Why not make it automatically add itself just like three.interaction?

interactionManager.update() event based

Thanks for that great work!

One thing I wondered about was the "interactionManager.update();"
I added it to the render function but didn't get any mousemove events, unless I clicked on one of my objects (then the "click" and the "enter/leave" events were triggerd).
Then I found out you use the update() inside the animation but in my app I do not have any animation function as I use three event based. (In my opininion this is something like polling, and polling should be used only if there is no other way...)

For me I moved the update() to the onDocumentMouseMove(e) (just added this.update();)
and removed the update on rendering. so, every time document triggers the mouseMove event, the interactionManager will be updated.

Do you see any problems on doing it that way?

Frame rate goes to 1 with autoAdd

The new autoAdd feature is excellent for intuitive user experience, but performance drops to 1 frame per second.

Any thoughts on how to configure it to prevent that? I added it as commend in #20 but wasn't sure if it belongs as separate issue now.

On mobile onclick not working

On android one plus 8 with chrome and many other mobile devices the clickmanager is not working. It only works after rotating the phone to landscape mode.

The onclick handlers does work in portrait but not on the 3d object it self it feels like the click register happens on the wrong orientantion. That it using the height/wiodthto see if it clicks on the object instead of height/width.

It is recommended to mention the `name` field in the documentation

A common programming idiom is to call add() when the object (interactive) is created, and then call remove() when the object is destroyed.

However, due to the lack of understanding of the source code details, if the name field of the object is not assigned, it will cause all objects called remove() to be removed.

https://github.com/markuslerner/THREE.Interactive/blob/v1.6.1/src/index.ts#L223

remove() {
   if (!object) return;
   const filterSet = new Set<string>(
     childNames.length > 0 ? childNames : [object.name]  // If `name` is `undefined` ?
   );

  this.interactiveObjects = this.interactiveObjects.filter(
    (o) => !filterSet.has(o.name)  // If `name` is `undefined` ?
  );
}

So, if I understand correctly, the name field value of a 3D object should be unique? If yes, is this critical and should it be specifically mentioned in the docs?

mouseup bug

On my end, v.1.1.0

mouseup triggers outside of specified object.

had to implement this workaround to work as wanted . looking for 'intersected' key value to be true.

object.addEventListener('mouseup', function (event) {

            if (event.intersected) {

                   console.log('hello');

            }

        })

Prevent pointermove/mousemove events on the body element

First of all thanks for this awesome lib, really helpfull in a project I am working on.

And it seems that the InteractionManager also binds mousemove/pointermove events to the body element:

domElement.ownerDocument.addEventListener(

In my situation I show a popup when someone clicks something in the 3D scene. But hover events are still fired when someone moves his mouse in the popup.

A simple option to prevent the event binding to the body would be great.

Typescript types

I stumbled upon this neat library, but quickly realized that it doesn't have any Typescript types. Could that be improved and would pull requests that do so be appreciated?

Not working with three 140

I am having problem installing the npm package.
npm ERR! Could not resolve dependency:
npm ERR! peer three@"^0.138.0" from [email protected]

I am using "three": "^0.140.2"

Using -force solves the problem.
npm i --force three.interactive

Mouse click event bug?

Hello, I have a problem, I have bound the click event to object A. Then I pressed the left mouse button on a blank space but did not release it, then I dragged the mouse to rotate the space (added orbitcontrol), when the mouse is on object A and released the left mouse button, it will activate the click event of A. This caused me to inadvertently activate the event when I just wanted to rotate the space.

Something is off with Safari

I'm trying to figure out what is going on with my app and safari. I was able to trace it down to THREE.Interactive as the part which causes the crash. The crash only tells me "SyntaxError: Left hand side of operator '=' must be a reference." and something about promiseReactionJob, but no other details, no line number.

What I tried:

  • I've played with my code and removed Three.Interactive, and it starts working.
  • Tried to isolate things and only reference classes like this console.log(InteractionManager); and it still fails.
  • I included the main ts file from Three.Interactive and removed the dependency and it works.
  • Development builds are working, only production suffers, so should be something with the optimizer

So it feels like something is off when the dependency is used, since if I natively bring in the class and use it, everything is fine. I know THREE had a similar issue a few years ago from this page: angular/angular-cli#21107

Repo: https://github.com/mgatelabs/GenshinSolvers
Live: https://mgatelabs.github.io/GenshinSolvers/ (This will have the internal version of Three.Interactive for now)

Also, I tried the sample apps linked to the Readme and in safari they are working, so unsure what is going on.

Uncaught TypeError: Error resolving specificator module « three »

Hello Mark,

Thanks for your great work
I try to integrate your module on a webserver but I get this error, I think it's because my browser can't resolve the name "three" in your code (import{Raycaster as a,Vector2 as h}from"three")
I have tried to change "three" with "https://threejs.org/build/three.min.js" where belong three.js on my server but i get an other error :
Uncaught SyntaxError: ambiguous indirect export: Raycaster

Any thoughts ?
Thanks
Chris

Module parse Failed

I install the library and when I import it I am geting this error


ERROR in ./node_modules/three.interactive/build/three.interactive.js 3005:231723
Module parse failed: Unexpected token (3005:231723)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| 	float std_dev = sqrt( squared_mean - mean * mean );
| 	gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );
> }`;function sc(r,e,t){let n=new Zi,i=new Z,s=new Z,o=new ke,a=new Zs({depthPacking:iu}),l

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.