Keine Beschreibung

index.ts 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { MarkerType } from '../types';
  2. import { RectangularMarker } from '../RectangularMarker';
  3. import { SvgHelper } from '../../renderer/SvgHelper';
  4. import { PositionType } from 'fc-whiteboard/src/event/Event';
  5. import { WhitePage } from 'fc-whiteboard/src/board/WhitePage';
  6. const OkIcon = require('../../assets/check.svg');
  7. const CancelIcon = require('../../assets/times.svg');
  8. export class TextMarker extends RectangularMarker {
  9. type: MarkerType = 'text';
  10. public static createMarker = (page?: WhitePage): TextMarker => {
  11. const marker = new TextMarker();
  12. marker.page = page;
  13. marker.setup();
  14. return marker;
  15. };
  16. /** 设置文本 */
  17. public setText(text: string) {
  18. this.text = text;
  19. this.renderText();
  20. }
  21. protected readonly MIN_SIZE = 50;
  22. private readonly DEFAULT_TEXT = 'Double-click to edit text';
  23. private text: string = this.DEFAULT_TEXT;
  24. private textElement: SVGTextElement;
  25. private inDoubleTap = false;
  26. private editor: HTMLDivElement;
  27. private editorTextArea: HTMLTextAreaElement;
  28. protected setup() {
  29. super.setup();
  30. this.textElement = SvgHelper.createText();
  31. this.addToRenderVisual(this.textElement);
  32. SvgHelper.setAttributes(this.visual, [['class', 'text-marker']]);
  33. this.textElement.transform.baseVal.appendItem(SvgHelper.createTransform()); // translate transorm
  34. this.textElement.transform.baseVal.appendItem(SvgHelper.createTransform()); // scale transorm
  35. this.renderText();
  36. this.visual.addEventListener('dblclick', this.onDblClick);
  37. this.visual.addEventListener('touchstart', this.onTap);
  38. }
  39. protected resize(x: number, y: number, onPosition?: (pos: PositionType) => void) {
  40. super.resize(x, y, onPosition);
  41. this.sizeText();
  42. }
  43. private renderText = () => {
  44. const LINE_SIZE = '1.2em';
  45. while (this.textElement.lastChild) {
  46. this.textElement.removeChild(this.textElement.lastChild);
  47. }
  48. const lines = this.text.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/);
  49. for (let line of lines) {
  50. if (line.trim() === '') {
  51. line = ' '; // workaround for swallowed empty lines
  52. }
  53. this.textElement.appendChild(SvgHelper.createTSpan(line, [['x', '0'], ['dy', LINE_SIZE]]));
  54. }
  55. setTimeout(this.sizeText, 10);
  56. };
  57. private sizeText = () => {
  58. const textSize = this.textElement.getBBox();
  59. let x = 0;
  60. let y = 0;
  61. let scale = 1.0;
  62. if (textSize.width > 0 && textSize.height > 0) {
  63. const xScale = (this.width * 1.0) / textSize.width;
  64. const yScale = (this.height * 1.0) / textSize.height;
  65. scale = Math.min(xScale, yScale);
  66. x = (this.width - textSize.width * scale) / 2;
  67. y = (this.height - textSize.height * scale) / 2;
  68. }
  69. this.textElement.transform.baseVal.getItem(0).setTranslate(x, y);
  70. this.textElement.transform.baseVal.getItem(1).setScale(scale, scale);
  71. };
  72. private onDblClick = (ev: MouseEvent) => {
  73. this.showEditor();
  74. };
  75. private onTap = (ev: TouchEvent) => {
  76. if (this.inDoubleTap) {
  77. this.inDoubleTap = false;
  78. this.showEditor();
  79. } else {
  80. this.inDoubleTap = true;
  81. setTimeout(() => {
  82. this.inDoubleTap = false;
  83. }, 300);
  84. }
  85. };
  86. private showEditor = () => {
  87. this.editor = document.createElement('div');
  88. this.editor.className = 'fc-whiteboard-text-editor';
  89. this.editorTextArea = document.createElement('textarea');
  90. if (this.text !== this.DEFAULT_TEXT) {
  91. this.editorTextArea.value = this.text;
  92. }
  93. this.editorTextArea.addEventListener('keydown', this.onEditorKeyDown);
  94. this.editor.appendChild(this.editorTextArea);
  95. document.body.appendChild(this.editor);
  96. const buttons = document.createElement('div');
  97. buttons.className = 'fc-whiteboard-text-editor-button-bar';
  98. this.editor.appendChild(buttons);
  99. const okButton = document.createElement('div');
  100. okButton.className = 'fc-whiteboard-text-editor-button';
  101. okButton.innerHTML = OkIcon;
  102. okButton.addEventListener('click', this.onEditorOkClick);
  103. buttons.appendChild(okButton);
  104. const cancelButton = document.createElement('div');
  105. cancelButton.className = 'fc-whiteboard-text-editor-button';
  106. cancelButton.innerHTML = CancelIcon;
  107. cancelButton.addEventListener('click', this.closeEditor);
  108. buttons.appendChild(cancelButton);
  109. };
  110. /** 响应文本输入的事件 */
  111. private onEditorOkClick = (ev: MouseEvent | null) => {
  112. if (this.editorTextArea.value.trim()) {
  113. this.text = this.editorTextArea.value;
  114. } else {
  115. this.text = this.DEFAULT_TEXT;
  116. }
  117. // 触发文本修改时间
  118. this.onChange({ target: 'marker', id: this.id, event: 'changeText', data: this.text });
  119. this.renderText();
  120. this.closeEditor();
  121. };
  122. private closeEditor = () => {
  123. document.body.removeChild(this.editor);
  124. };
  125. private onEditorKeyDown = (ev: KeyboardEvent) => {
  126. if (ev.key === 'Enter' && ev.ctrlKey) {
  127. ev.preventDefault();
  128. this.onEditorOkClick(null);
  129. }
  130. };
  131. }