Aucune description

index.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { WhitePage } from './../../board/WhitePage/index';
  2. import { PositionType } from 'fc-whiteboard/src/event/Event';
  3. import { onSyncFunc, EventType } from '../../event/Event';
  4. import { MarkerType } from '../types';
  5. import * as uuid from 'uuid/v1';
  6. import { SvgHelper } from '../../renderer/SvgHelper';
  7. export class BaseMarker {
  8. id: string = uuid();
  9. type: MarkerType = 'base';
  10. // 归属的
  11. page?: WhitePage;
  12. // Marker 的属性发生变化后的回调
  13. onChange: onSyncFunc = () => {};
  14. public static createMarker = (page?: WhitePage): BaseMarker => {
  15. const marker = new BaseMarker();
  16. marker.page = page;
  17. marker.setup();
  18. return marker;
  19. };
  20. public visual: SVGGElement;
  21. public renderVisual: SVGGElement;
  22. public onSelected: (marker: BaseMarker) => void;
  23. public defs: SVGElement[] = [];
  24. protected width: number = 200;
  25. protected height: number = 50;
  26. protected isActive: boolean = true;
  27. protected isDragging: boolean = false;
  28. protected isResizing: boolean = false;
  29. protected previousMouseX: number = 0;
  30. protected previousMouseY: number = 0;
  31. public reactToManipulation(
  32. type: EventType,
  33. { dx, dy, pos }: { dx: number; dy: number; pos: PositionType }
  34. ) {
  35. if (type === 'move') {
  36. this.move(dx, dy);
  37. }
  38. if (type === 'resize') {
  39. this.resizeByEvent(dx, dy, pos);
  40. }
  41. }
  42. /** 响应元素视图状态变化 */
  43. public manipulate = (ev: MouseEvent) => {
  44. const scale = this.visual.getScreenCTM()!.a;
  45. const dx = (ev.screenX - this.previousMouseX) / scale;
  46. const dy = (ev.screenY - this.previousMouseY) / scale;
  47. if (this.isDragging) {
  48. this.onChange({ target: 'marker', id: this.id, event: 'move', data: { dx, dy } });
  49. this.move(dx, dy);
  50. }
  51. if (this.isResizing) {
  52. this.resize(dx, dy, (pos: PositionType) => {
  53. this.onChange({ target: 'marker', id: this.id, event: 'resize', data: { dx, dy, pos } });
  54. });
  55. }
  56. this.previousMouseX = ev.screenX;
  57. this.previousMouseY = ev.screenY;
  58. };
  59. public endManipulation() {
  60. this.isDragging = false;
  61. this.isResizing = false;
  62. }
  63. public select() {
  64. this.isActive = true;
  65. if (this.onSelected) {
  66. this.onSelected(this);
  67. }
  68. return;
  69. }
  70. public deselect() {
  71. this.isActive = false;
  72. this.endManipulation();
  73. return;
  74. }
  75. protected setup() {
  76. this.visual = SvgHelper.createGroup();
  77. // translate
  78. this.visual.transform.baseVal.appendItem(SvgHelper.createTransform());
  79. this.visual.addEventListener('mousedown', this.mouseDown);
  80. this.visual.addEventListener('mouseup', this.mouseUp);
  81. this.visual.addEventListener('mousemove', this.mouseMove);
  82. this.visual.addEventListener('touchstart', this.onTouch, { passive: false });
  83. this.visual.addEventListener('touchend', this.onTouch, { passive: false });
  84. this.visual.addEventListener('touchmove', this.onTouch, { passive: false });
  85. this.renderVisual = SvgHelper.createGroup([['class', 'render-visual']]);
  86. this.visual.appendChild(this.renderVisual);
  87. }
  88. protected addToVisual = (el: SVGElement) => {
  89. this.visual.appendChild(el);
  90. };
  91. protected addToRenderVisual = (el: SVGElement) => {
  92. this.renderVisual.appendChild(el);
  93. };
  94. protected resize(x: number, y: number, cb?: Function) {
  95. return;
  96. }
  97. protected resizeByEvent(x: number, y: number, pos?: PositionType) {
  98. return;
  99. }
  100. /** 截获 Touch 事件,并且转发为 Mouse 事件 */
  101. protected onTouch(ev: TouchEvent) {
  102. ev.preventDefault();
  103. const newEvt = document.createEvent('MouseEvents');
  104. const touch = ev.changedTouches[0];
  105. let type = null;
  106. switch (ev.type) {
  107. case 'touchstart':
  108. type = 'mousedown';
  109. break;
  110. case 'touchmove':
  111. type = 'mousemove';
  112. break;
  113. case 'touchend':
  114. type = 'mouseup';
  115. break;
  116. default:
  117. break;
  118. }
  119. newEvt.initMouseEvent(
  120. type!,
  121. true,
  122. true,
  123. window,
  124. 0,
  125. touch.screenX,
  126. touch.screenY,
  127. touch.clientX,
  128. touch.clientY,
  129. ev.ctrlKey,
  130. ev.altKey,
  131. ev.shiftKey,
  132. ev.metaKey,
  133. 0,
  134. null
  135. );
  136. ev.target!.dispatchEvent(newEvt);
  137. }
  138. private mouseDown = (ev: MouseEvent) => {
  139. ev.stopPropagation();
  140. if (this.page && this.page.mode === 'mirror') {
  141. return;
  142. }
  143. this.select();
  144. this.isDragging = true;
  145. this.previousMouseX = ev.screenX;
  146. this.previousMouseY = ev.screenY;
  147. };
  148. private mouseUp = (ev: MouseEvent) => {
  149. ev.stopPropagation();
  150. this.endManipulation();
  151. };
  152. private mouseMove = (ev: MouseEvent) => {
  153. ev.stopPropagation();
  154. this.manipulate(ev);
  155. };
  156. private move = (dx: number, dy: number) => {
  157. const translate = this.visual.transform.baseVal.getItem(0);
  158. translate.setMatrix(translate.matrix.translate(dx, dy));
  159. this.visual.transform.baseVal.replaceItem(translate, 0);
  160. };
  161. }