1) Orbit controls
If you want your experience to revolve around a model, moving the camera around it, and zooming in and out - I'd say the orbit controls are exacly what You're looking for:
<a-entity camera look-controls
orbit-controls="target: 0 1.6 -0.5; initialPosition: 0 5 15"></a-entity>
The camera will move around the defined target
. Works with mobile touch events.
Check it out with gltf
models here.
2) Moving the camera with pinching
If you want to move the camera towars or away the direction it's looking at, as far as i know, You'll have to implement such behavior in a custom component. A poor mans version could look a bit like this:
AFRAME.registerComponent('pinch-controls', {
init: function() {
// moveCamera uses variables from 'this' scope
this.moveCamera.bind(this);
// we'll use this to get the 'pinch direction'
this.distance = 0;
// we'll keep here the camera's current direction
this.direction = new THREE.Vector3();
// camera entity reference
this.camera = document.querySelector("[camera]");
// listeners
document.body.addEventListener('touchstart', event => {
// we're interested only in two - finger pinches
if (event.touches.length != 2) return 0;
// calculate the distance
this.distance = this.calculatePinchDistance(event);
// we don't want the touch to rotate the camera around
this.el.setAttribute('look-controls', 'touchEnabled', false);
}, false);
document.body.addEventListener('touchend', event => {
// when the pinch ends - restore the look-controls
if (event.touches.length != 1) this.el.setAttribute('look-controls', 'touchEnabled', true);
}, false);
document.body.addEventListener('touchmove', event => {
// we're interested only in two - finger pinches
if (event.touches.length != 2) return 0;
// compare the distances to determine which direction should we move
var distance = this.calculatePinchDistance(event);
let speed = (distance < this.distance) ? -0.2 : 0.2;
this.moveCamera(speed);
// keep the distance for the next callback
this.distance = distance;
}, false);
},
calculatePinchDistance(event) {
var dx = event.touches[0].pageX - event.touches[1].pageX;
var dy = event.touches[0].pageY - event.touches[1].pageY;
return Math.sqrt(dx * dx + dy * dy);
},
moveCamera: function(speed) {
// get the camera direction, and multiply it by the desired 'speed'
this.el.sceneEl.camera.getWorldDirection(this.direction);
this.direction.multiplyScalar(speed);
// apply the change to the actual position
var pos = this.el.getAttribute("position");
pos.add(this.direction);
this.el.setAttribute("position", pos);
}
}
// HTML
// <a-entity camera look-controls pinch-controls></a-entity>
Check it out here
If you want to move the camera towards the model (instead of the direction it's facing), you can just replace extracting the camera direction (in moveCamera
) with calculating the camera <-> model direction:
// instead of
// this.el.sceneEl.camera.getWorldDirection(this.direction);
// grab the direction towards the model
this.direction.copy(this.el.object3D.position)
this.direction.add(modelReference.object3D.position)
this.direction.normalize();
// (...)
Tiago edit:
The final code used was
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Octant Cube - by Dodds, H. & Peres, T.</title>
<meta name="description" content="Present in the Taxonomy article">
<script src="https://kit.fontawesome.com/c9500776a0.js" crossorigin="anonymous"></script>
<script src="misc/codeBtn.js"></script>
<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-orbit-controls@1.2.0/dist/aframe-orbit-controls.min.js"></script>
<script src="https://unpkg.com/aframe-supercraft-loader@1.1.3/dist/aframe-supercraft-loader.js"></script>
<style>
#toggleBtn {
position: fixed;
z-index: 9999;
margin: 25px;
font-size: 3em;
color: rgb(128, 0, 128);
cursor: pointer;
}
#toggleBtn:hover {
color: rgba(128, 0, 128, 0.6);
}
</style>
<script>
// Models swap component
AFRAME.registerComponent('content-manager', {
init: function() {
const btn = document.querySelector("#toggleBtn")
const castle = document.querySelector("#castle")
const fish = document.querySelector("#fish")
btn.addEventListener("click", e => {
if (castle.getAttribute("visible")) {
castle.emit("fadeOut")
btn.classList.remove("fa-fish")
btn.classList.add("fa-landmark")
} else {
fish.emit("fadeOut")
btn.classList.remove("fa-landmark")
btn.classList.add("fa-fish")
}
})
fish.addEventListener('animationcomplete__fadeout', e => {
fish.setAttribute("visible", "false")
castle.setAttribute("visible", "true")
castle.emit("fadeIn")
})
castle.addEventListener('animationcomplete__fadeout', e => {
castle.setAttribute("visible", "false")
fish.setAttribute("visible", "true")
fish.emit("fadeIn")
})
}
})
// move the camera on pinching
AFRAME.registerComponent('pinch-controls', {
init: function() {
// bind the methods that use the scope variables
this.moveCamera.bind(this);
// use this to keep track whether the user is moving forward or backwards
this.distance = 0;
// store the camera direction here
this.direction = new THREE.Vector3();
// camera entity reference
this.camera = document.querySelector("[camera]");
document.body.addEventListener('touchstart', event => {
// react only on two finger pinches
if (event.touches.length != 2) return 0;
this.distance = this.calculatePinchDistance(event);
// prevent the look controls to rotate the camera while pinching
this.el.setAttribute('look-controls', 'touchEnabled', false);
}, false);
document.body.addEventListener('touchend', event => {
// restore the look-controls
if (event.touches.length != 1) this.el.setAttribute('look-controls', 'touchEnabled', true)
}, false);
document.body.addEventListener('wheel', e => {
this.moveCamera(e.deltaY < 0 ? -0.2 : 0.2);
})
document.body.addEventListener('touchmove', event => {
// we're interested only in pinching
if (event.touches.length != 2) return 0;
// calculate the pinch difference and move the camera
var distance = this.calculatePinchDistance(event);
let multiplier = (distance < this.distance) ? -0.2 : 0.2;;
if (!isNaN(multiplier)) this.moveCamera(multiplier);
// for later use
this.distance = distance;
}, false);
},
calculatePinchDistance: function(mouseEvent) {
var dx = event.touches[0].pageX - event.touches[1].pageX;
var dy = event.touches[0].pageY - event.touches[1].pageY;
return Math.sqrt(dx * dx + dy * dy);
},
moveCamera: function(speed) {
this.el.sceneEl.camera.getWorldDirection(this.direction);
this.direction.multiplyScalar(speed);
var pos = this.el.getAttribute("position");
pos.add(this.direction);
this.el.setAttribute("position", pos)
}
})
</script>
</head>
<body>
<a-scene background="color: #FAFAFA">
<a-assets>
<a-asset-item id="octant" src="octant.glb"></a-asset-item>
</a-assets>
<a-entity position="0 0.347 -4" rotation="0 60 -1" gltf-model="#octant" scale="5 5 5" animation__fadeIn="property: scale; dur: 150; from: 0.001 0.001 0.001; to: 0.5 0.5 0.5; easing: easeInQuad; startEvents: fadeIn" animation__fadeOut="property: scale; dur: 150; from: 0.5 0.5 0.5; to: 0.001 0.001 0.001; easing: easeInQuad; startEvents: fadeOut"></a-entity>
<a-entity camera look-controls
orbit-controls="target: 0 1.6 -0.5; initialPosition: 0 5 15"></a-entity>
</a-scene>
</body>
</html>
And this was the final result
