diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/animationtimeline/Region.tsx | 53 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Timeline.tsx | 62 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Track.tsx | 68 |
3 files changed, 98 insertions, 85 deletions
diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx index b09456cd7..15cbbc16f 100644 --- a/src/client/views/animationtimeline/Region.tsx +++ b/src/client/views/animationtimeline/Region.tsx @@ -10,6 +10,7 @@ import '../global/globalCssVariables.module.scss'; import './Region.scss'; import './Timeline.scss'; import { TimelineMenu } from './TimelineMenu'; +import { ObservableReactComponent } from '../ObservableReactComponent'; /** * Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also @@ -156,31 +157,31 @@ interface IProps { * @author Andrew Kim */ @observer -export class Region extends React.Component<IProps> { +export class Region extends ObservableReactComponent<IProps> { @observable private _bar = React.createRef<HTMLDivElement>(); @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; @computed private get regiondata() { - return RegionData(this.props.RegionData); + return RegionData(this._props.RegionData); } @computed private get regions() { - return DocListCast(this.props.animatedDoc.regions); + return DocListCast(this._props.animatedDoc.regions); } @computed private get keyframes() { return DocListCast(this.regiondata.keyframes); } @computed private get pixelPosition() { - return RegionHelpers.convertPixelTime(this.regiondata.position, 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); + return RegionHelpers.convertPixelTime(this.regiondata.position, 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); } @computed private get pixelDuration() { - return RegionHelpers.convertPixelTime(this.regiondata.duration, 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); + return RegionHelpers.convertPixelTime(this.regiondata.duration, 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); } @computed private get pixelFadeIn() { - return RegionHelpers.convertPixelTime(this.regiondata.fadeIn, 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); + return RegionHelpers.convertPixelTime(this.regiondata.fadeIn, 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); } @computed private get pixelFadeOut() { - return RegionHelpers.convertPixelTime(this.regiondata.fadeOut, 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); + return RegionHelpers.convertPixelTime(this.regiondata.fadeOut, 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); } constructor(props: any) { @@ -191,10 +192,10 @@ export class Region extends React.Component<IProps> { setTimeout(() => { //giving it a temporary 1sec delay... if (!this.regiondata.keyframes) this.regiondata.keyframes = new List<Doc>(); - const start = this.props.makeKeyData(this.regiondata, this.regiondata.position, RegionHelpers.KeyframeType.end); - const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, RegionHelpers.KeyframeType.fade); - const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, RegionHelpers.KeyframeType.fade); - const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, RegionHelpers.KeyframeType.end); + const start = this._props.makeKeyData(this.regiondata, this.regiondata.position, RegionHelpers.KeyframeType.end); + const fadeIn = this._props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, RegionHelpers.KeyframeType.fade); + const fadeOut = this._props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, RegionHelpers.KeyframeType.fade); + const finish = this._props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, RegionHelpers.KeyframeType.end); fadeIn.opacity = 1; fadeOut.opacity = 1; start.opacity = 0.1; @@ -213,7 +214,7 @@ export class Region extends React.Component<IProps> { this._doubleClickEnabled = false; } else { setTimeout(() => { - if (!this._mouseToggled && this._doubleClickEnabled) this.props.changeCurrentBarX(this.pixelPosition + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); + if (!this._mouseToggled && this._doubleClickEnabled) this._props.changeCurrentBarX(this.pixelPosition + (clientX - this._bar.current!.getBoundingClientRect().left) * this._props.transform.Scale); this._mouseToggled = false; this._doubleClickEnabled = false; }, 200); @@ -235,7 +236,7 @@ export class Region extends React.Component<IProps> { const left = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.left, this.regiondata, this.regions)!; const right = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.right, this.regiondata, this.regions)!; const prevX = this.regiondata.position; - const futureX = this.regiondata.position + RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const futureX = this.regiondata.position + RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement); if (futureX <= 0) { this.regiondata.position = 0; } else if (left && left.position + left.duration >= futureX) { @@ -274,7 +275,7 @@ export class Region extends React.Component<IProps> { e.preventDefault(); e.stopPropagation(); const bar = this._bar.current!; - const offset = RegionHelpers.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const offset = RegionHelpers.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this._props.transform.Scale), 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement); const leftRegion = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.left, this.regiondata, this.regions); if (leftRegion && this.regiondata.position + offset <= leftRegion.position + leftRegion.duration) { this.regiondata.position = leftRegion.position + leftRegion.duration; @@ -298,7 +299,7 @@ export class Region extends React.Component<IProps> { e.preventDefault(); e.stopPropagation(); const bar = this._bar.current!; - const offset = RegionHelpers.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const offset = RegionHelpers.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this._props.transform.Scale), 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement); const rightRegion = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.right, this.regiondata, this.regions); const fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= fadeOutKeyframeTime) { @@ -317,13 +318,13 @@ export class Region extends React.Component<IProps> { createKeyframe = (clientX: number) => { this._mouseToggled = true; const bar = this._bar.current!; - const offset = RegionHelpers.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const offset = RegionHelpers.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this._props.transform.Scale), 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends const position = this.regiondata.position; - this.props.makeKeyData(this.regiondata, Math.round(position + offset), RegionHelpers.KeyframeType.default); + this._props.makeKeyData(this.regiondata, Math.round(position + offset), RegionHelpers.KeyframeType.default); this.regiondata.hasData = true; - this.props.changeCurrentBarX(RegionHelpers.convertPixelTime(Math.round(position + offset), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied + this._props.changeCurrentBarX(RegionHelpers.convertPixelTime(Math.round(position + offset), 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied } }; @@ -331,7 +332,7 @@ export class Region extends React.Component<IProps> { moveKeyframe = (e: React.MouseEvent, kf: Doc) => { e.preventDefault(); e.stopPropagation(); - this.props.changeCurrentBarX(RegionHelpers.convertPixelTime(NumCast(kf.time!), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement)); + this._props.changeCurrentBarX(RegionHelpers.convertPixelTime(NumCast(kf.time!), 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement)); }; /** @@ -374,7 +375,7 @@ export class Region extends React.Component<IProps> { */ @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { - TimelineMenu.Instance.addItem('button', 'Remove Region', () => Cast(this.props.animatedDoc.regions, listSpec(Doc))?.splice(this.regions.indexOf(this.props.RegionData), 1)), + TimelineMenu.Instance.addItem('button', 'Remove Region', () => Cast(this._props.animatedDoc.regions, listSpec(Doc))?.splice(this.regions.indexOf(this._props.RegionData), 1)), TimelineMenu.Instance.addItem('input', `fadeIn: ${this.regiondata.fadeIn}ms`, val => { runInAction(() => { let cannotMove: boolean = false; @@ -460,7 +461,7 @@ export class Region extends React.Component<IProps> { e.stopPropagation(); const div = ref.current!; div.style.opacity = '1'; - Doc.BrushDoc(this.props.animatedDoc); + Doc.BrushDoc(this._props.animatedDoc); }; /** @@ -472,7 +473,7 @@ export class Region extends React.Component<IProps> { e.stopPropagation(); const div = ref.current!; div.style.opacity = '0'; - Doc.UnBrushDoc(this.props.animatedDoc); + Doc.UnBrushDoc(this._props.animatedDoc); }; ///////////////////////UI STUFF ///////////////////////// @@ -486,12 +487,12 @@ export class Region extends React.Component<IProps> { return DocListCast(this.regiondata.keyframes).map(kf => { return ( <> - <div className="keyframe" style={{ left: `${RegionHelpers.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement) - this.pixelPosition}px` }}> + <div className="keyframe" style={{ left: `${RegionHelpers.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement) - this.pixelPosition}px` }}> <div className="divider"></div> <div className="keyframeCircle keyframe-indicator" style={{ - borderColor: this.props.saveStateKf === kf ? 'red' : undefined, + borderColor: this._props.saveStateKf === kf ? 'red' : undefined, }} onPointerDown={e => { e.preventDefault(); @@ -526,8 +527,8 @@ export class Region extends React.Component<IProps> { if (index !== this.keyframes.length - 1) { const right = this.keyframes[index + 1]; const bodyRef = React.createRef<HTMLDivElement>(); - const kfPos = RegionHelpers.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); - const rightPos = RegionHelpers.convertPixelTime(NumCast(right.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement); + const kfPos = RegionHelpers.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); + const rightPos = RegionHelpers.convertPixelTime(NumCast(right.time), 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement); keyframeDividers.push( <div ref={bodyRef} diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 4be3b05ab..f27a2d2fd 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,7 +1,7 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core'; import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; +import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; @@ -14,6 +14,7 @@ import { RegionHelpers } from './Region'; import './Timeline.scss'; import { TimelineOverview } from './TimelineOverview'; import { Track } from './Track'; +import { ObservableReactComponent } from '../ObservableReactComponent'; /** * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are @@ -43,7 +44,7 @@ import { Track } from './Track'; */ @observer -export class Timeline extends React.Component<FieldViewProps> { +export class Timeline extends ObservableReactComponent<FieldViewProps> { //readonly constants private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MAX_TITLE_HEIGHT = 75; @@ -54,6 +55,11 @@ export class Timeline extends React.Component<FieldViewProps> { private DEFAULT_CONTAINER_HEIGHT: number = 330; private MIN_CONTAINER_HEIGHT: number = 205; + constructor(props: any) { + super(props); + makeObservable(this); + } + //react refs @observable private _trackbox = React.createRef<HTMLDivElement>(); @observable private _titleContainer = React.createRef<HTMLDivElement>(); @@ -82,11 +88,11 @@ export class Timeline extends React.Component<FieldViewProps> { */ @computed private get children(): Doc[] { - const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this.props.Document.type) as any); + const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as any); if (annotatedDoc) { - return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '_annotations']); + return DocListCast(this._props.Document[Doc.LayoutFieldKey(this._props.Document) + '_annotations']); } - return DocListCast(this.props.Document[this.props.fieldKey]); + return DocListCast(this._props.Document[this._props.fieldKey]); } /////////lifecycle functions//////////// @@ -96,21 +102,21 @@ export class Timeline extends React.Component<FieldViewProps> { this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset - if (!this.props.Document.AnimationLength) { + if (!this._props.Document.AnimationLength) { //if animation length did not exist - this.props.Document.AnimationLength = this._time; //set it to default time + this._props.Document.AnimationLength = this._time; //set it to default time } else { - this._time = NumCast(this.props.Document.AnimationLength); //else, set time to animationlength stored from before + this._time = NumCast(this._props.Document.AnimationLength); //else, set time to animationlength stored from before } this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); //the entire length of the timeline div (actual div part itself) this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts - this.props.Document.isATOn = !this.props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on + this._props.Document.isATOn = !this._props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); } componentWillUnmount() { - this.props.Document.AnimationLength = this._time; //save animation length + this._props.Document.AnimationLength = this._time; //save animation length } ///////////////////////////////////////////////// @@ -206,7 +212,7 @@ export class Timeline extends React.Component<FieldViewProps> { onScrubberMove = (e: PointerEvent) => { const scrubberbox = this._infoContainer.current!; const left = scrubberbox.getBoundingClientRect().left; - const offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; + const offsetX = Math.round(e.clientX - left) * this._props.ScreenToLocalTransform().Scale; this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position return false; }; @@ -233,7 +239,7 @@ export class Timeline extends React.Component<FieldViewProps> { this._visibleStart -= e.movementX; this._totalLength -= e.movementX; this._time -= RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement); - this.props.Document.AnimationLength = this._time; + this._props.Document.AnimationLength = this._time; } return false; }; @@ -349,8 +355,8 @@ export class Timeline extends React.Component<FieldViewProps> { private timelineToolBox = (scale: number, totalTime: number) => { const size = 40 * scale; //50 is default const iconSize = 25; - const width: number = this.props.PanelWidth(); - const modeType = this.props.Document.isATOn ? 'Author' : 'Play'; + const width: number = this._props.PanelWidth(); + const modeType = this._props.Document.isATOn ? 'Author' : 'Play'; //decides if information should be omitted because the timeline is very small // if its less than 950 pixels then it's going to be overlapping @@ -389,7 +395,7 @@ export class Timeline extends React.Component<FieldViewProps> { tickIncrement={this._tickIncrement} time={this._time} parent={this} - isAuthoring={BoolCast(this.props.Document.isATOn)} + isAuthoring={BoolCast(this._props.Document.isATOn)} currentBarX={this._currentBarX} totalLength={this._totalLength} visibleLength={this._visibleLength} @@ -410,10 +416,10 @@ export class Timeline extends React.Component<FieldViewProps> { </div> <div className="time-box overview-tool" style={{ display: 'flex' }}> {this.timeIndicator(lengthString, totalTime)} - <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}> + <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this._props.Document)}> <FontAwesomeIcon icon="compress-arrows-alt" size="lg" /> </div> - <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this.props.Document)}> + <div className="resetView-tool" style={{ display: this._props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this._props.Document)}> <FontAwesomeIcon icon="expand-arrows-alt" size="lg" /> </div> </div> @@ -423,17 +429,17 @@ export class Timeline extends React.Component<FieldViewProps> { }; timeIndicator(lengthString: string, totalTime: number) { - if (this.props.Document.isATOn) { - return <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', display: this.props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>; + if (this._props.Document.isATOn) { + return <div key="time-text" className="animation-text" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', display: this._props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>; } else { const ctime = `Current: ${this.getCurrentTime()}`; const ttime = `Total: ${this.toReadTime(this._time)}`; return ( <div style={{ flexDirection: 'column' }}> - <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}> + <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}> {ctime} </div> - <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}> + <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}> {ttime} </div> </div> @@ -459,8 +465,8 @@ export class Timeline extends React.Component<FieldViewProps> { const roundToggleContainer = this._roundToggleContainerRef.current!; const timelineContainer = this._timelineContainer.current!; - this.props.Document.isATOn = !this.props.Document.isATOn; - if (!BoolCast(this.props.Document.isATOn)) { + this._props.Document.isATOn = !this._props.Document.isATOn; + if (!BoolCast(this._props.Document.isATOn)) { //turning on playmode... roundToggle.style.transform = 'translate(0px, 0px)'; roundToggle.style.animationName = 'turnoff'; @@ -535,7 +541,7 @@ export class Timeline extends React.Component<FieldViewProps> { // change visible and total width return ( <div style={{ visibility: 'visible' }}> - <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}> + <div key="timeline_wrapper" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}> <div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}> <div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}> {this.drawTicks()} @@ -543,18 +549,18 @@ export class Timeline extends React.Component<FieldViewProps> { <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown}></div> </div> <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}> - {[...this.children, this.props.Document].map(doc => ( + {[...this.children, this._props.Document].map(doc => ( <Track ref={ref => this.mapOfTracks.push(ref)} timeline={this} animatedDoc={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} - transform={this.props.ScreenToLocalTransform()} + transform={this._props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} - collection={this.props.Document} + collection={this._props.Document} timelineVisible={true} /> ))} @@ -562,7 +568,7 @@ export class Timeline extends React.Component<FieldViewProps> { </div> <div className="currentTime">Current: {this.getCurrentTime()}</div> <div key="timeline_title" className="title-container" ref={this._titleContainer}> - {[...this.children, this.props.Document].map(doc => ( + {[...this.children, this._props.Document].map(doc => ( <div style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}> <p>{StrCast(doc.title)}</p> </div> diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index d959241d0..00aa51cac 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,4 +1,4 @@ -import { action, computed, intercept, observable, reaction, runInAction } from 'mobx'; +import { action, computed, intercept, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc'; @@ -11,6 +11,7 @@ import { Transform } from '../../util/Transform'; import { Region, RegionData, RegionHelpers } from './Region'; import { Timeline } from './Timeline'; import './Track.scss'; +import { ObservableReactComponent } from '../ObservableReactComponent'; interface IProps { timeline: Timeline; @@ -26,7 +27,7 @@ interface IProps { } @observer -export class Track extends React.Component<IProps> { +export class Track extends ObservableReactComponent<IProps> { @observable private _inner = React.createRef<HTMLDivElement>(); @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; @@ -37,24 +38,29 @@ export class Track extends React.Component<IProps> { private primitiveWhitelist = ['x', 'y', '_freeform_panX', '_freeform_panY', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop']; private objectWhitelist = ['data']; + constructor(props: any) { + super(props); + makeObservable(this); + } + @computed private get regions() { - return DocListCast(this.props.animatedDoc.regions); + return DocListCast(this._props.animatedDoc.regions); } @computed private get time() { - return NumCast(RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); + return NumCast(RegionHelpers.convertPixelTime(this._props.currentBarX, 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement)); } componentDidMount() { - DocListCastAsync(this.props.animatedDoc.regions).then(regions => { - if (!regions) this.props.animatedDoc.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff + DocListCastAsync(this._props.animatedDoc.regions).then(regions => { + if (!regions) this._props.animatedDoc.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff //these two lines are exactly same from timeline.tsx const relativeHeight = window.innerHeight / 20; runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); - if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time); - this.props.animatedDoc.hidden = false; - this.props.animatedDoc.opacity = 1; + if (DocListCast(this._props.animatedDoc.regions).length === 0) this.createRegion(this.time); + this._props.animatedDoc.hidden = false; + this._props.animatedDoc.opacity = 1; // this.autoCreateKeyframe(); }); } @@ -88,7 +94,7 @@ export class Track extends React.Component<IProps> { */ @action saveKeyframe = () => { - if (this.props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) { + if (this._props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) { this.saveStateKf = undefined; this.saveStateRegion = undefined; return; @@ -130,13 +136,13 @@ export class Track extends React.Component<IProps> { */ @action autoCreateKeyframe = () => { - const objects = this.objectWhitelist.map(key => this.props.animatedDoc[key]); - intercept(this.props.animatedDoc, change => { + const objects = this.objectWhitelist.map(key => this._props.animatedDoc[key]); + intercept(this._props.animatedDoc, change => { return change; }); return reaction( () => { - return [...this.primitiveWhitelist.map(key => this.props.animatedDoc[key]), ...objects]; + return [...this.primitiveWhitelist.map(key => this._props.animatedDoc[key]), ...objects]; }, (changed, reaction) => { //check for region @@ -170,18 +176,18 @@ export class Track extends React.Component<IProps> { @action currentBarXReaction = () => { return reaction( - () => this.props.currentBarX, + () => this._props.currentBarX, () => { const regiondata = this.findRegion(this.time); if (regiondata) { - this.props.animatedDoc.hidden = false; + this._props.animatedDoc.hidden = false; // if (!this._autoKfReaction) { // // this._autoKfReaction = this.autoCreateKeyframe(); // } this.timeChange(); } else { - this.props.animatedDoc.hidden = true; - this.props.animatedDoc !== this.props.collection && (this.props.animatedDoc.opacity = 0); + this._props.animatedDoc.hidden = true; + this._props.animatedDoc !== this._props.collection && (this._props.animatedDoc.opacity = 0); //if (this._autoKfReaction) this._autoKfReaction(); } } @@ -195,7 +201,7 @@ export class Track extends React.Component<IProps> { timelineVisibleReaction = () => { return reaction( () => { - return this.props.timelineVisible; + return this._props.timelineVisible; }, isVisible => { if (isVisible) { @@ -252,14 +258,14 @@ export class Track extends React.Component<IProps> { @action private applyKeys = (kf: Doc) => { this.primitiveWhitelist.forEach(key => { - if (key === 'opacity' && this.props.animatedDoc === this.props.collection) { + if (key === 'opacity' && this._props.animatedDoc === this._props.collection) { return; } if (!kf[key]) { - this.props.animatedDoc[key] = undefined; + this._props.animatedDoc[key] = undefined; } else { const stored = kf[key]; - this.props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored; + this._props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); }; @@ -283,7 +289,7 @@ export class Track extends React.Component<IProps> { @action interpolate = (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { - if (key === 'opacity' && this.props.animatedDoc === this.props.collection) { + if (key === 'opacity' && this._props.animatedDoc === this._props.collection) { return; } if (typeof left[key] === 'number' && typeof right[key] === 'number') { @@ -291,11 +297,11 @@ export class Track extends React.Component<IProps> { const dif = NumCast(right[key]) - NumCast(left[key]); const deltaLeft = this.time - NumCast(left.time); const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); - this.props.animatedDoc[key] = NumCast(left[key]) + dif * ratio; + this._props.animatedDoc[key] = NumCast(left[key]) + dif * ratio; } else { // case data const stored = left[key]; - this.props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored; + this._props.animatedDoc[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); }; @@ -314,8 +320,8 @@ export class Track extends React.Component<IProps> { @action onInnerDoubleClick = (e: React.MouseEvent) => { const inner = this._inner.current!; - const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); - this.createRegion(RegionHelpers.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); + const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this._props.transform.Scale); + this.createRegion(RegionHelpers.convertPixelTime(offsetX, 'mili', 'time', this._props.tickSpacing, this._props.tickIncrement)); }; /** @@ -335,7 +341,7 @@ export class Track extends React.Component<IProps> { regiondata.duration = rightRegion.position - regiondata.position; } if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - Cast(this.props.animatedDoc.regions, listSpec(Doc))?.push(regiondata); + Cast(this._props.animatedDoc.regions, listSpec(Doc))?.push(regiondata); this._newKeyframe = true; this.saveStateRegion = regiondata; return regiondata; @@ -370,7 +376,7 @@ export class Track extends React.Component<IProps> { copyDocDataToKeyFrame = (doc: Doc) => { var somethingChanged = false; this.primitiveWhitelist.map(key => { - const originalVal = this.props.animatedDoc[key]; + const originalVal = this._props.animatedDoc[key]; somethingChanged = somethingChanged || originalVal !== doc[key]; if (doc.type === RegionHelpers.KeyframeType.end && key === 'opacity') doc.opacity = 0; else doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal; @@ -391,10 +397,10 @@ export class Track extends React.Component<IProps> { ref={this._inner} style={{ height: `${this._trackHeight}px` }} onDoubleClick={this.onInnerDoubleClick} - onPointerOver={() => Doc.BrushDoc(this.props.animatedDoc)} - onPointerOut={() => Doc.UnBrushDoc(this.props.animatedDoc)}> + onPointerOver={() => Doc.BrushDoc(this._props.animatedDoc)} + onPointerOut={() => Doc.UnBrushDoc(this._props.animatedDoc)}> {this.regions?.map((region, i) => { - return <Region key={`${i}`} {...this.props} saveStateKf={saveStateKf} RegionData={region} makeKeyData={this.makeKeyData} />; + return <Region key={`${i}`} {...this._props} saveStateKf={saveStateKf} RegionData={region} makeKeyData={this.makeKeyData} />; })} </div> </div> |