Search code examples
javascriptanimationcameraaframe

How do I animate a camera movement in a-frame, using JavaScript?


I am working with A-Frame to build a virtual tour, running on a regular webpage, in the desktop. No VR navigation, just moving and clicking the mouse cursor around.

I will have several buttons above the a-scene. Each button will point the camera at a different direction. I managed to get that working. Next step would be animating it as if the camera is panning to its new direction, instead of jumping right to it.

Is there a way to achieve this, having the possibility to also set the duration and easing of the animation?

I know, from this SO post how to do that inside a-frame. But since I am calling a function outside of it, I am a bit lost on how to get this working.

This is my code:

function moveCamera() {
  var el = document.querySelector("a-camera");
  el.components["look-controls"].pitchObject.rotation.x = -0.1;
  el.components["look-controls"].yawObject.rotation.y = 0.85;
}
  .sceneWrapper {
    position: relative;
    padding-top: 20px;
    height: 100vh;
  }
<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
<button id="btn01" type="button" name="button" onclick="moveCamera()">Move Camera</button>

<div class="sceneWrapper">
  <a-scene embedded background="color: #FAFAFA">
    <a-entity id='cameraWrapper' position="0 0 0">
      <a-camera wasd-controls-enabled="false" look-controls="reverseMouseDrag:true">
        <a-animation attribute="position"
              dur="2000"
              easing="linear"
              to="0 1.6 -10"></a-animation>
      </a-camera>
    </a-entity>
    <a-sky color="#c7dcff"></a-sky>
    <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
    <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
    <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
    <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
  </a-scene>
</div>


Solution

  • There are several ways to do this. One would be to manage it directly in the tick function of the look-controls component. It means you would need to create your own component based on look-controls. It needs a few tricks but it works. A more simple way is to use requestAnimationFrame to manage the movement. Here is a fully working example I made based on your code. You can change the duration to what pleases you.

    <html>
    <head>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    </head>
    <body>
    <a-scene background="color: #FAFAFA">
        <a-entity id='cameraWrapper' position="0 0 0">
            <a-camera wasd-controls-enabled="false" look-controls="reverseMouseDrag:true">
                <a-animation attribute="position" dur="2000" easing="linear" to="0 1.6 -10"></a-animation>
            </a-camera>
        </a-entity>
        <a-sky color="#c7dcff"></a-sky>
        <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
        <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
        <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
        <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
    </a-scene>
    
    <button type="button" style="position: fixed; top: 10px; left: 10px;" onclick="moveCamera(-0.1, 0.85)">Move Camera</button>
    
    <script type="text/javascript">
        var start, x_start, y_start, x_rotation, y_rotation;
        var duration = 1000;
        var camera = document.querySelector("a-camera");
    
        function moveCamera(x_destination, y_destination) {
            start = null;
            x_start = camera.components["look-controls"].pitchObject.rotation.x;
            x_rotation = x_destination - x_start;
            y_start = camera.components["look-controls"].yawObject.rotation.y;
            y_rotation = y_destination - y_start;
            requestAnimationFrame(step);
        }
    
        function step(timestamp) {
            if (start === null) start = timestamp;
            var progress = timestamp - start;
            camera.components["look-controls"].pitchObject.rotation.x = x_start + x_rotation * progress / duration;
            camera.components["look-controls"].yawObject.rotation.y = y_start + y_rotation * progress / duration;
            if (progress < duration) {
                requestAnimationFrame(step);
            }
        }
    </script>
    </body>
    </html>