diff options
-rw-r--r-- | src/client/views/animationtimeline/Timeline.scss | 92 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Timeline.tsx | 88 | ||||
-rw-r--r-- | src/client/views/animationtimeline/TimelineOverview.scss | 29 | ||||
-rw-r--r-- | src/client/views/animationtimeline/TimelineOverview.tsx | 180 |
4 files changed, 284 insertions, 105 deletions
diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 02c1bdb16..a8dd9b9e7 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -10,39 +10,79 @@ $timelineDark: #77a1aa; display: flex; align-items: flex-start; flex-direction: row; - justify-content: space-evenly; + // justify-content: space-evenly; align-items: center; - top: 20px; + top: 3px; + width: 100%; - .timeline-icon { - color: $timelineColor; + .overview-tool { + display: flex; + justify-content: center; } - div { - padding: 0px; + .playbackControls { + display: flex; + margin-left: 30px; + max-width: 84px; + width: 84px; + + .timeline-icon { + color: $timelineColor; + margin-left: 3px; + } + + } + + .grid-box { + display: grid; + grid-template-columns: [first] 50% [line2] 25% [line3] 25%; + width: calc(100% - 150px); + // width: 100%; margin-left: 10px; + + .time-box { + margin-left: 5px; + display: flex; + } + + .mode-box { + display: flex; + margin-left: 5px; + } + + .overview-box { + width: 100%; + display: flex; + } + + div { + padding: 0px; + // margin-left: 10px; + } } .animation-text { - font-size: 20px; + // font-size: 16px; height: auto; width: auto; white-space: nowrap; - font-size: 16px; + font-size: 14px; color: black; letter-spacing: 2px; text-transform: uppercase; } .round-toggle { - height: 40px; - width: 80px; + height: 20px; + width: 40px; + min-width: 40px; background-color: white; - border: 2px solid black; + border: 2px solid $timelineDark; border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; top: 30px; + margin-left: 5px; input { position: absolute; @@ -52,29 +92,30 @@ $timelineDark: #77a1aa; } .round-toggle-slider { - height: 35px; - width: 35px; + height: 17px; + width: 17px; background-color: white; - border: 3px solid black; - border-radius: 20px; + border: 2px solid $timelineDark; + border-radius: 50%; transition: transform 500ms ease-in-out; margin-left: 0px; - margin-top: 0.5px; + // margin-top: 0.5px; } } } .time-input { - height: 40px; - width: 120px; + height: 20px; + // width: 120px; + width: 100%; white-space: nowrap; - font-size: 16px; + font-size: 12px; color: black; letter-spacing: 2px; text-transform: uppercase; padding-left: 5px; - + margin-left: 5px; } .tick { @@ -99,7 +140,7 @@ $timelineDark: #77a1aa; transition: transform 500ms ease; .info-container { - margin-top: 80px; + margin-top: 50px; right: 20px; position: absolute; height: calc(100% - 100px); @@ -138,9 +179,12 @@ $timelineDark: #77a1aa; .trackbox { top: 30px; + // TODO: where is this 30px coming from? height: calc(100% - 30px); + // height: 100%; width: 100%; - border: 2px solid black; + border-top: 2px solid black; + border-bottom: 2px solid black; overflow: hidden; background-color: white; position: absolute; @@ -150,7 +194,7 @@ $timelineDark: #77a1aa; } .title-container { - margin-top: 110px; + margin-top: 80px; margin-left: 20px; height: calc(100% - 100px - 30px); width: 100px; @@ -159,6 +203,7 @@ $timelineDark: #77a1aa; border-left: 2px solid black; border-top: 2px solid black; border-bottom: 2px solid black; + border-right: 2px solid $timelineDark; .datapane { top: 0px; @@ -181,6 +226,7 @@ $timelineDark: #77a1aa; height: 20px; width: 40px; left: calc(50% - 25px); + color: $timelineDark; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 37d93f6c1..e6201d431 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -3,7 +3,7 @@ import "./Timeline.scss"; import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; -import { observable, action, computed, runInAction } from "mobx"; +import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -77,6 +77,11 @@ export class Timeline extends React.Component<FieldViewProps> { @observable private _doubleClickEnabled = false; @observable private _titleHeight = 0; + // so a reaction can be made + @observable public _isAuthoring = this.props.Document.isATOn; + @observable private _resizeReaction?: IReactionDisposer; + @observable private _panelWidth = 0; + /** * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. */ @@ -114,6 +119,19 @@ export class Timeline extends React.Component<FieldViewProps> { this.props.Document.isATOn = !this.props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); }); + + this._resizeReaction = reaction( + () => this.props.PanelWidth, + () => { + // if (!this.props.parent._isAuthoring) { + // runInAction(() => { + console.log("resizing"); + // this.setOverviewWidth(); + // }); + // } + }, + ); + } componentWillUnmount() { @@ -402,19 +420,58 @@ export class Timeline extends React.Component<FieldViewProps> { private timelineToolBox = (scale: number) => { let size = 40 * scale; //50 is default let iconSize = 25; + + //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 + let shouldCompress = false; + let width: number = this.props.PanelWidth(); + if (width < 850) { + shouldCompress = true; + } + + let modeString, overviewString, lengthString; + let modeType = this.props.Document.isATOn ? "Author" : "Play"; + + if (!shouldCompress) { + modeString = "Mode: " + modeType; + overviewString = "Overview:"; + lengthString = "Length: "; + } + else { + modeString = modeType; + overviewString = ""; + lengthString = ""; + } + + + // let modeType: string = this.props.Document.isATOn ? "Author" : "Play"; + // let modeString: string = "Mode: " + modeType; + // let overviewString: string = "Overview:"; + // let lengthString: string = "Length: "; + return ( <div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}> - <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward}> <FontAwesomeIcon icon={faBackward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> - <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay}> <FontAwesomeIcon icon={this._playButton} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> - <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward}> <FontAwesomeIcon icon={faForward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> - <div key="overview-text" className="animation-text">Timeline Overview</div> - <TimelineOverview isAuthoring={BoolCast(this.props.Document.isATOn)} currentBarX={this._currentBarX} totalLength={this._totalLength} visibleLength={this._visibleLength} visibleStart={this._visibleStart} changeCurrentBarX={this.changeCurrentBarX} movePanX={this.movePanX} /> - <div key="animation-text" className="animation-text">Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}</div> - <div key="round-toggle" ref={this._roundToggleContainerRef} className="round-toggle"> - <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}> </div> + <div className="playbackControls"> + <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + </div> + <div className="grid-box overview-tool"> + <div className="overview-box"> + <div key="overview-text" className="animation-text">{overviewString}</div> + <TimelineOverview parent={this} isAuthoring={BoolCast(this.props.Document.isATOn)} currentBarX={this._currentBarX} totalLength={this._totalLength} visibleLength={this._visibleLength} visibleStart={this._visibleStart} changeCurrentBarX={this.changeCurrentBarX} movePanX={this.movePanX} /> + </div> + <div className="mode-box overview-tool"> + <div key="animation-text" className="animation-text">{modeString}</div> + <div key="round-toggle" ref={this._roundToggleContainerRef} className="round-toggle"> + <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}> </div> + </div> + </div> + <div className="time-box overview-tool" style={{ display: this._timelineVisible ? "flex" : "none" }}> + <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden" }}>{lengthString}</div> + <input className="time-input" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden" }} placeholder={String(Math.floor(this._time) / 1000) + " s"} ref={this._timeInputRef} onKeyDown={this.onTimeInput} /> + </div> </div> - <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden" }}>Length: </div> - <input className="time-input" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden" }} placeholder={String(this._time) + "ms"} ref={this._timeInputRef} onKeyDown={this.onTimeInput} /> </div> ); } @@ -457,13 +514,15 @@ export class Timeline extends React.Component<FieldViewProps> { roundToggleContainer.style.backgroundColor = "white"; timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; + this._isAuthoring = false; } else { - roundToggle.style.transform = "translate(40px, 0px)"; + roundToggle.style.transform = "translate(20px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; roundToggleContainer.style.backgroundColor = "#9acedf"; timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; + this._isAuthoring = true; } } @@ -498,6 +557,11 @@ export class Timeline extends React.Component<FieldViewProps> { * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { + console.log(this.props.PanelWidth()); + runInAction(() => { + this._panelWidth = this.props.PanelWidth(); + console.log("changing!!") + }); return ( <div> <div style={{ visibility: this._timelineVisible ? "visible" : "hidden" }}> diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index 3517d3f39..a0b9d462b 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -1,23 +1,34 @@ @import "./../globalCssVariables.scss"; $timelineColor: #9acedf; +$timelineDark: #77a1aa; + +.timelineOverview-bounding { + width: 100%; + margin-right: 10px; +} .timeline-overview-container { - padding: 0px; - margin: 0px; - width: 300px; - height: 40px; + // padding: 0px; + margin-right: 5px; + // margin: 0px; + margin-left: 5px; + // width: 300px; + width: 100%; + height: 25px; background: white; position: relative; - border: 2px solid black; + border: 2px solid $timelineDark; + // width: 100%; .timeline-overview-visible { position: absolute; - height: 98%; + height: 21px; background: $timelineColor; display: inline-block; margin: 0px; padding: 0px; + // top: 1px; } .timeline-overview-scrubber-container { @@ -38,9 +49,9 @@ $timelineColor: #9acedf; position: absolute; height: 10px; width: 10px; - background-color: black; + background-color: white; border-radius: 50%; - // border: 3px solid black; + border: 2px solid black; left: -4px; // top: -30px; top: -10px; @@ -54,7 +65,7 @@ $timelineColor: #9acedf; position: relative; padding: 0px; margin: 0px; - width: 300px; + width: 100%; height: 4px; background-color: $timelineColor; border-radius: 20px; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 4741969dc..4907a1ae1 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -1,101 +1,159 @@ -import * as React from "react"; -import {observable, action} from "mobx"; -import {observer} from "mobx-react"; -import "./TimelineOverview.scss"; +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"; - -interface TimelineOverviewProps{ - totalLength: number; - visibleLength:number; - visibleStart:number; - currentBarX:number; - isAuthoring: boolean; - changeCurrentBarX: (pixel:number) => void; - movePanX: (pixel:number) => any; +interface TimelineOverviewProps { + totalLength: number; + visibleLength: number; + visibleStart: number; + currentBarX: number; + isAuthoring: boolean; + parent: Timeline; + changeCurrentBarX: (pixel: number) => void; + movePanX: (pixel: number) => any; } @observer export class TimelineOverview extends React.Component<TimelineOverviewProps>{ - @observable private _visibleRef = React.createRef<HTMLDivElement>(); - @observable private _scrubberRef = React.createRef<HTMLDivElement>(); - private readonly DEFAULT_HEIGHT = 50; - private readonly DEFAULT_WIDTH = 300; + @observable private _visibleRef = React.createRef<HTMLDivElement>(); + @observable private _scrubberRef = React.createRef<HTMLDivElement>(); + @observable private overviewBarWidth: number = 0; + @observable private _authoringReaction?: IReactionDisposer; + @observable private _resizeReaction?: IReactionDisposer; + private readonly DEFAULT_HEIGHT = 50; + private readonly DEFAULT_WIDTH = 300; + + componentDidMount = () => { + this.setOverviewWidth(); + + this._authoringReaction = reaction( + () => this.props.parent._isAuthoring, + () => { + if (!this.props.parent._isAuthoring) { + runInAction(() => { + this.setOverviewWidth(); + }); + } + }, + ); + // this._resizeReaction = reaction( + // () => this.props.parent.props.PanelWidth, + // () => { + // // if (!this.props.parent._isAuthoring) { + // // runInAction(() => { + // console.log("resizing"); + // this.setOverviewWidth(); + // // }); + // // } + // }, + // ); + } + + componentWillUnmount = () => { + this._authoringReaction && this._authoringReaction(); + this._resizeReaction && this._resizeReaction(); + } @action - 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); + setOverviewWidth() { + let width = $("#timelineOverview").width(); + if (width) this.overviewBarWidth = width; + else this.overviewBarWidth = 0; + } + + @action + 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); } @action onPanX = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH) + e.movementX; - this.props.movePanX((movX / (this.DEFAULT_WIDTH )) * this.props.totalLength); + e.stopPropagation(); + e.preventDefault(); + let movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; + // let movX = (this.props.visibleStart / this.props.totalLength) * (this.overviewWidth) + e.movementX; + this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); + // this.props.movePanX((movX / (this.overviewWidth) * this.props.totalLength); + + // console.log(this.props.totalLength); } @action onPointerUp = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onPanX); - document.removeEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + 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); + 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); } @action onScrubberMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let scrubberRef = this._scrubberRef.current!; - let left = scrubberRef.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); + e.preventDefault(); + e.stopPropagation(); + let scrubberRef = this._scrubberRef.current!; + let left = scrubberRef.getBoundingClientRect().left; + let offsetX = Math.round(e.clientX - left); + this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); } @action - onScrubberUp = (e:PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.removeEventListener("pointermove", this.onScrubberMove); + onScrubberUp = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointermove", this.onScrubberMove); document.removeEventListener("pointerup", this.onScrubberUp); } - render(){ + render() { + // calculates where everything should fall based on its size + let percentVisible = this.props.visibleLength / this.props.totalLength; + let visibleBarWidth = percentVisible * this.overviewBarWidth; + + let percentScrubberStart = this.props.currentBarX / this.props.totalLength; + let scrubberStart = percentScrubberStart * this.overviewBarWidth; + + let percentBarStart = this.props.visibleStart / this.props.totalLength; + let barStart = percentBarStart * this.overviewBarWidth; + let timeline = this.props.isAuthoring ? [ - <div key="timeline-overview-container" className="timeline-overview-container"> - <div ref={this._visibleRef} key="timeline-overview-visible" className="timeline-overview-visible" style={{left:`${(Math.round(this.props.visibleStart) / Math.round(this.props.totalLength)) * 296}px`, width:`${(Math.round(this.props.visibleLength) / Math.round(this.props.totalLength)) * 296}px`}} onPointerDown={this.onPointerDown}></div>, - <div ref={this._scrubberRef} key="timeline-overview-scrubber-container" className="timeline-overview-scrubber-container" style={{left:`${(this.props.currentBarX / this.props.totalLength) * 294}px`}} onPointerDown={this.onScrubberDown}> + + <div key="timeline-overview-container" className="timeline-overview-container" id="timelineOverview"> + <div ref={this._visibleRef} key="timeline-overview-visible" className="timeline-overview-visible" style={{ left: `${barStart}px`, width: `${visibleBarWidth}px` }} onPointerDown={this.onPointerDown}></div>, + <div ref={this._scrubberRef} key="timeline-overview-scrubber-container" 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> ] : [ - <div className="timeline-play-bar"> - <div ref={this._scrubberRef} className="timeline-play-head" style={{left:`${(this.props.currentBarX / this.props.totalLength) * 294}px`}} onPointerDown={this.onScrubberDown}></div> - </div>, - <div className="timeline-play-tail" style={{width: `${(this.props.currentBarX / this.props.totalLength) * 294}px`}}></div> - ]; - return( - <div> + <div className="timeline-play-bar"> + <div ref={this._scrubberRef} className="timeline-play-head" style={{ left: `${(this.props.currentBarX / this.props.totalLength) * 294}px` }} onPointerDown={this.onScrubberDown}></div> + </div>, + <div className="timeline-play-tail" style={{ width: `${(this.props.currentBarX / this.props.totalLength) * 294}px` }}></div> + ]; + return ( + <div className="timelineOverview-bounding"> {timeline} </div> - ); + ); } } |