Camera Controls
Builds on the basic example by customising the THREE.js OrbitControls for a more focused navigation experience. It clamps the polar angle, remaps the mouse buttons for rotate/pan, raycasts against the capture on click to drop a marker and smoothly tweens the orbit centre to the point you clicked.
Source Code#
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>Teleport.js Camera Control Sample</title>
<style>
body { margin: 0; overflow: hidden; background: #222; }
canvas { display: block; width: 100vw; height: 100vh; }
</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/",
"teleport": "https://d1ziouw89q7sp5.cloudfront.net/teleport-js/0.0.2/teleport.module.js"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as TELEPORT from "teleport";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000);
const renderer = new THREE.WebGLRenderer({ antialias: false, logarithmicDepthBuffer: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.minPolarAngle = 0.01;
orbitControls.maxPolarAngle = Math.PI / 2;
orbitControls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.PAN,
RIGHT: THREE.MOUSE.PAN,
};
const targetTween = { active: false, from: new THREE.Vector3(), to: new THREE.Vector3(), startTime: 0, duration: 250 };
const easeInOutCubic = (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2);
function focusOrbitCenter(point) {
targetTween.from.copy(orbitControls.target);
targetTween.to.copy(point);
targetTween.startTime = performance.now();
targetTween.active = true;
}
const teleportManager = new TELEPORT.Manager({ renderer: renderer, scene: scene, camera: camera });
const capture = await teleportManager.loadCapture("524ee89f293a4a2e907009191ba7b9f4");
if (capture.startingCamera)
capture.startingCamera.applyTo(camera);
const mouseStart = { x: 0, y: 0 };
const CLICK_MAX_DELTA = 4;
window.addEventListener("mousedown", (e) => {
mouseStart.x = e.clientX;
mouseStart.y = e.clientY;
});
window.addEventListener("mouseup", (e) => {
if (Math.abs(e.clientX - mouseStart.x) > CLICK_MAX_DELTA ||
Math.abs(e.clientY - mouseStart.y) > CLICK_MAX_DELTA) {
return;
}
const hit = teleportManager.raycast(e.clientX, e.clientY, camera);
if (!hit) return;
const marker = new THREE.Mesh(new THREE.SphereGeometry(0.05), new THREE.MeshBasicMaterial({ color: 0xff0000 }));
marker.position.copy(hit.point);
scene.add(marker);
focusOrbitCenter(hit.point);
});
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
renderer.setAnimationLoop(() => {
if (targetTween.active) {
const raw = (performance.now() - targetTween.startTime) / targetTween.duration;
const t = Math.min(raw, 1);
orbitControls.target.lerpVectors(targetTween.from, targetTween.to, easeInOutCubic(t));
if (t >= 1) targetTween.active = false;
}
orbitControls.update();
renderer.render(scene, camera);
});
</script>
</body>
</html>