Guide 2: Control playback position

Overview

In this guide, we'll show a few common controls that allow users to control the playback of a 3D model animation in the display-3d-model component. This includes the ability to:

  • Start playing and pausing a 3D model animation

  • Moving the playback position forwards or backwards

  • Setting the playback position to a specified point

The view we'll build will look as follows:

Developer notes:

Please refer to the developer notes in the first guide. The same notes apply to this guide.

Load and display the 3D model in your view

Please refer to this section in the first guide. We'll use the same foundations from the first guide in the below guide.

Play and pause the 3D model animation

To play and/or pause the 3D model animation, we'll add the following buttons to our view XML:

main.view.xml
<?xml version="1.0" encoding="UTF-8"?>
<view title="3D Model Viewer">
    <var name="animation_file" type="attachment" media="any" />
    <var name="scale" type="number" />
    
    <grid column-count="4">
        <cell column-span="3">
            <display-3d-model
                id="example"
                bind="animation_file"
                scale="scale"
                autoplay="false"
                material-vertex-groups="false"
            />
        </cell>
        <cell>
            <heading>Playback:</heading>
            <button-group>
                <button hide-if="$:isPlaying" label="Play" icon="fa-play" on-press="$:playAnimation()" validate="false" />
                <button show-if="$:isPlaying" label="Pause" icon="fa-pause" on-press="$:pauseAnimation()" validate="false" />
            </button-group>

            <heading>Update 3D model:</heading>
            <capture-file bind="animation_file" label=".fbx.zip file" downloadable="true" />
        </cell>
    </grid>
</view>

Note that we have set the autoplay attribute to false, since we want users to be able to control the playback manually. We have also added an id attribute, which is used by component methods to identify the component in the view XML.

In the view JS, we'll initialize a global variable for isPlaying which is used to dynamically show and hide the play and pause buttons.

main.js
var isPlaying = false;

// This function is called when the app navigates to this view (using a link)
function init() {
    // initialize any data here that should be available when the view is shown
    view.scale = 0.1;
}

Next, we'll need to define the logic for playing and pausing the animation. In the view JS, we'll add two functions and add a playheadPosition variable to track the elapsed playback time:

main.js
var isPlaying = false;
var playheadPosition = 0;

// This function is called when the app navigates to this view (using a link)
function init() {
    // initialize any data here that should be available when the view is shown
    view.scale = 0.1;
}

// This function is called when the user returns to this view from another view
function resume(from) { }


function playAnimation() {
    console.log("Playing animation")
    
    // Set the component's playhead to the specified playhead position
    component.display3dModel({ id: "example" }).setPlayhead(playheadPosition);
    // Start the animation from the defined playhead
    component.display3dModel({ id: "example" }).animationStart();
    // Hide the "Play" button and show the "Pause" button
    isPlaying = true;
}

function pauseAnimation() {
    console.log("Pausing animation")
    
    // Stop playing the animation
    component.display3dModel({ id: "example" }).animationStop();
    // Show the "Play" button
    isPlaying = false;

    // Store the current playhead position
    playheadPosition = component.display3dModel({ id: "example" }).getPlayhead();
}

Note that the above functions, to start and stop the animation playback, make use of multiple component methods of display-3d-model.

Move the playhead forwards, backwards or set it to a specific point

Here we are adding controls to move the animation playhead position forwards and backwards by a set amount, as well as directly specifying the playhead position.

We'll update the relevant part of the view XML code to the following:

main.view.xml
<?xml version="1.0" encoding="UTF-8"?>
<view title="3D Model Viewer">
    ...
    
    <grid column-count="4">
        <cell column-span="3">
            ...
        </cell>
        <cell>
            <heading>Playback:</heading>
            <button label="Set Time" subtext="Current: {$:timeElapsed}" icon="fa-stopwatch" on-press="$:setPlayhead()" validate="false" />
            <button-group>
                <button label="Backwards" icon="fa-fast-backward" on-press="$:skipAnimation(-10000)" validate="false" />
                <button label="Forward" icon="fa-fast-forward" on-press="$:skipAnimation(10000)" validate="false" />
            </button-group>
            <button-group>
                <button hide-if="$:isPlaying" label="Play" icon="fa-play" on-press="$:playAnimation()" validate="false" />
                <button show-if="$:isPlaying" label="Pause" icon="fa-pause" on-press="$:pauseAnimation()" validate="false" />
            </button-group>

            ...
        </cell>
    </grid>
