GLTF Camera animation, control

Hi All,

After dabbling with R3F to load a GLTF animation I have come round full circle and back to Three.js and finally got my GLTF Camera animation to play.

Question 1:
I am now trying to make the animation play once and resume Orbit controls after single loop. Is this possible?

Question 2:
Is it possible to slow animations down or would it be better to edit the Blender file before exporting to GLTF?

Here is my code, any help is much appreciated:

<script type="module">
  import * as THREE from "three";

  import Stats from 'three/addons/libs/stats.js';

  import { OrbitControls } from "three/addons/controls/OrbitControls.js";
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
  import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
  import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

  let camera, scene, renderer;
  let raycaster, mouse, mixer, clock;

  init();
  animate();

  function init() {
    const container = document.createElement("div");
    document.body.appendChild(container);

    // Camera

    camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      0.01,
      150
    );
    camera.position.set(-18, 1.8, 20);

    scene = new THREE.Scene();

    clock = new THREE.Clock();

    // setting up the onclick function
    raycaster = new THREE.Raycaster();
    mouse = new THREE.Vector2();

    new RGBELoader()
      .setPath("textures/equirectangular/")
      .load("forest_cave_1k.hdr", function (texture) {
        texture.mapping = THREE.EquirectangularReflectionMapping;

        scene.background = texture;
        scene.environment = texture;

        // Loads GLTF model

        // Configure and create Draco decoder.
          const dracoLoader = new DRACOLoader();
          dracoLoader.setDecoderPath( 'jsm/libs/draco/' );

        const loader = new GLTFLoader()
        loader.setDRACOLoader(dracoLoader)
        loader.load("models/gltf/MakerVerdeSchool/MakerVerde-Gallery-anitest.gltf", 
        function (gltf) {
          gltf.scene.traverse(function (child) {

            if (child.isMesh) {
              child.castShadow = true;

              child.receiveShadow = true;
            }

            scene.add(gltf.scene);
            camera = gltf.cameras[ 0 ];
            
            scene.add( gltf.scene );

            mixer = new THREE.AnimationMixer( gltf.scene );
    
            gltf.animations.forEach( ( clip ) => {
            
            mixer.clipAction( clip ).play();
      
            });

          });

        });

      });

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = true;
    renderer.toneMappingExposure = 0.5;
    renderer.outputEncoding = THREE.sRGBEncoding;
    container.appendChild(renderer.domElement);

    const controls = new OrbitControls(camera, renderer.domElement);
    // controls.addEventListener("change", render); // use if there is no animation loop
    controls.minDistance = 1;
    controls.maxDistance = 1000;
    controls.target.set(0, 0, 0);
    controls.update();

    window.addEventListener("resize", onWindowResize);
    renderer.domElement.addEventListener("click", onClick, false);
  }

  function onClick() {
    event.preventDefault();

    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    var intersects = raycaster.intersectObjects(scene.children, false);

    // if (intersects.length > 0) {

    // // console.log('intersection:', intersects[0]);
    // window.open(intersects[0].object.userData.name);

    // }

    if (!intersects[0].object.userData.name.includes("Object")) {
      window.open(intersects[0].object.userData.name);
    }
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

  }
  //

  function animate() {

  requestAnimationFrame( animate );
      
  var delta = clock.getDelta();
      
  if ( mixer ) mixer.update( delta );
      
  renderer.render( scene, camera );
      
}

</script>