I'm working on a threejs project where I aim to render a model and utilize mouse interaction to rotate around it.
For inspiration, I found a similar concept on this page:
While I have successfully created an example, the controls are currently limited to click and drag actions.
- The main objectives are to render a model.
- Implement a feature where clicking a button will rotate the camera and display a new model as the previous one fades away (currently using a dropdown to switch models).
- Upon hovering over the model, trigger a call to action or a special mouse animation that can act as a link.
- Enable the ability to pan around the model using mouse movement (without click-drag).
-- To start off, I'm experimenting with this demo:
I tried incorporating mouse interactions like this:
let mouseX = 0, mouseY = 0;
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
However, this caused the model to spin rapidly and zoom in excessively.
Here's the most recent code snippet:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - animation - keyframes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {
background-color: #bfe3dd;
color: #000;
}
a {
color: #2983ff;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">
<!--
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - animation - keyframes<br/>
Model: <a href="https://www.artstation.com/artwork/1AGwX" target="_blank" rel="noopener">Littlest Tokyo</a> by
<a href="https://www.artstation.com/glenatron" target="_blank" rel="noopener">Glen Fox</a>, CC Attribution.
-->
<select id="models">
<option value="models/gltf/LittlestTokyo.glb">LittlestTokyo</option>
<option value="models/gltf/Flamingo.glb" selected="selected">Flamingo</option>
</select>
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
//import Stats from './jsm/libs/stats.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { RoomEnvironment } from './jsm/environments/RoomEnvironment.js';
import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
let mixer;
const clock = new THREE.Clock();
const container = document.getElementById( 'container' );
//const stats = new Stats();
//container.appendChild( stats.dom );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild( renderer.domElement );
const pmremGenerator = new THREE.PMREMGenerator( renderer );
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xbfe3dd );
scene.environment = pmremGenerator.fromScene( new RoomEnvironment(), 0.04 ).texture;
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 5, 2, 8 );
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0.5, 0 );
controls.update();
controls.enablePan = false;
controls.enableDamping = true;
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'js/libs/draco/gltf/' );
const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader );
loadModel('models/gltf/Flamingo.glb')
function loadModel(path){
loader.load(path, function ( gltf ) {
//remove model
// remove last model
// if(model) model.parent.remove(model)
scene.clear();
//add model
add(gltf)
}, undefined, function ( e ) {
console.error(e);
});
}
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
function add(gltf){
let model = gltf.scene;
model.position.set( 1, 1, 0 );
model.scale.set( 0.01, 0.01, 0.01 );
scene.add( model );
mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();
animate();
}
function load(path) {
console.log("loading", path)
loadModel(path)
}
document.getElementById('models').addEventListener('change', function() {
console.log('You selected: ', this.value);
console.log("model", window.model)
load(this.value)
});
function animate() {
requestAnimationFrame( animate );
const delta = clock.getDelta();
mixer.update( delta );
controls.update();
//stats.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>