import * as React from 'react'; import { IAnimationKey } from 'babylonjs/Animations/animationKey'; import { Controls } from './controls'; interface ITimelineProps { keyframes: IAnimationKey[] | null; selected: IAnimationKey | null; currentFrame: number; onCurrentFrameChange: (frame: number) => void; dragKeyframe: (frame: number, index: number) => void; playPause: (direction: number) => void; isPlaying: boolean; } export class Timeline extends React.Component< ITimelineProps, { selected: IAnimationKey; activeKeyframe: number | null } > { readonly _frames: object[] = Array(300).fill({}); private _scrollable: React.RefObject; private _scrollbarHandle: React.RefObject; private _direction: number; private _scrolling: boolean; private _shiftX: number; constructor(props: ITimelineProps) { super(props); if (this.props.selected !== null) { this.state = { selected: this.props.selected, activeKeyframe: null }; } this._scrollable = React.createRef(); this._scrollbarHandle = React.createRef(); this._direction = 0; this._scrolling = false; this._shiftX = 0; } playBackwards(event: React.MouseEvent) { this.props.playPause(-1); } play(event: React.MouseEvent) { this.props.playPause(1); } pause(event: React.MouseEvent) { if (this.props.isPlaying) { this.props.playPause(1); } } handleInputChange(event: React.ChangeEvent) { this.props.onCurrentFrameChange(parseInt(event.target.value)); event.preventDefault(); } nextFrame(event: React.MouseEvent) { event.preventDefault(); this.props.onCurrentFrameChange(this.props.currentFrame + 1); (this._scrollable.current as HTMLDivElement).scrollLeft = this.props.currentFrame * 5; } previousFrame(event: React.MouseEvent) { event.preventDefault(); if (this.props.currentFrame !== 0) { this.props.onCurrentFrameChange(this.props.currentFrame - 1); (this._scrollable.current as HTMLDivElement).scrollLeft = -( this.props.currentFrame * 5 ); } } nextKeyframe(event: React.MouseEvent) { event.preventDefault(); if (this.props.keyframes !== null) { let first = this.props.keyframes.find( (kf) => kf.frame > this.props.currentFrame ); if (first) { this.props.onCurrentFrameChange(first.frame); this.setState({ selected: first }); (this._scrollable.current as HTMLDivElement).scrollLeft = first.frame * 5; } } } previousKeyframe(event: React.MouseEvent) { event.preventDefault(); if (this.props.keyframes !== null) { let keyframes = [...this.props.keyframes]; let first = keyframes .reverse() .find((kf) => kf.frame < this.props.currentFrame); if (first) { this.props.onCurrentFrameChange(first.frame); this.setState({ selected: first }); (this._scrollable.current as HTMLDivElement).scrollLeft = -( first.frame * 5 ); } } } dragStart(e: React.TouchEvent): void; dragStart(e: React.MouseEvent): void; dragStart(e: any): void { e.preventDefault(); this.setState({ activeKeyframe: parseInt(e.target.id.replace('kf_', '')) }); this._direction = e.clientX; } drag(e: React.TouchEvent): void; drag(e: React.MouseEvent): void; drag(e: any): void { e.preventDefault(); if (this.props.keyframes) { if ( this.state.activeKeyframe === parseInt(e.target.id.replace('kf_', '')) ) { let updatedKeyframe = this.props.keyframes[this.state.activeKeyframe]; if (this._direction > e.clientX) { console.log(`Dragging left ${this.state.activeKeyframe}`); let used = this.isFrameBeingUsed(updatedKeyframe.frame - 1, -1); if (used) { updatedKeyframe.frame = used; } } else { console.log(`Dragging Right ${this.state.activeKeyframe}`); let used = this.isFrameBeingUsed(updatedKeyframe.frame + 1, 1); if (used) { updatedKeyframe.frame = used; } } this.props.dragKeyframe( updatedKeyframe.frame, this.state.activeKeyframe ); } } } isFrameBeingUsed(frame: number, direction: number) { let used = this.props.keyframes?.find((kf) => kf.frame === frame); if (used) { this.isFrameBeingUsed(used.frame + direction, direction); return false; } else { return frame; } } dragEnd(e: React.TouchEvent): void; dragEnd(e: React.MouseEvent): void; dragEnd(e: any): void { e.preventDefault(); this._direction = 0; this.setState({ activeKeyframe: null }); } scrollDragStart(e: React.TouchEvent): void; scrollDragStart(e: React.MouseEvent): void; scrollDragStart(e: any) { e.preventDefault(); if ((e.target.class = 'scrollbar') && this._scrollbarHandle.current) { this._scrolling = true; this._shiftX = e.clientX - this._scrollbarHandle.current.getBoundingClientRect().left; this._scrollbarHandle.current.style.left = e.pageX - this._shiftX + 'px'; } } scrollDrag(e: React.TouchEvent): void; scrollDrag(e: React.MouseEvent): void; scrollDrag(e: any) { e.preventDefault(); if (this._scrolling && this._scrollbarHandle.current) { let moved = e.pageX - this._shiftX; if (moved > 233 && moved < 630) { this._scrollbarHandle.current.style.left = moved + 'px'; (this._scrollable.current as HTMLDivElement).scrollLeft = moved + 10; } } } scrollDragEnd(e: React.TouchEvent): void; scrollDragEnd(e: React.MouseEvent): void; scrollDragEnd(e: any) { e.preventDefault(); this._scrolling = false; this._shiftX = 0; } render() { return ( <>
this.drag(e)} onTouchMove={(e) => this.drag(e)} onTouchStart={(e) => this.dragStart(e)} onTouchEnd={(e) => this.dragEnd(e)} onMouseDown={(e) => this.dragStart(e)} onMouseUp={(e) => this.dragEnd(e)} onMouseLeave={(e) => this.dragEnd(e)} onDragStart={() => false} > {this._frames.map((frame, i) => { return ( {i % 5 === 0 ? ( <> {i} ) : null} ); })} {this.props.keyframes && this.props.keyframes.map((kf, i) => { return ( ); })}
20
this.scrollDrag(e)} onTouchMove={(e) => this.scrollDrag(e)} onTouchStart={(e) => this.scrollDragStart(e)} onTouchEnd={(e) => this.scrollDragEnd(e)} onMouseDown={(e) => this.scrollDragStart(e)} onMouseUp={(e) => this.scrollDragEnd(e)} onMouseLeave={(e) => this.scrollDragEnd(e)} onDragStart={() => false} >
100
this.handleInputChange(e)} >
); } }