aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/animationtimeline/Region.scss (renamed from src/client/views/animationtimeline/Keyframe.scss)13
-rw-r--r--src/client/views/animationtimeline/Region.tsx (renamed from src/client/views/animationtimeline/Keyframe.tsx)142
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx49
-rw-r--r--src/client/views/animationtimeline/TimelineMenu.tsx100
-rw-r--r--src/client/views/animationtimeline/TimelineOverview.tsx112
-rw-r--r--src/client/views/animationtimeline/Track.tsx77
-rw-r--r--src/fields/List.ts5
7 files changed, 261 insertions, 237 deletions
diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Region.scss
index 38eb103c6..f7476ab55 100644
--- a/src/client/views/animationtimeline/Keyframe.scss
+++ b/src/client/views/animationtimeline/Region.scss
@@ -1,5 +1,4 @@
-@import "./../global/globalCssVariables.scss";
-
+@import './../global/globalCssVariables.scss';
$timelineColor: #9acedf;
$timelineDark: #77a1aa;
@@ -9,7 +8,7 @@ $timelineDark: #77a1aa;
width: 5px;
position: absolute;
- // pointer-events: none;
+ // pointer-events: none;
.menubox {
width: 200px;
height: 200px;
@@ -27,11 +26,15 @@ $timelineDark: #77a1aa;
.leftResize {
left: -10px;
border: 3px solid black;
+ transform: rotate(45deg) scale(0.25) !important;
+ background-color: black !important;
}
.rightResize {
right: -10px;
border: 3px solid black;
+ transform: rotate(45deg) scale(0.25) !important;
+ background-color: black !important;
}
.keyframe-indicator {
@@ -100,6 +103,4 @@ $timelineDark: #77a1aa;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Region.tsx
index addc00c85..df00924c6 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Region.tsx
@@ -5,19 +5,16 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { createSchema, defaultSpec, listSpec, makeInterface } from '../../../fields/Schema';
import { Cast, NumCast } from '../../../fields/Types';
-import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
-import { CollectionDockingView } from '../collections/CollectionDockingView';
import '../global/globalCssVariables.scss';
-import { OpenWhereMod } from '../nodes/DocumentView';
-import './Keyframe.scss';
+import './Region.scss';
import './Timeline.scss';
import { TimelineMenu } from './TimelineMenu';
/**
* Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also
*/
-export namespace KeyframeFunc {
+export namespace RegionHelpers {
export enum KeyframeType {
end = 'end',
fade = 'fade',
@@ -29,7 +26,7 @@ export namespace KeyframeFunc {
right = 'right',
}
- export const findAdjacentRegion = (dir: KeyframeFunc.Direction, currentRegion: Doc, regions: Doc[]): RegionData | undefined => {
+ export const findAdjacentRegion = (dir: RegionHelpers.Direction, currentRegion: Doc, regions: Doc[]): RegionData | undefined => {
let leftMost: RegionData | undefined = undefined;
let rightMost: RegionData | undefined = undefined;
regions.forEach(region => {
@@ -128,10 +125,11 @@ interface IProps {
collection: Doc;
tickSpacing: number;
tickIncrement: number;
+ saveStateKf: Doc | undefined;
time: number;
changeCurrentBarX: (x: number) => void;
transform: Transform;
- makeKeyData: (region: RegionData, pos: number, kftype: KeyframeFunc.KeyframeType) => Doc;
+ makeKeyData: (region: RegionData, pos: number, kftype: RegionHelpers.KeyframeType) => Doc;
}
/**
@@ -158,7 +156,7 @@ interface IProps {
* @author Andrew Kim
*/
@observer
-export class Keyframe extends React.Component<IProps> {
+export class Region extends React.Component<IProps> {
@observable private _bar = React.createRef<HTMLDivElement>();
@observable private _mouseToggled = false;
@observable private _doubleClickEnabled = false;
@@ -173,16 +171,16 @@ export class Keyframe extends React.Component<IProps> {
return DocListCast(this.regiondata.keyframes);
}
@computed private get pixelPosition() {
- return KeyframeFunc.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 KeyframeFunc.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 KeyframeFunc.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 KeyframeFunc.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) {
@@ -192,10 +190,10 @@ export class Keyframe 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, KeyframeFunc.KeyframeType.end);
- const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade);
- const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade);
- const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, KeyframeFunc.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;
@@ -233,10 +231,10 @@ export class Keyframe extends React.Component<IProps> {
if (e.movementX !== 0) {
this._mouseToggled = true;
}
- const left = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions)!;
- const right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions)!;
+ 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 + KeyframeFunc.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) {
@@ -275,8 +273,8 @@ export class Keyframe extends React.Component<IProps> {
e.preventDefault();
e.stopPropagation();
const bar = this._bar.current!;
- const offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
- const leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions);
+ 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;
this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - (leftRegion.position + leftRegion.duration);
@@ -299,8 +297,8 @@ export class Keyframe extends React.Component<IProps> {
e.preventDefault();
e.stopPropagation();
const bar = this._bar.current!;
- const offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
- const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions);
+ 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) {
//case 1: when third to last keyframe is in the way
@@ -318,13 +316,13 @@ export class Keyframe extends React.Component<IProps> {
createKeyframe = async (clientX: number) => {
this._mouseToggled = true;
const bar = this._bar.current!;
- const offset = KeyframeFunc.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), KeyframeFunc.KeyframeType.default);
+ this.props.makeKeyData(this.regiondata, Math.round(position + offset), RegionHelpers.KeyframeType.default);
this.regiondata.hasData = true;
- this.props.changeCurrentBarX(KeyframeFunc.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
}
};
@@ -332,7 +330,7 @@ export class Keyframe extends React.Component<IProps> {
moveKeyframe = async (e: React.MouseEvent, kf: Doc) => {
e.preventDefault();
e.stopPropagation();
- this.props.changeCurrentBarX(KeyframeFunc.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));
};
/**
@@ -340,17 +338,14 @@ export class Keyframe extends React.Component<IProps> {
*/
@action
makeKeyframeMenu = (kf: Doc, e: MouseEvent) => {
- TimelineMenu.Instance.addItem('button', 'Toggle Fade Only', () => {
- kf.type = kf.type === KeyframeFunc.KeyframeType.fade ? KeyframeFunc.KeyframeType.default : KeyframeFunc.KeyframeType.fade;
- }),
- TimelineMenu.Instance.addItem(
- 'button',
- 'Delete',
- action(() => {
- (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
- this.forceUpdate();
- })
- ),
+ TimelineMenu.Instance.addItem(
+ 'button',
+ 'Delete',
+ action(() => {
+ (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
+ this.forceUpdate();
+ })
+ ),
TimelineMenu.Instance.addItem(
'input',
'Move',
@@ -362,7 +357,10 @@ export class Keyframe extends React.Component<IProps> {
}
if (!cannotMove) {
this.keyframes[kfIndex].time = parseInt(val, 10);
- this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn;
+ if (kfIndex === 1) {
+ this.regiondata.fadeIn = parseInt(val, 10) - this.regiondata.position;
+ }
+ // this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn;
}
})
);
@@ -485,38 +483,34 @@ export class Keyframe extends React.Component<IProps> {
drawKeyframes = () => {
const keyframeDivs: JSX.Element[] = [];
return DocListCast(this.regiondata.keyframes).map(kf => {
- if ((kf.type as KeyframeFunc.KeyframeType) !== KeyframeFunc.KeyframeType.end) {
- return (
- <>
- <div className="keyframe" style={{ left: `${KeyframeFunc.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement) - this.pixelPosition}px` }}>
- <div className="divider"></div>
- <div
- className="keyframeCircle keyframe-indicator"
- onPointerDown={e => {
- e.preventDefault();
- e.stopPropagation();
- this.moveKeyframe(e, kf);
- }}
- onContextMenu={(e: React.MouseEvent) => {
- e.preventDefault();
- e.stopPropagation();
- this.makeKeyframeMenu(kf, e.nativeEvent);
- }}
- onDoubleClick={e => {
- e.preventDefault();
- e.stopPropagation();
- }}></div>
- </div>
- <div className="keyframe-information" />
- </>
- );
- } else {
- return (
- <div className="keyframe" style={{ left: `${KeyframeFunc.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement) - this.pixelPosition}px` }}>
- <div className="divider" />
+ return (
+ <>
+ <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,
+ }}
+ onPointerDown={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.moveKeyframe(e, kf);
+ }}
+ onContextMenu={(e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.makeKeyframeMenu(kf, e.nativeEvent);
+ }}
+ onDoubleClick={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ />
</div>
- );
- }
+ <div className="keyframe-information" />
+ </>
+ );
});
};
@@ -531,8 +525,8 @@ export class Keyframe extends React.Component<IProps> {
if (index !== this.keyframes.length - 1) {
const right = this.keyframes[index + 1];
const bodyRef = React.createRef<HTMLDivElement>();
- const kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement);
- const rightPos = KeyframeFunc.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}
@@ -579,12 +573,12 @@ export class Keyframe extends React.Component<IProps> {
}%, rgba(154, 206, 223, 0) 100% )`,
}}
onPointerDown={this.onBarPointerDown}>
+ {this.drawKeyframes()}
+ {this.drawKeyframeDividers()}
<div className="leftResize keyframe-indicator" onPointerDown={this.onResizeLeft}></div>
{/* <div className="keyframe-information"></div> */}
<div className="rightResize keyframe-indicator" onPointerDown={this.onResizeRight}></div>
{/* <div className="keyframe-information"></div> */}
- {this.drawKeyframes()}
- {this.drawKeyframeDividers()}
</div>
);
}
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 7ca13756a..3675238fd 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,28 +1,28 @@
+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, runInAction, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from '../../../Utils';
+import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+import clamp from '../../util/clamp';
import { FieldViewProps } from '../nodes/FieldView';
-import { KeyframeFunc } from './Keyframe';
+import { RegionHelpers } from './Region';
import './Timeline.scss';
import { TimelineOverview } from './TimelineOverview';
import { Track } from './Track';
-import clamp from '../../util/clamp';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { IconLookup } from '@fortawesome/fontawesome-svg-core';
/**
- * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are
+ * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are
* zooming, panning, currentBarX (scrubber movement). Most of the UI stuff is also handled here. You shouldn't really make
* any logical changes here. Most work is needed on UI.
*
* The hierarchy works this way:
*
- * Timeline.tsx --> Track.tsx --> Keyframe.tsx
+ * Timeline.tsx --> Track.tsx --> Region.tsx
| |
| TimelineMenu.tsx (timeline's custom contextmenu)
|
@@ -58,7 +58,6 @@ export class Timeline extends React.Component<FieldViewProps> {
//boolean vars and instance vars
@observable private _currentBarX: number = 0;
@observable private _windSpeed: number = 1;
- @observable private _isPlaying: boolean = false; //scrubber playing
@observable private _totalLength: number = 0;
@observable private _visibleLength: number = 0;
@observable private _visibleStart: number = 0;
@@ -69,6 +68,8 @@ export class Timeline extends React.Component<FieldViewProps> {
@observable private _playButton = faPlayCircle;
@observable private _titleHeight = 0;
+ @observable public IsPlaying: boolean = false; //scrubber playing
+
/**
* collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit.
*/
@@ -144,14 +145,17 @@ export class Timeline extends React.Component<FieldViewProps> {
@action
play = () => {
const playTimeline = () => {
- if (this._isPlaying) {
+ if (this.IsPlaying) {
this.changeCurrentBarX(this._currentBarX >= this._totalLength ? 0 : this._currentBarX + this._windSpeed);
setTimeout(playTimeline, 15);
}
};
- this._isPlaying = !this._isPlaying;
- this._playButton = this._isPlaying ? faPauseCircle : faPlayCircle;
- this._isPlaying && playTimeline();
+ Array.from(this.mapOfTracks.values())
+ .filter(key => key)
+ .forEach(key => key!.saveKeyframe());
+ this.IsPlaying = !this.IsPlaying;
+ this._playButton = this.IsPlaying ? faPauseCircle : faPlayCircle;
+ this.IsPlaying && playTimeline();
};
/**
@@ -221,7 +225,7 @@ export class Timeline extends React.Component<FieldViewProps> {
if (this._visibleStart + this._visibleLength + 20 >= this._totalLength) {
this._visibleStart -= e.movementX;
this._totalLength -= e.movementX;
- this._time -= KeyframeFunc.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+ this._time -= RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
this.props.Document.AnimationLength = this._time;
}
return false;
@@ -278,11 +282,11 @@ export class Timeline extends React.Component<FieldViewProps> {
e.preventDefault();
e.stopPropagation();
const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left;
- const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
- const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+ const prevTime = RegionHelpers.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+ const prevCurrent = RegionHelpers.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
this.zoom(e.deltaY < 0);
- const currPixel = KeyframeFunc.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
- const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+ const currPixel = RegionHelpers.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+ const currCurrent = RegionHelpers.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
this._infoContainer.current!.scrollLeft = currPixel - offset;
this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0;
this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0;
@@ -478,7 +482,7 @@ export class Timeline extends React.Component<FieldViewProps> {
// @computed
getCurrentTime = () => {
- const current = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+ const current = RegionHelpers.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
return this.toReadTime(current > this._time ? this._time : current);
};
@@ -505,13 +509,13 @@ export class Timeline extends React.Component<FieldViewProps> {
@action
toAuthoring = () => {
this._time = Math.ceil((this.findLongestTime() ?? 1) / 100000) * 100000;
- this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+ this._totalLength = RegionHelpers.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
};
@action
toPlay = () => {
this._time = this.findLongestTime();
- this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+ this._totalLength = RegionHelpers.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
};
/**
@@ -535,6 +539,7 @@ export class Timeline extends React.Component<FieldViewProps> {
{this.children.map(doc => (
<Track
ref={ref => this.mapOfTracks.push(ref)}
+ timeline={this}
animatedDoc={doc}
currentBarX={this._currentBarX}
changeCurrentBarX={this.changeCurrentBarX}
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index aa422c092..1769c41bd 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -1,12 +1,11 @@
-import * as React from "react";
-import { observable, action, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import "./TimelineMenu.scss";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons";
-import { Utils } from "../../../Utils";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
-
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
+import { faChartLine, faClipboard } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils } from '../../../Utils';
+import './TimelineMenu.scss';
@observer
export class TimelineMenu extends React.Component {
@@ -25,9 +24,9 @@ export class TimelineMenu extends React.Component {
@action
openMenu = (x?: number, y?: number) => {
this._opacity = 1;
- x ? this._x = x : this._x = 0;
- y ? this._y = y : this._y = 0;
- }
+ x ? (this._x = x) : (this._x = 0);
+ y ? (this._y = y) : (this._y = 0);
+ };
@action
closeMenu = () => {
@@ -35,44 +34,67 @@ export class TimelineMenu extends React.Component {
this._currentMenu = [];
this._x = -1000000;
this._y = -1000000;
- }
+ };
@action
- addItem = (type: "input" | "button", title: string, event: (e: any, ...args: any[]) => void) => {
- if (type === "input") {
+ addItem = (type: 'input' | 'button', title: string, event: (e: any, ...args: any[]) => void) => {
+ if (type === 'input') {
const inputRef = React.createRef<HTMLInputElement>();
- let text = "";
- this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard as IconLookup} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
- e.stopPropagation();
- text = e.target.value;
- }} onKeyDown={(e) => {
- if (e.keyCode === 13) {
- event(text);
- this.closeMenu();
- e.stopPropagation();
- }
- }} /></div>);
- } else if (type === "button") {
- this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine as IconLookup} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
- e.preventDefault();
- e.stopPropagation();
- event(e);
- this.closeMenu();
- }}>{title}</p></div>);
+ let text = '';
+ this._currentMenu.push(
+ <div key={Utils.GenerateGuid()} className="timeline-menu-item">
+ <FontAwesomeIcon icon={faClipboard as IconLookup} size="lg" />
+ <input
+ className="timeline-menu-input"
+ ref={inputRef}
+ placeholder={title}
+ onChange={e => {
+ e.stopPropagation();
+ text = e.target.value;
+ }}
+ onKeyDown={e => {
+ if (e.keyCode === 13) {
+ event(Number(text));
+ this.closeMenu();
+ e.stopPropagation();
+ }
+ }}
+ />
+ </div>
+ );
+ } else if (type === 'button') {
+ this._currentMenu.push(
+ <div key={Utils.GenerateGuid()} className="timeline-menu-item">
+ <FontAwesomeIcon icon={faChartLine as IconLookup} size="lg" />
+ <p
+ className="timeline-menu-desc"
+ onClick={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ event(e);
+ this.closeMenu();
+ }}>
+ {title}
+ </p>
+ </div>
+ );
}
- }
+ };
@action
addMenu = (title: string) => {
- this._currentMenu.unshift(<div key={Utils.GenerateGuid()} className="timeline-menu-header"><p className="timeline-menu-header-desc">{title}</p></div>);
- }
+ this._currentMenu.unshift(
+ <div key={Utils.GenerateGuid()} className="timeline-menu-header">
+ <p className="timeline-menu-header-desc">{title}</p>
+ </div>
+ );
+ };
render() {
return (
- <div key={Utils.GenerateGuid()} className="timeline-menu-container" style={{ opacity: this._opacity, left: this._x, top: this._y }} >
+ <div key={Utils.GenerateGuid()} className="timeline-menu-container" style={{ opacity: this._opacity, left: this._x, top: this._y }}>
{this._currentMenu}
</div>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx
index 81a5587e4..82ac69a3b 100644
--- a/src/client/views/animationtimeline/TimelineOverview.tsx
+++ b/src/client/views/animationtimeline/TimelineOverview.tsx
@@ -1,11 +1,9 @@
-import * as React from "react";
-import { observable, action, computed, runInAction, reaction, IReactionDisposer } from "mobx";
-import { observer } from "mobx-react";
-import "./TimelineOverview.scss";
-import * as $ from 'jquery';
-import { Timeline } from "./Timeline";
-import { Keyframe, KeyframeFunc } from "./Keyframe";
-
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { RegionHelpers } from './Region';
+import { Timeline } from './Timeline';
+import './TimelineOverview.scss';
interface TimelineOverviewProps {
totalLength: number;
@@ -21,9 +19,8 @@ interface TimelineOverviewProps {
tickIncrement: number;
}
-
@observer
-export class TimelineOverview extends React.Component<TimelineOverviewProps>{
+export class TimelineOverview extends React.Component<TimelineOverviewProps> {
@observable private _visibleRef = React.createRef<HTMLDivElement>();
@observable private _scrubberRef = React.createRef<HTMLDivElement>();
@observable private authoringContainer = React.createRef<HTMLDivElement>();
@@ -49,13 +46,13 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
this.setOverviewWidth();
});
}
- },
+ }
);
- }
+ };
componentWillUnmount = () => {
this._authoringReaction && this._authoringReaction();
- }
+ };
@action
setOverviewWidth() {
@@ -66,8 +63,7 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
if (this.props.isAuthoring) {
this.activeOverviewWidth = this.overviewBarWidth;
- }
- else {
+ } else {
this.activeOverviewWidth = this.playbarWidth;
}
}
@@ -76,37 +72,37 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
onPointerDown = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("pointermove", this.onPanX);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointermove", this.onPanX);
- document.addEventListener("pointerup", this.onPointerUp);
- }
+ document.removeEventListener('pointermove', this.onPanX);
+ document.removeEventListener('pointerup', this.onPointerUp);
+ document.addEventListener('pointermove', this.onPanX);
+ document.addEventListener('pointerup', this.onPointerUp);
+ };
@action
onPanX = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- const movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX;
- this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength);
- }
+ const movX = (this.props.visibleStart / this.props.totalLength) * this.DEFAULT_WIDTH + e.movementX;
+ this.props.movePanX((movX / this.DEFAULT_WIDTH) * this.props.totalLength);
+ };
@action
onPointerUp = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("pointermove", this.onPanX);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
+ document.removeEventListener('pointermove', this.onPanX);
+ document.removeEventListener('pointerup', this.onPointerUp);
+ };
@action
onScrubberDown = (e: React.PointerEvent) => {
e.preventDefault();
e.stopPropagation();
- document.removeEventListener("pointermove", this.onScrubberMove);
- document.removeEventListener("pointerup", this.onScrubberUp);
- document.addEventListener("pointermove", this.onScrubberMove);
- document.addEventListener("pointerup", this.onScrubberUp);
- }
+ document.removeEventListener('pointermove', this.onScrubberMove);
+ document.removeEventListener('pointerup', this.onScrubberUp);
+ document.addEventListener('pointermove', this.onScrubberMove);
+ document.addEventListener('pointerup', this.onScrubberUp);
+ };
@action
onScrubberMove = (e: PointerEvent) => {
@@ -115,22 +111,22 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
const scrubberRef = this._scrubberRef.current!;
const left = scrubberRef.getBoundingClientRect().left;
const offsetX = Math.round(e.clientX - left);
- this.props.changeCurrentBarX((((offsetX) / this.activeOverviewWidth) * this.props.totalLength) + this.props.currentBarX);
- }
+ this.props.changeCurrentBarX((offsetX / this.activeOverviewWidth) * this.props.totalLength + this.props.currentBarX);
+ };
@action
onScrubberUp = (e: PointerEvent) => {
e.preventDefault();
e.stopPropagation();
- document.removeEventListener("pointermove", this.onScrubberMove);
- document.removeEventListener("pointerup", this.onScrubberUp);
- }
+ document.removeEventListener('pointermove', this.onScrubberMove);
+ document.removeEventListener('pointerup', this.onScrubberUp);
+ };
@action
getTimes() {
- const vis = KeyframeFunc.convertPixelTime(this.props.visibleLength, "mili", "time", this.props.tickSpacing, this.props.tickIncrement);
- const x = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement);
- const start = KeyframeFunc.convertPixelTime(this.props.visibleStart, "mili", "time", this.props.tickSpacing, this.props.tickIncrement);
+ const vis = RegionHelpers.convertPixelTime(this.props.visibleLength, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
+ const x = RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
+ const start = RegionHelpers.convertPixelTime(this.props.visibleStart, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
this.visibleTime = vis;
this.currentX = x;
this.visibleStart = start;
@@ -144,7 +140,7 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
const visibleBarWidth = percentVisible * this.activeOverviewWidth;
const percentScrubberStart = this.currentX / this.props.time;
- let scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth;
+ let scrubberStart = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth;
if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth;
const percentBarStart = this.visibleStart / this.props.time;
@@ -153,29 +149,25 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth;
if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth;
- const timeline = this.props.isAuthoring ? [
-
- <div key="timeline-overview-container" className="timeline-overview-container overviewBar" id="timelineOverview" ref={this.authoringContainer}>
- <div ref={this._visibleRef} key="1" className="timeline-overview-visible" style={{ left: `${barStart}px`, width: `${visibleBarWidth}px` }} onPointerDown={this.onPointerDown}></div>,
- <div ref={this._scrubberRef} key="2" className="timeline-overview-scrubber-container" style={{ left: `${scrubberStart}px` }} onPointerDown={this.onScrubberDown}>
- <div key="timeline-overview-scrubber-head" className="timeline-overview-scrubber-head"></div>
- </div>
- </div>
- ] : [
- <div key="1" className="timeline-play-bar overviewBar" id="timelinePlay" ref={this.playbackContainer}>
- <div ref={this._scrubberRef} className="timeline-play-head" style={{ left: `${scrubberStart}px` }} onPointerDown={this.onScrubberDown}></div>
- </div>,
- <div key="2" className="timeline-play-tail" style={{ width: `${playWidth}px` }}></div>
- ];
+ const timeline = this.props.isAuthoring
+ ? [
+ <div key="timeline-overview-container" className="timeline-overview-container overviewBar" id="timelineOverview" ref={this.authoringContainer}>
+ <div ref={this._visibleRef} key="1" className="timeline-overview-visible" style={{ left: `${barStart}px`, width: `${visibleBarWidth}px` }} onPointerDown={this.onPointerDown}></div>,
+ <div ref={this._scrubberRef} key="2" className="timeline-overview-scrubber-container" style={{ left: `${scrubberStart}px` }} onPointerDown={this.onScrubberDown}>
+ <div key="timeline-overview-scrubber-head" className="timeline-overview-scrubber-head"></div>
+ </div>
+ </div>,
+ ]
+ : [
+ <div key="1" className="timeline-play-bar overviewBar" id="timelinePlay" ref={this.playbackContainer}>
+ <div ref={this._scrubberRef} className="timeline-play-head" style={{ left: `${scrubberStart}px` }} onPointerDown={this.onScrubberDown}></div>
+ </div>,
+ <div key="2" className="timeline-play-tail" style={{ width: `${playWidth}px` }}></div>,
+ ];
return (
<div className="timeline-flex">
- <div className="timelineOverview-bounding">
- {timeline}
- </div>
+ <div className="timelineOverview-bounding">{timeline}</div>
</div>
);
}
-
}
-
-
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 1010332f5..2fd062a88 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,17 +1,19 @@
import { action, computed, intercept, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, Opt, DocListCastAsync } from '../../../fields/Doc';
+import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc';
import { Copy } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { listSpec } from '../../../fields/Schema';
import { Cast, NumCast } from '../../../fields/Types';
import { Transform } from '../../util/Transform';
-import { Keyframe, KeyframeFunc, RegionData } from './Keyframe';
+import { Region, RegionData, RegionHelpers } from './Region';
+import { Timeline } from './Timeline';
import './Track.scss';
interface IProps {
+ timeline: Timeline;
animatedDoc: Doc;
currentBarX: number;
transform: Transform;
@@ -32,14 +34,14 @@ export class Track extends React.Component<IProps> {
@observable private _newKeyframe: boolean = false;
private readonly MAX_TITLE_HEIGHT = 75;
@observable private _trackHeight = 0;
- private primitiveWhitelist = ['x', 'y', '_width', '_height', 'opacity', '_layout_scrollTop'];
+ private primitiveWhitelist = ['x', 'y', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop'];
private objectWhitelist = ['data'];
@computed private get regions() {
return DocListCast(this.props.animatedDoc.regions);
}
@computed private get time() {
- return NumCast(KeyframeFunc.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));
}
async componentDidMount() {
@@ -85,36 +87,36 @@ export class Track extends React.Component<IProps> {
*/
@action
saveKeyframe = async () => {
- const keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List<Doc>;
- const kfIndex = keyframes.indexOf(this.saveStateKf!);
+ if (this.props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) {
+ this.saveStateKf = undefined;
+ this.saveStateRegion = undefined;
+ return;
+ }
+ const keyframes = Cast(this.saveStateRegion.keyframes, listSpec(Doc)) as List<Doc>;
+ const kfIndex = keyframes.indexOf(this.saveStateKf);
const kf = keyframes[kfIndex] as Doc; //index in the keyframe
if (this._newKeyframe) {
- DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => {
+ DocListCast(this.saveStateRegion.keyframes).forEach((kf, index) => {
this.copyDocDataToKeyFrame(kf);
kf.opacity = index === 0 || index === 3 ? 0.1 : 1;
});
this._newKeyframe = false;
}
if (!kf) return;
- if (kf.type === KeyframeFunc.KeyframeType.default) {
- // only save for non-fades
- this.copyDocDataToKeyFrame(kf);
- const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
- const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists
- if (leftkf?.type === KeyframeFunc.KeyframeType.fade) {
+ // only save for non-fades
+ if (this.copyDocDataToKeyFrame(kf)) {
+ const leftkf = RegionHelpers.calcMinLeft(this.saveStateRegion, this.time, kf); // lef keyframe, if it exists
+ const rightkf = RegionHelpers.calcMinRight(this.saveStateRegion, this.time, kf); //right keyframe, if it exists
+ if (leftkf?.type === RegionHelpers.KeyframeType.end) {
//replicating this keyframe to fades
- const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
+ const edge = RegionHelpers.calcMinLeft(this.saveStateRegion, this.time, leftkf);
edge && this.copyDocDataToKeyFrame(edge);
leftkf && this.copyDocDataToKeyFrame(leftkf);
- edge && (edge.opacity = 0.1);
- leftkf && (leftkf.opacity = 1);
}
- if (rightkf?.type === KeyframeFunc.KeyframeType.fade) {
- const edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf);
+ if (rightkf?.type === RegionHelpers.KeyframeType.end) {
+ const edge = RegionHelpers.calcMinRight(this.saveStateRegion, this.time, rightkf);
edge && this.copyDocDataToKeyFrame(edge);
rightkf && this.copyDocDataToKeyFrame(rightkf);
- edge && (edge.opacity = 0.1);
- rightkf && (rightkf.opacity = 1);
}
}
keyframes[kfIndex] = kf;
@@ -143,7 +145,7 @@ export class Track extends React.Component<IProps> {
const r = region as RegionData; //for some region is returning undefined... which is not the case
if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) {
//basically when there is no additional keyframe at that timespot
- this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
+ this.makeKeyData(r, this.time, RegionHelpers.KeyframeType.default);
}
}
},
@@ -222,20 +224,20 @@ export class Track extends React.Component<IProps> {
*/
@action
timeChange = async () => {
- if (this.saveStateKf !== undefined) {
- await this.saveKeyframe();
- } else if (this._newKeyframe) {
+ if (this.saveStateKf !== undefined || this._newKeyframe) {
await this.saveKeyframe();
}
const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
if (regiondata) {
- const leftkf: Doc | undefined = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
- const rightkf: Doc | undefined = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists
+ const leftkf: Doc | undefined = await RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
+ const rightkf: Doc | undefined = await RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists
const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
if (currentkf) {
await this.applyKeys(currentkf);
- this.saveStateKf = currentkf;
- this.saveStateRegion = regiondata;
+ runInAction(() => {
+ this.saveStateKf = currentkf;
+ this.saveStateRegion = regiondata;
+ });
} else if (leftkf && rightkf) {
await this.interpolate(leftkf, rightkf);
}
@@ -277,7 +279,7 @@ export class Track extends React.Component<IProps> {
@action
interpolate = async (left: Doc, right: Doc) => {
this.primitiveWhitelist.forEach(key => {
- if (left[key] && right[key] && typeof left[key] === 'number' && typeof right[key] === 'number') {
+ if (typeof left[key] === 'number' && typeof right[key] === 'number') {
//if it is number, interpolate
const dif = NumCast(right[key]) - NumCast(left[key]);
const deltaLeft = this.time - NumCast(left.time);
@@ -306,7 +308,7 @@ export class Track extends React.Component<IProps> {
onInnerDoubleClick = (e: React.MouseEvent) => {
const inner = this._inner.current!;
const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale);
- this.createRegion(KeyframeFunc.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
+ this.createRegion(RegionHelpers.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
};
/**
@@ -316,10 +318,10 @@ export class Track extends React.Component<IProps> {
createRegion = (time: number) => {
if (this.findRegion(time) === undefined) {
//check if there is a region where double clicking (prevents phantom regions)
- const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
+ const regiondata = RegionHelpers.defaultKeyframe(); //create keyframe data
regiondata.position = time; //set position
- const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions);
+ const rightRegion = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.right, regiondata, this.regions);
if (rightRegion && rightRegion.position - regiondata.position <= 4000) {
//edge case when there is less than default 4000 duration space between this and right region
@@ -335,7 +337,7 @@ export class Track extends React.Component<IProps> {
};
@action
- makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => {
+ makeKeyData = (regiondata: RegionData, time: number, type: RegionHelpers.KeyframeType = RegionHelpers.KeyframeType.default) => {
//Kfpos is mouse offsetX, representing time
const trackKeyFrames = DocListCast(regiondata.keyframes);
const existingkf = trackKeyFrames.find(TK => TK.time === time);
@@ -359,16 +361,21 @@ export class Track extends React.Component<IProps> {
@action
copyDocDataToKeyFrame = (doc: Doc) => {
+ var somethingChanged = false;
this.primitiveWhitelist.map(key => {
const originalVal = this.props.animatedDoc[key];
- doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal;
+ 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;
});
+ return somethingChanged;
};
/**
* UI sstuff here. Not really much to change
*/
render() {
+ const saveStateKf = this.saveStateKf;
return (
<div className="track-container">
<div className="track">
@@ -380,7 +387,7 @@ export class Track extends React.Component<IProps> {
onPointerOver={() => Doc.BrushDoc(this.props.animatedDoc)}
onPointerOut={() => Doc.UnBrushDoc(this.props.animatedDoc)}>
{this.regions?.map((region, i) => {
- return <Keyframe key={`${i}`} {...this.props} RegionData={region} makeKeyData={this.makeKeyData} />;
+ return <Region key={`${i}`} {...this.props} saveStateKf={saveStateKf} RegionData={region} makeKeyData={this.makeKeyData} />;
})}
</div>
</div>
diff --git a/src/fields/List.ts b/src/fields/List.ts
index f3fcc87f7..da007e972 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -236,7 +236,10 @@ class ListImpl<T extends Field> extends ObjectField {
const list = new Proxy<this>(this, {
set: setter,
get: ListImpl.listGetter,
- ownKeys: target => Object.keys(target.__fieldTuples),
+ ownKeys: target => {
+ const keys = Object.keys(target.__fieldTuples);
+ return [...keys, '__realFields'];
+ },
getOwnPropertyDescriptor: (target, prop) => {
if (prop in target[FieldTuples]) {
return {