|
@@ -5,14 +5,14 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faCaretRight, faCaretLeft, faStepBackward, faStepForward } from "@fortawesome/free-solid-svg-icons";
|
|
import { faCaretRight, faCaretLeft, faStepBackward, faStepForward } from "@fortawesome/free-solid-svg-icons";
|
|
|
|
|
|
interface ITimelineProps {
|
|
interface ITimelineProps {
|
|
- keyframes: IAnimationKey[];
|
|
|
|
- selected: IAnimationKey;
|
|
|
|
- currentFrame: number;
|
|
|
|
- onCurrentFrameChange: (frame: number) => void;
|
|
|
|
|
|
+ keyframes: IAnimationKey[];
|
|
|
|
+ selected: IAnimationKey;
|
|
|
|
+ currentFrame: number;
|
|
|
|
+ onCurrentFrameChange: (frame: number) => void;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-export class Timeline extends React.Component<ITimelineProps, {selected: IAnimationKey }>{
|
|
|
|
|
|
+export class Timeline extends React.Component<ITimelineProps, { selected: IAnimationKey }>{
|
|
readonly _frames: object[] = Array(300).fill({});
|
|
readonly _frames: object[] = Array(300).fill({});
|
|
private _scrollable: React.RefObject<HTMLDivElement>;
|
|
private _scrollable: React.RefObject<HTMLDivElement>;
|
|
constructor(props: ITimelineProps) {
|
|
constructor(props: ITimelineProps) {
|
|
@@ -21,27 +21,27 @@ export class Timeline extends React.Component<ITimelineProps, {selected: IAnimat
|
|
this._scrollable = React.createRef();
|
|
this._scrollable = React.createRef();
|
|
}
|
|
}
|
|
|
|
|
|
- handleInputChange(event: React.ChangeEvent<HTMLInputElement>){
|
|
|
|
|
|
+ handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
this.props.onCurrentFrameChange(parseInt(event.target.value));
|
|
this.props.onCurrentFrameChange(parseInt(event.target.value));
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
|
|
- nextFrame(event: React.MouseEvent<HTMLDivElement>){
|
|
|
|
|
|
+ nextFrame(event: React.MouseEvent<HTMLDivElement>) {
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
this.props.onCurrentFrameChange(this.props.currentFrame + 1);
|
|
this.props.onCurrentFrameChange(this.props.currentFrame + 1);
|
|
(this._scrollable.current as HTMLDivElement).scrollLeft = this.props.currentFrame * 5;
|
|
(this._scrollable.current as HTMLDivElement).scrollLeft = this.props.currentFrame * 5;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- previousFrame(event: React.MouseEvent<HTMLDivElement>){
|
|
|
|
|
|
+ previousFrame(event: React.MouseEvent<HTMLDivElement>) {
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
if (this.props.currentFrame !== 0) {
|
|
if (this.props.currentFrame !== 0) {
|
|
- this.props.onCurrentFrameChange(this.props.currentFrame - 1);
|
|
|
|
- (this._scrollable.current as HTMLDivElement).scrollLeft = -(this.props.currentFrame * 5);
|
|
|
|
|
|
+ this.props.onCurrentFrameChange(this.props.currentFrame - 1);
|
|
|
|
+ (this._scrollable.current as HTMLDivElement).scrollLeft = -(this.props.currentFrame * 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- nextKeyframe(event: React.MouseEvent<HTMLDivElement>){
|
|
|
|
|
|
+ nextKeyframe(event: React.MouseEvent<HTMLDivElement>) {
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
let first = this.props.keyframes.find(kf => kf.frame > this.props.currentFrame);
|
|
let first = this.props.keyframes.find(kf => kf.frame > this.props.currentFrame);
|
|
if (first) {
|
|
if (first) {
|
|
@@ -51,7 +51,7 @@ export class Timeline extends React.Component<ITimelineProps, {selected: IAnimat
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- previousKeyframe(event: React.MouseEvent<HTMLDivElement>){
|
|
|
|
|
|
+ previousKeyframe(event: React.MouseEvent<HTMLDivElement>) {
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
let first = this.props.keyframes.find(kf => kf.frame < this.props.currentFrame);
|
|
let first = this.props.keyframes.find(kf => kf.frame < this.props.currentFrame);
|
|
if (first) {
|
|
if (first) {
|
|
@@ -65,58 +65,58 @@ export class Timeline extends React.Component<ITimelineProps, {selected: IAnimat
|
|
// scroll to current frame
|
|
// scroll to current frame
|
|
(this._scrollable.current as HTMLDivElement).scrollLeft = frame * 10;
|
|
(this._scrollable.current as HTMLDivElement).scrollLeft = frame * 10;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
render() {
|
|
render() {
|
|
return (
|
|
return (
|
|
- <>
|
|
|
|
- <div className="timeline">
|
|
|
|
- <div ref={this._scrollable} className="display-line">
|
|
|
|
- <svg viewBox="0 0 2010 100" style={{width: 2000}}>
|
|
|
|
-
|
|
|
|
- <line x1={this.props.currentFrame*10} y1="10" x2={this.props.currentFrame*10} y2="20" style={{stroke: '#12506b',strokeWidth:6}} />
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- this.props.keyframes.map((kf, i) => {
|
|
|
|
-
|
|
|
|
- return <svg key={`kf_${i}`}>
|
|
|
|
- <line x1={kf.frame*10} y1="10" x2={kf.frame*10} y2="20" style={{stroke: 'red',strokeWidth:6}} />
|
|
|
|
- </svg>
|
|
|
|
-
|
|
|
|
- })
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- this._frames.map((frame, i) => {
|
|
|
|
-
|
|
|
|
- return <svg key={`tl_${i}`}>
|
|
|
|
- { i % 10 === 0 ? <text x={(i*10) - 3} y="8" style={{fontSize:10}}>{i}</text> : null }
|
|
|
|
- <line x1={i*10} y1="10" x2={i*10} y2="20" style={{stroke: 'black',strokeWidth:0.5}} />
|
|
|
|
- </svg>
|
|
|
|
-
|
|
|
|
- })
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- </svg>
|
|
|
|
- </div>
|
|
|
|
- <div className="controls">
|
|
|
|
- <div className="input-frame">
|
|
|
|
- <input type="number" value={this.props.currentFrame} onChange={(e) => this.handleInputChange(e)}></input>
|
|
|
|
|
|
+ <>
|
|
|
|
+ <div className="timeline">
|
|
|
|
+ <div ref={this._scrollable} className="display-line">
|
|
|
|
+ <svg viewBox="0 0 2010 100" style={{ width: 2000 }}>
|
|
|
|
+
|
|
|
|
+ <line x1={this.props.currentFrame * 10} y1="10" x2={this.props.currentFrame * 10} y2="20" style={{ stroke: '#12506b', strokeWidth: 6 }} />
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ this.props.keyframes.map((kf, i) => {
|
|
|
|
+
|
|
|
|
+ return <svg key={`kf_${i}`}>
|
|
|
|
+ <line x1={kf.frame * 10} y1="10" x2={kf.frame * 10} y2="20" style={{ stroke: 'red', strokeWidth: 6 }} />
|
|
|
|
+ </svg>
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ this._frames.map((frame, i) => {
|
|
|
|
+
|
|
|
|
+ return <svg key={`tl_${i}`}>
|
|
|
|
+ {i % 10 === 0 ? <text x={(i * 10) - 3} y="8" style={{ fontSize: 10 }}>{i}</text> : null}
|
|
|
|
+ <line x1={i * 10} y1="10" x2={i * 10} y2="20" style={{ stroke: 'black', strokeWidth: 0.5 }} />
|
|
|
|
+ </svg>
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ </svg>
|
|
|
|
+ </div>
|
|
|
|
+ <div className="controls">
|
|
|
|
+ <div className="input-frame">
|
|
|
|
+ <input type="number" value={this.props.currentFrame} onChange={(e) => this.handleInputChange(e)}></input>
|
|
|
|
+ </div>
|
|
|
|
+ <div className="previous-frame button" onClick={(e) => this.previousFrame(e)}>
|
|
|
|
+ <FontAwesomeIcon icon={faCaretLeft} />
|
|
|
|
+ </div>
|
|
|
|
+ <div className="previous-key-frame button" onClick={(e) => this.previousKeyframe(e)}>
|
|
|
|
+ <FontAwesomeIcon icon={faStepBackward} />
|
|
|
|
+ </div>
|
|
|
|
+ <div className="next-key-frame button" onClick={(e) => this.nextKeyframe(e)}>
|
|
|
|
+ <FontAwesomeIcon icon={faStepForward} />
|
|
|
|
+ </div>
|
|
|
|
+ <div className="next-frame button" onClick={(e) => this.nextFrame(e)}>
|
|
|
|
+ <FontAwesomeIcon icon={faCaretRight} />
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- <div className="previous-frame button" onClick={(e) => this.previousFrame(e)}>
|
|
|
|
- <FontAwesomeIcon icon={faCaretLeft} />
|
|
|
|
- </div>
|
|
|
|
- <div className="previous-key-frame button" onClick={(e) => this.previousKeyframe(e)}>
|
|
|
|
- <FontAwesomeIcon icon={faStepBackward} />
|
|
|
|
- </div>
|
|
|
|
- <div className="next-key-frame button" onClick={(e) => this.nextKeyframe(e)}>
|
|
|
|
- <FontAwesomeIcon icon={faStepForward} />
|
|
|
|
- </div>
|
|
|
|
- <div className="next-frame button" onClick={(e) => this.nextFrame(e)}>
|
|
|
|
- <FontAwesomeIcon icon={faCaretRight} />
|
|
|
|
- </div>
|
|
|
|
</div>
|
|
</div>
|
|
- </div>
|
|
|
|
- </>
|
|
|
|
|
|
+ </>
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|