فهرست منبع

set currentFrame

Alejandro Toledo 5 سال پیش
والد
کامیت
1eca0e79fb

+ 106 - 93
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -7,6 +7,7 @@ import { EasingFunction, BezierCurveEase } from 'babylonjs/Animations/easing';
 import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 import { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { SvgDraggableArea } from './svgDraggableArea';
+import { Timeline } from './timeline';
 import { Scene } from "babylonjs/scene";
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
 
@@ -22,7 +23,7 @@ interface IAnimationCurveEditorComponentProps {
     entity: IAnimatable;
 }
 
-export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined }> {
+export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined, currentFrame: number }> {
 
     readonly _heightScale: number = 100;
     private _newAnimations: Animation[] = [];
@@ -31,7 +32,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     private _isPlaying: boolean = false;
     constructor(props: IAnimationCurveEditorComponentProps) {
         super(props);
-        this.state = { animations: this._newAnimations, selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, animationTargetProperty: 'position.x', animationName: "" }
+        this.state = { animations: this._newAnimations, selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, animationTargetProperty: 'position.x', animationName: "", currentFrame: 0 }
 
     }
 
@@ -340,12 +341,12 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     }
 
     isAnimationPlaying() {
-          this._isPlaying = this.props.scene.getAllAnimatablesByTarget(this.props.entity).length > 0;
-           if (this._isPlaying){
+        this._isPlaying = this.props.scene.getAllAnimatablesByTarget(this.props.entity).length > 0;
+        if (this._isPlaying) {
             this.props.playOrPause();
-           } else {
-               this._isPlaying = false;
-           }
+        } else {
+            this._isPlaying = false;
+        }
     }
 
     selectAnimation(animation: Animation) {
@@ -404,6 +405,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     }
 
