diff options
-rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Keyframe.tsx | 59 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Timeline.tsx | 51 | ||||
-rw-r--r-- | src/client/views/animationtimeline/TimelineMenu.tsx | 20 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Track.tsx | 40 |
5 files changed, 87 insertions, 89 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d5482bc12..126760234 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -206,9 +206,9 @@ export class MainView extends React.Component { if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); } - // if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1) { - // TimelineMenu.Instance.closeMenu(); - // } + if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-desc") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-input") === -1){ + TimelineMenu.Instance.closeMenu(); + } }); globalPointerUp = () => this.isPointerDown = false; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index b02d89bf0..2f0a968b9 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -28,7 +28,7 @@ export namespace KeyframeFunc { let leftMost: (RegionData | undefined) = undefined; let rightMost: (RegionData | undefined) = undefined; DocListCast(regions).forEach(region => { - let neighbor = RegionData(region as Doc); + let neighbor = RegionData(region); if (currentRegion.position! > neighbor.position) { if (!leftMost || neighbor.position > leftMost.position) { leftMost = neighbor; @@ -230,7 +230,6 @@ export class Keyframe extends React.Component<IProps> { let doclist = (await DocListCastAsync(this.regiondata.keyframes))!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { - TK = TK as Doc; if (TK.time === kfpos) existingkf = TK; }); if (existingkf) return existingkf; @@ -299,8 +298,8 @@ export class Keyframe extends React.Component<IProps> { this.regiondata.position = 0; } else if ((left && left.position + left.duration >= futureX)) { this.regiondata.position = left.position + left.duration; - } else if ((right && right.position <= futureX + this.pixelDuration)) { - this.regiondata.position = right.position - this.pixelDuration; + } else if ((right && right.position <= futureX + this.regiondata.duration)) { + this.regiondata.position = right.position - this.regiondata.duration; } else { this.regiondata.position = futureX; } @@ -353,7 +352,8 @@ export class Keyframe extends React.Component<IProps> { this.regiondata.duration -= offset; this.regiondata.position += offset; } - + this.keyframes[0].time = this.regiondata.position; + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } @@ -375,6 +375,8 @@ export class Keyframe extends React.Component<IProps> { } else { this.regiondata.duration += offset; } + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; } @@ -408,24 +410,36 @@ export class Keyframe extends React.Component<IProps> { @action makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { - let items = [ - TimelineMenu.Instance.addItem("button", "Show Data", () => { - runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + + TimelineMenu.Instance.addItem("button", "Show Data", () => { + runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); - }), - TimelineMenu.Instance.addItem("button", "Delete", () => {}), - TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}) - ]; - TimelineMenu.Instance.addMenu("Keyframe", items); + }), + TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}); + + TimelineMenu.Instance.addMenu("Keyframe"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); -} + } + + @action + makeRegionMenu = (kf: Doc, e: MouseEvent) => { + TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), + TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), + TimelineMenu.Instance.addMenu("Region"); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + } @action private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { if (type === KeyframeFunc.KeyframeType.default) { 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" onPointerDown={(e) => { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { + <div className="keyframeCircle" onPointerDown={(e) => { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.makeKeyframeMenu(kf, e.nativeEvent); @@ -546,7 +560,9 @@ export class Keyframe extends React.Component<IProps> { onPointerDown={this.onBarPointerDown}> <div className="leftResize" onPointerDown={this.onResizeLeft} ></div> <div className="rightResize" onPointerDown={this.onResizeRight}></div> - {this.keyframes.map(kf => { this.createKeyframeJSX(kf, kf.type as KeyframeFunc.KeyframeType); })} + {this.keyframes.map(kf => { + return this.createKeyframeJSX(kf, kf.type as KeyframeFunc.KeyframeType); + })} {this.keyframes.map( kf => { if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; @@ -561,16 +577,7 @@ export class Keyframe extends React.Component<IProps> { e.preventDefault(); e.stopPropagation(); this._mouseToggled = true; - let items = [ - TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), - TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), - TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), - ]; - TimelineMenu.Instance.addMenu("Region", items); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + this.makeRegionMenu(kf, e.nativeEvent); }}> </div> ); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 036c3ad9b..4eb5958b5 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,7 +1,6 @@ import * as React from "react"; import "./Timeline.scss"; -import { CollectionSubView } from "../collections/CollectionSubView"; -import { Document, listSpec } from "../../../new_fields/Schema"; +import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe } from "mobx"; @@ -13,8 +12,8 @@ import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDow import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; -import { playcustomapp } from "googleapis/build/src/apis/playcustomapp"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { FieldViewProps } from "../nodes/FieldView"; +import { KeyframeFunc } from "./Keyframe"; @@ -62,6 +61,8 @@ export class Timeline extends React.Component<FieldViewProps> { @observable private _timelineVisible = false; @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; + @observable private _mutationDisposer:MutationObserver[] = []; + @observable private _reactionDisposer:IReactionDisposer[] = []; @computed @@ -89,7 +90,7 @@ export class Timeline extends React.Component<FieldViewProps> { console.log(this.props.Document.duration); if (this.props.Document.duration) { this._time = Math.round(NumCast(this.props.Document.duration)) * 1000; - reaction(() => { + this._reactionDisposer.push(reaction(() => { return NumCast(this.props.Document.curPage); }, curPage => { if (!this._isPlaying) { @@ -97,12 +98,11 @@ export class Timeline extends React.Component<FieldViewProps> { this.props.Document.curPage = this._currentBarX; this.play(); } - - }); + })); } } runInAction(() => { - reaction(() => { + this._reactionDisposer.push(reaction(() => { return this._time; }, () => { this._ticks = []; @@ -111,7 +111,7 @@ export class Timeline extends React.Component<FieldViewProps> { i += 1000; } this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); - }, {fireImmediately:true}); + }, {fireImmediately:true})); this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; @@ -321,7 +321,6 @@ export class Timeline extends React.Component<FieldViewProps> { return `${min}:${sec}`; } - timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; let timelineContainer = this._timelineWrapper.current!; @@ -354,31 +353,41 @@ export class Timeline extends React.Component<FieldViewProps> { onWheelZoom = (e: React.WheelEvent) => { e.preventDefault(); e.stopPropagation(); + let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; + let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); e.deltaY < 0 ? this.zoom(true) : this.zoom(false); + let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this._infoContainer.current!.scrollLeft = currPixel - offset; + this._visibleStart = currPixel - offset; } @action zoom = (dir: boolean) => { + let spacingChange = this._tickSpacing; + let incrementChange = this._tickIncrement; if (dir){ if (!(this._tickSpacing === 100 && this._tickIncrement === 1000)){ if (this._tickSpacing >= 100) { - this._tickIncrement /= 2; - this._tickSpacing = 50; + incrementChange /= 2; + spacingChange = 50; } else { - this._tickSpacing += 10; + spacingChange += 5; } } } else { - if (this._totalLength >= this._infoContainer.current!.getBoundingClientRect().width){ - if (this._tickSpacing <= 50) { - this._tickSpacing = 100; - this._tickIncrement *= 2; - } else { - this._tickSpacing -= 10; - } + if (this._tickSpacing <= 50) { + spacingChange = 100; + incrementChange *= 2; + } else { + spacingChange -= 5; } } - this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); + let finalLength = spacingChange * (this._time / incrementChange); + if (finalLength >= this._infoContainer.current!.getBoundingClientRect().width){ + this._totalLength = finalLength; + this._tickSpacing = spacingChange; + this._tickIncrement = incrementChange; + } } private timelineToolBox = (scale:number) => { diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 3e63eec61..572b35b90 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -31,31 +31,31 @@ export class TimelineMenu extends React.Component { closeMenu = () => { this._opacity = 0; this._currentMenu = []; + this._x = -1000000; + this._y = -1000000; } + @action addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { if (type === "input"){ - let ref = React.createRef<HTMLInputElement>(); - return <div className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard} size="lg"/><input className="timeline-menu-input" ref = {ref} placeholder={title} onChange={(e) => { + let inputRef = React.createRef<HTMLInputElement>(); + this._currentMenu.push( <div className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard} size="lg"/><input className="timeline-menu-input" ref = {inputRef} placeholder={title} onChange={(e) => { let text = e.target.value; document.addEventListener("keypress", (e:KeyboardEvent) => { if (e.keyCode === 13) { event(text); } }); - }}/></div>; + }}/></div>); } else if (type === "button") { - let ref = React.createRef<HTMLDivElement>(); - return <div className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine}size="lg"/><p className="timeline-menu-desc" onClick={event}>{title}</p></div>; + let buttonRef = React.createRef<HTMLDivElement>(); + this._currentMenu.push( <div className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine}size="lg"/><p className="timeline-menu-desc" onClick={event}>{title}</p></div>); } - return <div></div>; } @action - addMenu = (title:string, items: JSX.Element[]) => { - items.unshift(<div className="timeline-menu-header"><p className="timeline-menu-header-desc">{title}</p></div>); - this._currentMenu = items; - + addMenu = (title:string) => { + this._currentMenu.unshift(<div className="timeline-menu-header"><p className="timeline-menu-header-desc">{title}</p></div>); } render() { diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 5a43f5b2a..13fe55e80 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,16 +1,14 @@ import * as React from "react"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, IObservableObject, runInAction, autorun } from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun } from "mobx"; import "./Track.scss"; import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; -import { FlyoutProps } from "./Timeline"; import { Transform } from "../../util/Transform"; import { RichTextField } from "../../../new_fields/RichTextField"; -import { createObjectBindingPattern } from "typescript"; import { DateField } from "../../../new_fields/DateField"; interface IProps { @@ -68,23 +66,7 @@ export class Track extends React.Component<IProps> { if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); //right keyframe, if it exists - // while (leftkf !== undefined) { - // if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { - // let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), leftkf!); - // edge!.key = Doc.MakeCopy(kf.key as Doc, true); - // leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); - // (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; - // (Cast(leftkf!.key, Doc)! as Doc).opacity = 1; - // } else if (leftkf!.key ) { - // leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); - // } - - // } - - - - + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); //right keyframe, if it exists if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), leftkf!); edge!.key = Doc.MakeCopy(kf.key as Doc, true); @@ -160,7 +142,6 @@ export class Track extends React.Component<IProps> { this.props.node[key] = new RichTextField(nodeData); } } else if (key === "creationDate") { - this.props.node[key] = new DateField(); } else { this.props.node[key] = kfNode[key]; @@ -175,9 +156,9 @@ export class Track extends React.Component<IProps> { @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { - if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns") acc.push(key); + if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns" && key !== "creationDate") acc.push(key); return acc; - }, []) as string[]; + }, []); } @action @@ -280,15 +261,16 @@ export class Track extends React.Component<IProps> { createRegion = (position: number) => { let regiondata = KeyframeFunc.defaultKeyframe(); regiondata.position = position; - let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, regiondata, this.regions); let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); - if ((rightRegion && leftRegion && rightRegion.position - (leftRegion.position + leftRegion.duration) < NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut)) || (rightRegion && rightRegion.position - regiondata.position < NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - return; - } else if (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut)) { + + if (rightRegion && rightRegion.position - regiondata.position <= 4000) { regiondata.duration = rightRegion.position - regiondata.position; + } + if(this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))){ + this.regions.push(regiondata); + return regiondata; } - this.regions.push(regiondata); - return regiondata; + } |