Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation scrubber on Editor #2723

Merged
merged 2 commits into from Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 12 additions & 14 deletions packages/model-viewer/src/three-components/ModelScene.ts
Expand Up @@ -506,27 +506,25 @@ export class ModelScene extends Scene {
try {
const {currentAnimationAction: lastAnimationAction} = this;

this.currentAnimationAction =
this.mixer.clipAction(animationClip, this).play();
this.currentAnimationAction.enabled = true;

if (lastAnimationAction != null &&
this.currentAnimationAction !== lastAnimationAction) {
this.currentAnimationAction.crossFadeFrom(
lastAnimationAction, crossfadeTime, false);
const action = this.mixer.clipAction(animationClip, this);
this.currentAnimationAction = action;

if ((this.element as any).paused) {
this.mixer.stopAllAction();
} else if (
lastAnimationAction != null && action !== lastAnimationAction) {
action.crossFadeFrom(lastAnimationAction, crossfadeTime, false);
}

action.enabled = true;
action.play();
} catch (error) {
console.error(error);
}
}

stopAnimation() {
if (this.currentAnimationAction != null) {
this.currentAnimationAction.stop();
this.currentAnimationAction.reset();
this.currentAnimationAction = null;
}

this.currentAnimationAction = null;
this.mixer.stopAllAction();
}

Expand Down
Expand Up @@ -26,25 +26,35 @@ import {reduxStore} from '../../space_opera_base.js';
import {State} from '../../types.js';
import {dispatchAnimationName, dispatchAutoplayEnabled, getConfig} from '../config/reducer';
import {ConnectedLitElement} from '../connected_lit_element/connected_lit_element.js';
import {getModelViewer} from '../model_viewer_preview/reducer.js';
import {getModel, getModelViewer, getUpdatedModelViewer} from '../model_viewer_preview/reducer.js';
import {CheckboxElement} from '../shared/checkbox/checkbox.js';
import {Dropdown} from '../shared/dropdown/dropdown.js';
import {SectionRow} from '../shared/section_row/section_row.js';
import {SliderWithInputElement} from '../shared/slider_with_input/slider_with_input.js';

/**
* Animation controls for gltf and model-viewer.
*/
@customElement('me-animation-controls')
export class AnimationControls extends ConnectedLitElement {
@query('me-checkbox#animation-autoplay') autoplayCheckbox?: CheckboxElement;
@query('me-checkbox#animation-autoplay') autoplayCheckbox!: CheckboxElement;
@query('me-slider-with-input#scrubber') scrubber!: SliderWithInputElement;
@query('me-section-row#time') timeElement!: SectionRow;
@internalProperty() animationNames: string[] = [];
@internalProperty() selectedAnimation: string|undefined = undefined;
@internalProperty() autoplay: boolean = false;
@internalProperty() autoplay = false;
@internalProperty() clipLength = 0;

stateChanged(state: State) {
this.animationNames = getModelViewer()?.availableAnimations ?? [];
const config = getConfig(state);
this.selectedAnimation = config.animationName;
this.autoplay = !!config.autoplay;

const model = getModel(state);
if (model != null) {
this.animationNames = getModelViewer().availableAnimations ?? [];
this.updateScrubber();
}
}

render() {
Expand All @@ -60,7 +70,8 @@ export class AnimationControls extends ConnectedLitElement {
const hasAnims = this.animationNames.length > 0;
const tabHeader = hasAnims ? 'Animations' : 'Animations (Model has none)';
return html`
<me-expandable-tab tabName=${tabHeader} .enabled=${hasAnims}>
<me-expandable-tab tabName=${tabHeader} .enabled=${hasAnims} .open=${
true}>
<div slot="content">
<me-dropdown id="animation-name-selector"
selectedIndex=${selectedAnimationIndex}
Expand All @@ -72,9 +83,16 @@ export class AnimationControls extends ConnectedLitElement {
</paper-item>`;
})}
</me-dropdown>
<me-checkbox id="animation-autoplay" label="Autoplay"
<me-checkbox id="animation-autoplay" label="Play"
?checked="${!!this.autoplay}"
@change=${this.onAutoplayChange}></me-checkbox>
<me-section-row class="Row" label="Time" id="time">
<me-slider-with-input
id="scrubber"
min=0 max=${this.clipLength} step=0.01
@change=${this.onScrub}>
</me-slider-with-input>
</me-section-row>
</div>
</me-expandable-tab>
`;
Expand All @@ -95,9 +113,20 @@ export class AnimationControls extends ConnectedLitElement {
const value = dropdown.selectedItem?.getAttribute('value') || undefined;
if (value !== undefined && this.animationNames.indexOf(value) !== -1) {
reduxStore.dispatch(dispatchAnimationName(value));
reduxStore.dispatch(dispatchAutoplayEnabled(true));
}
}

onScrub() {
const time = this.scrubber.value;
getModelViewer().currentTime = time;
}

async updateScrubber() {
const modelViewer = await getUpdatedModelViewer();
this.clipLength = Math.floor(modelViewer.duration * 100) / 100;
this.scrubber.value = modelViewer.currentTime;
this.timeElement.style.display = this.autoplay ? 'none' : '';
}
}

declare global {
Expand Down