+    changeCurrentFrame(frame: number){
+        this.setState({ currentFrame: frame });
+    }
+
     render() {
         return (
             <div id="animation-curve-editor">
@@ -415,104 +420,112 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 </div>
                 <div className="content">
 
-                    <div className="animation-list">
+                    <div className="row">
+                        <div className="animation-list">
 
-                        <div>
                             <div>
-                                <label>Animation Name</label>
-                                <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
-                            </div>
-                            <div>
-                                <label>Target Property</label>
-                                <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
-                            </div>
-                            <div className="add" onClick={(e) => this.addAnimation(e)}>
-                                <FontAwesomeIcon icon={faPlusCircle} />
+                                <div>
+                                    <label>Animation Name</label>
+                                    <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
+                                </div>
+                                <div>
+                                    <label>Target Property</label>
+                                    <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
+                                </div>
+                                <div className="add" onClick={(e) => this.addAnimation(e)}>
+                                    <FontAwesomeIcon icon={faPlusCircle} />
+                                </div>
                             </div>
-                        </div>
 
-                        <h2>{this.props.entityName}</h2>
-                        <ul>
-                            {this.props.animations && this.props.animations.map((animation, i) => {
-                                return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
-                            })}
+                            <h2>{this.props.entityName}</h2>
+                            <ul>
+                                {this.props.animations && this.props.animations.map((animation, i) => {
+                                    return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
+                                })}
 
-                        </ul>
+                            </ul>
 
-                        <h2>New Animations</h2>
-                        <ul>
-                            {this.state.animations && this.state.animations.map((animation, i) => {
-                                return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
-                            })}
+                            <h2>New Animations</h2>
+                            <ul>
+                                {this.state.animations && this.state.animations.map((animation, i) => {
+                                    return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
+                                })}
 
-                        </ul>
-                    </div>
-                    <div className="graph-chart">
-
-                        {this.state.svgKeyframes && <SvgDraggableArea keyframeSvgPoints={this.state.svgKeyframes} updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
-
-                            {/* Frame Labels  */}
-                            <text x="10" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>10</text>
-                            <text x="20" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>20</text>
-                            <text x="30" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>30</text>
-                            <text x="40" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>40</text>
-                            <text x="50" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>50</text>
-                            <text x="60" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>60</text>
-                            <text x="70" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>70</text>
-                            <text x="80" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>80</text>
-                            <text x="90" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>90</text>
-
-                            { /* Vertical Grid  */}
-                            <line x1="10" y1="0" x2="10" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="20" y1="0" x2="20" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="30" y1="0" x2="30" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="40" y1="0" x2="40" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="50" y1="0" x2="50" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="60" y1="0" x2="60" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="70" y1="0" x2="70" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="80" y1="0" x2="80" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="90" y1="0" x2="90" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-
-                            { /* Value Labels  */}
-                            <text x="0" y="10" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.8</text>
-                            <text x="0" y="20" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.6</text>
-                            <text x="0" y="30" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.4</text>
-                            <text x="0" y="40" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.2</text>
-                            <text x="0" y="50" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1</text>
-                            <text x="0" y="60" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.8</text>
-                            <text x="0" y="70" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.6</text>
-                            <text x="0" y="80" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.4</text>
-                            <text x="0" y="90" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.2</text>
-
-                            { /* Horizontal Grid  */}
-                            <line x1="0" y1="10" x2="100" y2="10" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="20" x2="100" y2="20" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="30" x2="100" y2="30" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="40" x2="100" y2="40" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="50" x2="100" y2="50" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="60" x2="100" y2="60" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="70" x2="100" y2="70" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="80" x2="100" y2="80" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="90" x2="100" y2="90" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-
-                            { /* Single Curve -Modify this for multiple selection and view  */}
-                            <path id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
-
-                            {this._frames && this._frames.map(frame =>
-                                <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
-                                    <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
-                                </svg>
-
-                            )}
-
-                        </SvgDraggableArea>
-
-                        }
+                            </ul>
+                        </div>
+                        <div className="graph-chart">
+
+                            {this.state.svgKeyframes && <SvgDraggableArea keyframeSvgPoints={this.state.svgKeyframes} updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
+
+                                {/* Frame Labels  */}
+                                <text x="10" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>10</text>
+                                <text x="20" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>20</text>
+                                <text x="30" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>30</text>
+                                <text x="40" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>40</text>
+                                <text x="50" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>50</text>
+                                <text x="60" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>60</text>
+                                <text x="70" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>70</text>
+                                <text x="80" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>80</text>
+                                <text x="90" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>90</text>
+
+                                { /* Vertical Grid  */}
+                                <line x1="10" y1="0" x2="10" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="20" y1="0" x2="20" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="30" y1="0" x2="30" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="40" y1="0" x2="40" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="50" y1="0" x2="50" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="60" y1="0" x2="60" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="70" y1="0" x2="70" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="80" y1="0" x2="80" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="90" y1="0" x2="90" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+
+                                { /* Value Labels  */}
+                                <text x="0" y="10" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.8</text>
+                                <text x="0" y="20" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.6</text>
+                                <text x="0" y="30" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.4</text>
+                                <text x="0" y="40" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.2</text>
+                                <text x="0" y="50" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1</text>
+                                <text x="0" y="60" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.8</text>
+                                <text x="0" y="70" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.6</text>
+                                <text x="0" y="80" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.4</text>
+                                <text x="0" y="90" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.2</text>
+
+                                { /* Horizontal Grid  */}
+                                <line x1="0" y1="10" x2="100" y2="10" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="20" x2="100" y2="20" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="30" x2="100" y2="30" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="40" x2="100" y2="40" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="50" x2="100" y2="50" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="60" x2="100" y2="60" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="70" x2="100" y2="70" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="80" x2="100" y2="80" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+                                <line x1="0" y1="90" x2="100" y2="90" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
+
+                                { /* Single Curve -Modify this for multiple selection and view  */}
+                                <path id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
+
+                                {this._frames && this._frames.map(frame =>
+                                    <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
+                                        <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
+                                    </svg>
+
+                                )}
+
+                            </SvgDraggableArea>
+
+                            }
 
                         Animation name: {this.state.selected.name}
 
+                        </div>
+                       
+                    </div>
+                    <div className="row">
+                            <Timeline currentFrame={this.state.currentFrame} onCurrentFrameChange={(frame: number) => this.changeCurrentFrame(frame)} keyframes={this.state.selected.getKeys()} selected={this.state.selected.getKeys()[0]}></Timeline>
                     </div>
                 </div>
+
+
             </div>
         );
     }

+ 58 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss

@@ -26,11 +26,68 @@
         display: flex;
         align-items: flex-start;
         justify-content: flex-start;
