THREE.js остановите камеру и предоставьте пользователю контроль в конце пути

#javascript #three.js

Вопрос:

На моем сайте у меня есть плавучий остров и камера, которая следует по кривой. Я бы хотел, чтобы эта камера, следующая по кривой, появлялась только при загрузке веб-сайта. Таким образом, это немного похоже на экскурсию с гидом для пользователя, но как только она закончится, я хотел бы, чтобы она остановилась там, где она есть, и вернула контроль пользователю.

Прямо сейчас в моем коде камера просто отлично повторяет кривую, но она зацикливается. Как только камера достигает конца, она возвращается к началу и воспроизводится снова. Ниже приведен мой код:

 import "./style.css"; import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";  let camera,  scene,  renderer,  mixerA,  mixerB,  mixerC,  mixerD,  delta,  raycaster,  mouse,  reqAnim; var clock = new THREE.Clock(); var atStart = true;  init(); animate(); function init() {  //scene  scene = new THREE.Scene();  scene.background = new THREE.Color(0x87ceeb);   //renderer  renderer = new THREE.WebGLRenderer({ antialias: true });  renderer.setSize(window.innerWidth, window.innerHeight);  document.body.appendChild(renderer.domElement);   //camera  camera = new THREE.PerspectiveCamera(  5,  window.innerWidth / window.innerHeight,  1,  10000  );  camera.position.set(80, 55, 80);  camera.lookAt(-0.3, 0.4, -0.25);   //sphere for sun   var geometry1 = new THREE.SphereGeometry(0.22);  var material1 = new THREE.MeshLambertMaterial({  transparent: true,  opacity: 0.5,  });  var sphere1 = new THREE.Mesh(geometry1, material1);  sphere1.position.set(5, 2.5, 10);   //sphere for rect angle light   var geometry2 = new THREE.SphereGeometry(0.25);  var material2 = new THREE.MeshLambertMaterial({  transparent: true,  opacity: 0,  });  var sphere2 = new THREE.Mesh(geometry2, material2);  sphere2.position.set(-0.5, 0.75, 0);   //sphere for arm clickbox   var geometry3 = new THREE.SphereGeometry(0.08);  var material3 = new THREE.MeshLambertMaterial({  transparent: true,  opacity: 0.5,  });  var sphere3 = new THREE.Mesh(geometry3, material3);  sphere3.position.set(-0.25, 0.4, -0.4);  scene.add(sphere3);  sphere3.userData.name = "ARM_SPHERE";   //sphere for laptop clickbox   var geometry4 = new THREE.SphereGeometry(0.08);  var material4 = new THREE.MeshLambertMaterial({  transparent: true,  opacity: 0.5,  });  var sphere4 = new THREE.Mesh(geometry4, material4);  sphere4.position.set(-0.3, 0.4, -0.25);  scene.add(sphere4);  sphere4.userData.name = "LAPTOP_SPHERE";   //loader   let loader = new GLTFLoader();   loader.load("FloatingIsland.gltf", function (gltf) {  const model = gltf.scene;  model.scale.set(3, 3, 3);  mixerA = new THREE.AnimationMixer(model);  console.log(gltf.animations);  mixerA.clipAction(gltf.animations[0]).play();  scene.add(model, sphere1, sphere2);  });   loader.load("circle.glb", function (gltf) {  const model2 = gltf.scene;  mixerB = new THREE.AnimationMixer(model2);  mixerB.clipAction(gltf.animations[0]).play();  model2.position.set(-0.9, 0, 0.15);  model2.lookAt(-10, 0.5);  model2.scale.set(0.5, 0.5, 0.5);   scene.add(model2);  });   loader.load("circle.glb", function (gltf) {  const model3 = gltf.scene;  mixerC = new THREE.AnimationMixer(model3);  mixerC.clipAction(gltf.animations[0]).play();  model3.position.set(-0.9, 0, 0.4);  model3.lookAt(-10, 0.5);  model3.scale.set(0.5, 0.5, 0.5);   scene.add(model3);  });   //laptop screen video  var video01 = document.getElementById("video01");  video01.addEventListener("click", playArmVideo, false);  const videoTexture01 = new THREE.VideoTexture(video01);  const geometry = new THREE.BoxGeometry(0, 1, 1);  const material = new THREE.MeshBasicMaterial({ map: videoTexture01 });  const cube = new THREE.Mesh(geometry, material);  cube.position.set(-0.245, 0.41, -0.25);  cube.scale.set(0.1, 0.09, 0.14);  cube.rotateZ(Math.PI / -23);  video01.play();  scene.add(cube);   // lights  const directionalLight = new THREE.PointLight(0xcb6015, 1);  directionalLight.castShadow = true;  directionalLight.position.set(-500, 300, 50);  scene.add(directionalLight);   const directionalLight2 = new THREE.DirectionalLight(0xcb6015, 1.4);  directionalLight2.castShadow = true;  directionalLight2.position.set(5, 2.5, 10);  scene.add(directionalLight2);  sphere1.add(directionalLight2);   const rectLight = new THREE.RectAreaLight(0xffffff, 1, 3, 3);  rectLight.position.set(-0.5, 0.75, 0);  rectLight.lookAt(0, 0, 0);  scene.add(rectLight);  sphere2.add(rectLight);   //dynamic resize  window.addEventListener("resize", onWindowResize, false);   //orbit controls  const controls = new OrbitControls(camera, renderer.domElement);   controls.enablePan = false;  controls.update();   var rightmousemove;   document.addEventListener("mousemove", function (event) {  if (rightmousemove === true) {  // Use stopImmediatePropagation to stop the other handeller from trigerring  event.stopImmediatePropagation();  }  });   // raycaster  raycaster = new THREE.Raycaster();  mouse = new THREE.Vector2();  window.addEventListener("click", (e) =gt; {  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;  mouse.y = -(e.clientY / window.innerHeight) * 2   1;  raycaster.setFromCamera(mouse, camera);  const found = raycaster.intersectObjects(scene.children);  if (found.length gt; 0 amp;amp; found[0].object.userData.name == "LAPTOP_SPHERE") {  playArmVideo();  }  });   renderPath();   render(); }  function renderPath() {   var curve = new THREE.CubicBezierCurve3(  new THREE.Vector3(200, 200, 800),  new THREE.Vector3(100, 150, -200),  new THREE.Vector3(-50, 10, -150),  new THREE.Vector3(-50, 10, 0),  new THREE.Vector3(-20, 0, 10)  );   // Create the final object to add to the scene  var clock2 = new THREE.Clock();  clock2.start();  var speed = 0.1;   var pathTarget = new THREE.Vector3(0, 10, 0);  curve.getPoint((clock.getElapsedTime() * speed) % 1.0, pathTarget);  camera.position.copy(pathTarget);  camera.lookAt(0, 0, 0);   requestAnimationFrame(renderPath); }  function onWindowResize() {  var width = window.innerWidth;  var height = window.innerHeight;  renderer.setSize(width, height);  camera.aspect = width / height;  camera.updateProjectionMatrix(); }  function animate() {  reqAnim = requestAnimationFrame(animate);  delta = clock.getDelta();  if (mixerA) mixerA.update(delta);  if (mixerB) mixerB.update(delta);  if (mixerC) mixerC.update(delta);  if (mixerD) mixerC.update(delta);  // required if controls.enableDamping or controls.autoRotate are set to true  render(); }  function playArmVideo() {  var video02 = document.getElementById("video02");  const videoTexture02 = new THREE.VideoTexture(video02);  const geometry5 = new THREE.BoxGeometry(0, 1, 1);  const material5 = new THREE.MeshBasicMaterial({ map: videoTexture02 });  const cube2 = new THREE.Mesh(geometry5, material5);  cube2.position.set(1, 1, 1);  cube2.scale.set(0.4, 0.4, 0.4);  video02.play();  scene.add(cube2);  setTimeout(() =gt; scene.remove(cube2), 7100); }  function render() {  renderer.render(scene, camera); }