svgDraggableArea.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import * as React from "react";
  2. import { Vector2 } from 'babylonjs/Maths/math.vector';
  3. import { KeyframeSvgPoint, IKeyframeSvgPoint } from './keyframeSvgPoint';
  4. interface ISvgDraggableAreaProps {
  5. keyframeSvgPoints: IKeyframeSvgPoint[];
  6. updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void;
  7. }
  8. export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
  9. private _active: boolean;
  10. private _isCurrentPointControl: string;
  11. private _currentPointIndex: number;
  12. private _draggableArea: React.RefObject<SVGSVGElement>;
  13. private _panStart: Vector2;
  14. private _panStop: Vector2;
  15. constructor(props: ISvgDraggableAreaProps) {
  16. super(props);
  17. this._currentPointIndex = -1;
  18. this._isCurrentPointControl = "";
  19. this._draggableArea = React.createRef();
  20. this._panStart = new Vector2(0, 0);
  21. this._panStop = new Vector2(0, 0);
  22. }
  23. componentDidMount() {
  24. this._draggableArea.current?.addEventListener("keydown", this.keyDown.bind(this));
  25. this._draggableArea.current?.addEventListener("keyup", this.keyUp.bind(this));
  26. }
  27. dragStart(e: React.TouchEvent<SVGSVGElement>): void;
  28. dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
  29. dragStart(e: any): void {
  30. e.preventDefault();
  31. if (e.target.classList.contains("draggable")) {
  32. this._active = true;
  33. this._currentPointIndex = parseInt(e.target.getAttribute('data-id'));
  34. if (e.target.classList.contains("control-point")) {
  35. this._isCurrentPointControl = e.target.getAttribute("type");
  36. }
  37. }
  38. if (e.target.classList.contains("pannable")) {
  39. if (e.buttons === 1 && e.ctrlKey) {
  40. this._panStart.set(e.clientX, e.clientY);
  41. }
  42. }
  43. }
  44. drag(e: React.TouchEvent<SVGSVGElement>): void;
  45. drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
  46. drag(e: any): void {
  47. if (this._active) {
  48. e.preventDefault();
  49. var coord = this.getMousePosition(e);
  50. if (coord !== undefined) {
  51. var newPoints = [...this.props.keyframeSvgPoints];
  52. if (this._isCurrentPointControl === "left") {
  53. newPoints[this._currentPointIndex].leftControlPoint = coord;
  54. } else if (this._isCurrentPointControl === "right") {
  55. newPoints[this._currentPointIndex].rightControlPoint = coord;
  56. } else {
  57. newPoints[this._currentPointIndex].keyframePoint = coord;
  58. }
  59. this.props.updatePosition(newPoints[this._currentPointIndex], this._currentPointIndex);
  60. }
  61. }
  62. }
  63. dragEnd(e: React.TouchEvent<SVGSVGElement>): void;
  64. dragEnd(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
  65. dragEnd(e: any): void {
  66. e.preventDefault();
  67. this._active = false;
  68. this._currentPointIndex = -1;
  69. this._isCurrentPointControl = "";
  70. if (e.target.classList.contains("pannable")) {
  71. if (this._panStart.x !== 0 && this._panStart.y !== 0) {
  72. this._panStop.set(e.clientX, e.clientY);
  73. this.panDirection();
  74. }
  75. }
  76. }
  77. getMousePosition(e: React.TouchEvent<SVGSVGElement>): Vector2 | undefined;
  78. getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>): Vector2 | undefined;
  79. getMousePosition(e: any): Vector2 | undefined {
  80. if (e.touches) { e = e.touches[0]; }
  81. if (this._draggableArea.current) {
  82. var svg = this._draggableArea.current as SVGSVGElement;
  83. var CTM = svg.getScreenCTM();
  84. if (CTM) {
  85. return new Vector2((e.clientX - CTM.e) / CTM.a, (e.clientY - CTM.f) / CTM.d);
  86. } else {
  87. return undefined;
  88. }
  89. } else {
  90. return undefined;
  91. }
  92. }
  93. panDirection() {
  94. // Movement Right to Left
  95. if (this._panStart.x > this._panStop.x) {
  96. console.log("right to left");
  97. this.panTo("right", Math.abs(this._panStart.x - this._panStop.x));
  98. }
  99. // Movement Right to Left
  100. if (this._panStart.x < this._panStop.x) {
  101. this.panTo("left", Math.abs(this._panStart.x - this._panStop.x));
  102. console.log("left to right");
  103. }
  104. // Movement Bottom to Up
  105. if (this._panStart.y > this._panStop.y) {
  106. console.log("down up");
  107. }
  108. // Movement Up to Bottom
  109. if (this._panStart.y < this._panStop.y) {
  110. console.log("up down");
  111. }
  112. this._panStart.set(0, 0);
  113. this._panStop.set(0, 0);
  114. }
  115. panTo(direction: string, value: number) {
  116. switch (direction) {
  117. case "left":
  118. (this._draggableArea.current?.parentElement as HTMLDivElement).scrollLeft -= (value * 1);
  119. break;
  120. case "right":
  121. (this._draggableArea.current?.parentElement as HTMLDivElement).scrollLeft += (value * 1);
  122. break;
  123. case "top":
  124. break;
  125. case "down":
  126. break;
  127. }
  128. }
  129. keyDown(e: KeyboardEvent) {
  130. e.preventDefault();
  131. if (e.keyCode === 17) {
  132. this._draggableArea.current?.style.setProperty("cursor", "grab");
  133. }
  134. }
  135. keyUp(e: KeyboardEvent) {
  136. e.preventDefault();
  137. if (e.keyCode === 17) {
  138. this._draggableArea.current?.style.setProperty("cursor", "initial");
  139. }
  140. }
  141. focus(e: React.MouseEvent<SVGSVGElement>) {
  142. e.preventDefault();
  143. this._draggableArea.current?.focus();
  144. }
  145. render() {
  146. return (
  147. <>
  148. <svg className="linear pannable" ref={this._draggableArea} tabIndex={0}
  149. onMouseMove={(e) => this.drag(e)}
  150. onTouchMove={(e) => this.drag(e)}
  151. onTouchStart={(e) => this.dragStart(e)}
  152. onTouchEnd={(e) => this.dragEnd(e)}
  153. onMouseDown={(e) => this.dragStart(e)}
  154. onMouseUp={(e) => this.dragEnd(e)}
  155. onMouseLeave={(e) => this.dragEnd(e)}
  156. // Add way to add new keyframe
  157. onClick={(e) => this.focus(e)}
  158. viewBox="0 0 200 100">
  159. {this.props.children}
  160. {this.props.keyframeSvgPoints.map((keyframe, i) =>
  161. <KeyframeSvgPoint key={i} id={i.toString()} keyframePoint={keyframe.keyframePoint} leftControlPoint={keyframe.leftControlPoint} rightControlPoint={keyframe.rightControlPoint} />
  162. )}
  163. </svg>
  164. </>)
  165. }
  166. }