+        flex-direction: column;
+
+        .row {
+            display: flex;
+            align-items: stretch;
+            justify-content: flex-start;
+            flex-direction: row;
+            width: 100vw;
+
+            .timeline{
+                width: 100vw;
+                background: gray;
+                display: flex;
+                align-items: center;
+                justify-content: stretch;
+                height: 2.5rem;
+
+                .display-line {
+                    width: 80vw;
+                    height: 2em;
+                    overflow: hidden;
+                    overflow-x: scroll;
+                    scrollbar-color: cornflowerblue slategrey;
+                    scrollbar-width: thin;
+                    margin-right: 2em;
+
+                    &::-webkit-scrollbar{
+                        height: 0.4em;
+                    }
+
+                    &::-webkit-scrollbar-track {
+                        box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+                      }
+                       
+                    &::-webkit-scrollbar-thumb {
+                        background-color: cornflowerblue;
+                        outline: 1px solid slategrey;
+                      }
+                }
+
+                .controls {
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    width: 8em;
+
+                    .input-frame input {
+                        width: 3em;
+                    }
+
+                    .button {
+                        margin-left: 0.5em;
+                        margin-right: 0.5em;
+                    }
+                }
+            }
+        }
 
         .animation-list{
             padding: 1.5rem;
             background: lightgrey;
-            height: 100vh;
+            // height: 100vh;
             ul {
                 list-style:none;
                 padding-left: 0px;

+ 53 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx

@@ -7,61 +7,103 @@ import { faCaretRight, faCaretLeft, faStepBackward, faStepForward } from "@forta
 interface ITimelineProps {
    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({});
     constructor(props: ITimelineProps) {
         super(props);
         this.state = { selected: this.props.selected };
     }
 
     handleInputChange(event: React.ChangeEvent<HTMLInputElement>){
+        this.props.onCurrentFrameChange(parseInt(event.target.value));
         event.preventDefault();
     }
 
     nextFrame(event: React.MouseEvent<HTMLDivElement>){
         event.preventDefault();
+        this.props.onCurrentFrameChange(this.props.currentFrame + 1)
 
     }
 
     previousFrame(event: React.MouseEvent<HTMLDivElement>){
         event.preventDefault();
+        if (this.props.currentFrame !== 0) {
+        this.props.onCurrentFrameChange(this.props.currentFrame - 1)
+        }
     }
 
     nextKeyframe(event: React.MouseEvent<HTMLDivElement>){
         event.preventDefault();
+        // implement jumpTo keyframe
+        this.props.onCurrentFrameChange(this.state.selected.frame);
     }
 
     previousKeyframe(event: React.MouseEvent<HTMLDivElement>){
         event.preventDefault();
+         // implement jumpTo keyframe
+        this.props.onCurrentFrameChange(this.state.selected.frame);
+    }
+
+    scrollToFrame() {
+        // scroll to current frame
+    }
+
+    selectKeyframe(frame: number){
+        let selected = this.props.keyframes[frame];
+        this.setState({ selected: selected });
     }
     
     render() {
         return (
         <>
-            
-           
            <div className="timeline">
                <div className="display-line">
-                   <svg>
+                   <svg viewBox="0 0 2000 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="input-frame">
-                    <input type="text" value={this.state.selected.frame} onChange={() => this.handleInputChange}></input>
-                </div>
                 <div className="controls">
-                     <div className="previous-frame" onClick={(e) => this.previousFrame(e)}>
+                    <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" onClick={(e) => this.previousKeyframe(e)}>
+                     <div className="previous-key-frame button" onClick={(e) => this.previousKeyframe(e)}>
                         <FontAwesomeIcon icon={faStepBackward} />
                      </div>
-                     <div className="next-key-frame" onClick={(e) => this.nextKeyframe(e)}>
+                     <div className="next-key-frame button" onClick={(e) => this.nextKeyframe(e)}>
                         <FontAwesomeIcon icon={faStepForward} />
                      </div>
-                     <div className="next-frame" onClick={(e) => this.nextFrame(e)}>
+                     <div className="next-frame button" onClick={(e) => this.nextFrame(e)}>
                         <FontAwesomeIcon icon={faCaretRight} />
                      </div>
                 </div>