timeline.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import * as React from "react";
  2. import { IAnimationKey } from 'babylonjs/Animations/animationKey';
  3. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  4. import { faCaretRight, faCaretLeft, faStepBackward, faStepForward } from "@fortawesome/free-solid-svg-icons";
  5. interface ITimelineProps {
  6. keyframes: IAnimationKey[] | null;
  7. selected: IAnimationKey | null;
  8. currentFrame: number;
  9. onCurrentFrameChange: (frame: number) => void;
  10. }
  11. export class Timeline extends React.Component<ITimelineProps, { selected: IAnimationKey }>{
  12. readonly _frames: object[] = Array(300).fill({});
  13. private _scrollable: React.RefObject<HTMLDivElement>;
  14. constructor(props: ITimelineProps) {
  15. super(props);
  16. if (this.props.selected !== null){
  17. this.state = { selected: this.props.selected };
  18. }
  19. this._scrollable = React.createRef();
  20. }
  21. handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
  22. this.props.onCurrentFrameChange(parseInt(event.target.value));
  23. event.preventDefault();
  24. }
  25. nextFrame(event: React.MouseEvent<HTMLDivElement>) {
  26. event.preventDefault();
  27. this.props.onCurrentFrameChange(this.props.currentFrame + 1);
  28. (this._scrollable.current as HTMLDivElement).scrollLeft = this.props.currentFrame * 5;
  29. }
  30. previousFrame(event: React.MouseEvent<HTMLDivElement>) {
  31. event.preventDefault();
  32. if (this.props.currentFrame !== 0) {
  33. this.props.onCurrentFrameChange(this.props.currentFrame - 1);
  34. (this._scrollable.current as HTMLDivElement).scrollLeft = -(this.props.currentFrame * 5);
  35. }
  36. }
  37. nextKeyframe(event: React.MouseEvent<HTMLDivElement>) {
  38. event.preventDefault();
  39. if (this.props.keyframes !== null){
  40. let first = this.props.keyframes.find(kf => kf.frame > this.props.currentFrame);
  41. if (first) {
  42. this.props.onCurrentFrameChange(first.frame);
  43. this.setState({ selected: first });
  44. (this._scrollable.current as HTMLDivElement).scrollLeft = first.frame * 5;
  45. }
  46. }
  47. }
  48. previousKeyframe(event: React.MouseEvent<HTMLDivElement>) {
  49. event.preventDefault();
  50. if (this.props.keyframes !== null){
  51. let first = this.props.keyframes.find(kf => kf.frame < this.props.currentFrame);
  52. if (first) {
  53. this.props.onCurrentFrameChange(first.frame);
  54. this.setState({ selected: first });
  55. (this._scrollable.current as HTMLDivElement).scrollLeft = -(first.frame * 5);
  56. }
  57. }
  58. }
  59. render() {
  60. return (
  61. <>
  62. <div className="timeline">
  63. <div ref={this._scrollable} className="display-line">
  64. <svg viewBox="0 0 2010 100" style={{ width: 2000 }}>
  65. <line x1={this.props.currentFrame * 10} y1="10" x2={this.props.currentFrame * 10} y2="20" style={{ stroke: '#12506b', strokeWidth: 6 }} />
  66. {
  67. this.props.keyframes && this.props.keyframes.map((kf, i) => {
  68. return <svg key={`kf_${i}`}>
  69. <line x1={kf.frame * 10} y1="10" x2={kf.frame * 10} y2="20" style={{ stroke: 'red', strokeWidth: 6 }} />
  70. </svg>
  71. })
  72. }
  73. {
  74. this._frames.map((frame, i) => {
  75. return <svg key={`tl_${i}`}>
  76. {i % 10 === 0 ? <text x={(i * 10) - 3} y="8" style={{ fontSize: 10 }}>{i}</text> : null}
  77. <line x1={i * 10} y1="10" x2={i * 10} y2="20" style={{ stroke: 'black', strokeWidth: 0.5 }} />
  78. </svg>
  79. })
  80. }
  81. </svg>
  82. </div>
  83. <div className="controls">
  84. <div className="input-frame">
  85. <input type="number" value={this.props.currentFrame} onChange={(e) => this.handleInputChange(e)}></input>
  86. </div>
  87. <div className="previous-frame button" onClick={(e) => this.previousFrame(e)}>
  88. <FontAwesomeIcon icon={faCaretLeft} />
  89. </div>
  90. <div className="previous-key-frame button" onClick={(e) => this.previousKeyframe(e)}>
  91. <FontAwesomeIcon icon={faStepBackward} />
  92. </div>
  93. <div className="next-key-frame button" onClick={(e) => this.nextKeyframe(e)}>
  94. <FontAwesomeIcon icon={faStepForward} />
  95. </div>
  96. <div className="next-frame button" onClick={(e) => this.nextFrame(e)}>
  97. <FontAwesomeIcon icon={faCaretRight} />
  98. </div>
  99. </div>
  100. </div>
  101. </>
  102. )
  103. }
  104. }