|
@@ -7,6 +7,8 @@ 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 { Playhead } from './playhead';
|
|
|
import { Scene } from "babylonjs/scene";
|
|
|
import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
|
|
|
|
|
@@ -22,7 +24,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 +33,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 +342,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 +406,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
|
|
|
|
}
|
|
|
|
|
|
+ changeCurrentFrame(frame: number){
|
|
|
+ this.setState({ currentFrame: frame });
|
|
|
+ }
|
|
|
+
|
|
|
render() {
|
|
|
return (
|
|
|
<div id="animation-curve-editor">
|
|
@@ -415,104 +421,114 @@ 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">
|
|
|
+
|
|
|
+ <Playhead frame={this.state.currentFrame}/>
|
|
|
+
|
|
|
+ {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>
|
|
|
);
|
|
|
}
|