</view>

In the view JS, we'll define the skipAnimation and setPlayhead functions:

main.js
function setPlayhead() {
    // Allow the user to enter a new position in ms
    var position = journey.dialog.input({
        title: 'Set Playhead position',
        message: "Enter the value in ms",
        inputValue: playheadPosition.toString(),
    });


    if (position != null) {
        // Store the new position 
        playheadPosition = Number(position);
        // Show the "Play" button
        isPlaying = false;
    }
}

function skipAnimation(timeValue) {
    // Update the playhead position
    playheadPosition = playheadPosition + timeValue;

    // Automatically start/continue playing from this new position
    playAnimation();
}

Complete view code

Below is the complete View XML and JS code used in this guide:

main.view.xml
<?xml version="1.0" encoding="UTF-8"?>
<view title="3D Model Viewer">
    <var name="animation_file" type="attachment" media="any" />
    <var name="scale" type="number" />
    
    <grid column-count="4">
        <cell column-span="3">
            <display-3d-model
                id="example"
                bind="animation_file"
                scale="scale"
                autoplay="false"
                
                material-vertex-groups="false"
            />
        </cell>
        <cell>
            <heading>Playback:</heading>
            <button label="Set Time" subtext="Current: {$:playheadPosition}" icon="fa-stopwatch" on-press="$:setPlayhead()" validate="false" />
            <button-group>
                <button label="Backwards" icon="fa-fast-backward" on-press="$:skipAnimation(-10000)" validate="false" />
                <button label="Forward" icon="fa-fast-forward" on-press="$:skipAnimation(10000)" validate="false" />
            </button-group>
            <button-group>
                <button hide-if="$:isPlaying" label="Play" icon="fa-play" on-press="$:playAnimation()" validate="false" />
                <button show-if="$:isPlaying" label="Pause" icon="fa-pause" on-press="$:pauseAnimation()" validate="false" />
            </button-group>

            <heading>Update 3D model:</heading>
            <capture-file bind="animation_file" label=".fbx.zip file" downloadable="true" />
        </cell>
    </grid>
</view>
main.js
var isPlaying = false;
var playheadPosition = 0;

// This function is called when the app navigates to this view (using a link)
function init() {
    // initialize any data here that should be available when the view is shown
    view.scale = 0.1;
}

// This function is called when the user returns to this view from another view
function resume(from) { }

function setPlayhead() {
    // Allow the user to enter a new position in ms
    var position = journey.dialog.input({
        title: 'Set Playhead position',
        message: "Enter the value in ms",
        inputValue: playheadPosition.toString(),
    });


    if (position != null) {
        // Store the new position 
        playheadPosition = Number(position);
        // Show the "Play" button
        isPlaying = false;
    }
}

function skipAnimation(timeValue) {
    // Update the playhead position
    playheadPosition = playheadPosition + timeValue;

    // Automatically start/continue playing from this new position
    playAnimation();
}

function playAnimation() {
    console.log("Playing animation")
    
    // Set the component's playhead to the specified playhead position
    component.display3dModel({ id: "example" }).setPlayhead(playheadPosition);
    // Start the animation from the defined playhead
    component.display3dModel({ id: "example" }).animationStart();
    // Hide the "Play" button and show the "Pause" button
    isPlaying = true;
}

function pauseAnimation() {
    console.log("Pausing animation")
    
    // Stop playing the animation
    component.display3dModel({ id: "example" }).animationStop();
    // Show the "Play" button
    isPlaying = false;

    // Store the current playhead position
    playheadPosition = component.display3dModel({ id: "example" }).getPlayhead();
}

Last updated