Skip to content

Commit b351c18

Browse files
JessYan0913yanheng
andauthored
46 支持文本换行即多行文本 (#74)
* update: 监测浏览器功能由utils统一提供 * update: 更新文本工具相关的国际化缺失告警 * update: 更新文本换行方式为回车换行,并重新计算文本域尺寸 * update: 当图形尺寸变化时高亮框跟随变化 * update: 更新文本域的高度计算逻辑 * refactor: 重构函数定义 * update: 增加函数辅助工具 * update: 文本域输入考虑节点缩放问题 --------- Co-authored-by: yanheng <[email protected]>
1 parent cdd3691 commit b351c18

File tree

16 files changed

+348
-202
lines changed

16 files changed

+348
-202
lines changed

packages/plugin-selector/src/selector.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ interface HightLightRect {
77
transformHandler: (...args: any) => any;
88
}
99

10+
let TRANSFORM_CHANGE_STR = [
11+
'widthChange',
12+
'heightChange',
13+
'scaleXChange',
14+
'scaleYChange',
15+
'skewXChange',
16+
'skewYChange',
17+
'rotationChange',
18+
'offsetXChange',
19+
'offsetYChange',
20+
'transformsEnabledChange',
21+
'strokeWidthChange',
22+
];
23+
1024
export class Selector {
1125
public app: App;
1226
public selected: Map<number | string, KonvaNode>;
@@ -179,6 +193,7 @@ export class Selector {
179193
private setHightRect(...nodes: KonvaNode[]) {
180194
this.hightLightRects = nodes.reduce((hightRects, node) => {
181195
const rect = new Konva.Rect({
196+
name: `${node._id}_height_rect`,
182197
stroke: this.hightLightConfig.stroke,
183198
strokeWidth: this.hightLightConfig.strokeWidth,
184199
dash: this.hightLightConfig.dash,
@@ -191,7 +206,9 @@ export class Selector {
191206
const transformHandler = () =>
192207
requestAnimationFrame(() => this.calculateNodeRect(node, rect, this.hightLightConfig.padding ?? 0));
193208

194-
node.on('dragmove transform xChange yChange', transformHandler);
209+
node.on('absoluteTransformChange', transformHandler);
210+
211+
node.on(TRANSFORM_CHANGE_STR.join(' '), transformHandler);
195212

196213
hightRects.set(node.id(), {
197214
rect,

packages/tools/src/text/utils.ts

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,47 +60,43 @@ export const createTextarea = (app: App, textNode: Konva.Text, onUpdated: () =>
6060
textNode.show();
6161
}
6262

63-
function setTextareaWidth(newWidth: number) {
64-
newWidth = Math.max(textarea.value.length * textNode.fontSize(), newWidth);
65-
66-
//TODO: The platform detection should be a generic function.
67-
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
68-
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
69-
if (isSafari || isFirefox) {
70-
newWidth = Math.ceil(newWidth);
71-
}
72-
73-
const isEdge = document.DOCUMENT_NODE || /Edge/.test(navigator.userAgent);
74-
if (isEdge) {
75-
newWidth += 1;
76-
}
77-
textarea.style.width = newWidth + 'px';
78-
}
79-
8063
textarea.addEventListener('keydown', function (e: KeyboardEvent) {
8164
e.stopPropagation();
82-
if (e.key === 'Enter' && !e.shiftKey) {
83-
if (textarea.value.trim().length >= 1) {
84-
textNode.text(textarea.value);
85-
} else {
86-
app.remove(textNode);
87-
}
88-
removeTextarea();
89-
onUpdated();
90-
}
9165
if (e.key === 'Escape') {
9266
removeTextarea();
9367
}
94-
const scale = textNode.getAbsoluteScale().x;
95-
setTextareaWidth(textNode.width() * scale - textNode.padding() * 2 + 20);
96-
textarea.style.height = 'auto';
97-
textarea.style.height = textarea.scrollHeight + textNode.fontSize() + 'px';
68+
});
69+
70+
textarea.addEventListener('input', () => {
71+
let text = textarea.value.replace(/\n/g, '<br/>'); // 将换行符替换为<br/>标签,以便正确测量宽度
72+
let tempDiv = document.createElement('div');
73+
tempDiv.innerHTML = text;
74+
tempDiv.style.position = 'absolute';
75+
tempDiv.style.visibility = 'hidden';
76+
tempDiv.style.whiteSpace = 'pre-wrap'; // 保持文本的换行
77+
document.body.appendChild(tempDiv);
78+
const newWidth = tempDiv.offsetWidth + 10;
79+
textarea.style.width = newWidth * textNode.scaleX() + 'px'; // 设置宽度为文本实际宽度
80+
document.body.removeChild(tempDiv);
81+
82+
let lineHeight = parseFloat(getComputedStyle(textarea).lineHeight);
83+
let rows = textarea.value.split('\n').length;
84+
textarea.style.height = 'auto'; // 重置高度以便重新计算
85+
const newHeight = lineHeight * rows;
86+
textarea.style.height = newHeight + 'px';
87+
textNode.width(newWidth);
88+
textNode.height(newHeight / textNode.scaleY());
9889
});
9990

10091
function handleOutsideClick(e: MouseEvent) {
10192
if (e.target !== textarea) {
102-
textNode.text(textarea.value);
93+
if (textarea.value.trim().length >= 1) {
94+
textNode.text(textarea.value);
95+
} else {
96+
app.remove(textNode);
97+
}
10398
removeTextarea();
99+
onUpdated();
104100
}
105101
}
106102
setTimeout(() => {

packages/utils/src/base-service.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { EventArgs, EventBus } from './event';
22

33
type Method = (...args: any[]) => any;
44

5-
export interface Middleware {
6-
(args: any[], next: Method): any;
7-
}
5+
export type Middleware = (args: any[], next: Method) => any;
86

97
export class BaseService<T extends EventArgs = EventArgs> extends EventBus<T> {
108
constructor() {

packages/utils/src/files.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ export const isXmlDocument = (file: File): boolean => {
106106
return /^(?:application|text)\/(?:xml|xhtml\+xml)$/.test(file.type);
107107
};
108108

109-
export interface FileHandler {
110-
(fileRender: FileReader): void;
111-
}
109+
export type FileHandler = (fileRender: FileReader) => void;
112110

113111
export const readeFile = <T extends string | ArrayBuffer | null>(fileHandler: FileHandler): Promise<T> => {
114112
const reader = new FileReader();

packages/utils/src/func.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
export interface ThrottleWrapper {
2+
(...args: any[]): void;
3+
cancel: () => void;
4+
}
5+
6+
export type DebounceWrapper = (...args: any[]) => void;
7+
8+
export const emptyFn = (): any => undefined;
9+
10+
/**
11+
* 阻塞函数
12+
*
13+
* @param ms
14+
* @returns
15+
*/
16+
export const sleep = (ms: number): Promise<void> =>
17+
new Promise((resolve) => {
18+
const timer = setTimeout(() => {
19+
clearTimeout(timer);
20+
resolve();
21+
}, ms);
22+
});
23+
24+
/**
25+
* 函数柯里化
26+
*
27+
* @param fun
28+
* @returns
29+
*/
30+
export function curryFunction(fun: Function): Function {
31+
return function curried(this: any, ...args: any[]) {
32+
if (args.length >= fun.length) {
33+
return fun.apply(this, args);
34+
} else {
35+
return function (this: any, ...args1: any[]) {
36+
return curried.apply(this, args.concat(args1));
37+
};
38+
}
39+
};
40+
}
41+
42+
/**
43+
* 函数节流
44+
*
45+
* @param callback
46+
* @param delay
47+
* @returns
48+
*/
49+
export function throttle(callback: Function, delay: number): ThrottleWrapper {
50+
let lastExec: number = 0;
51+
let timerId: any = undefined;
52+
let canceled: boolean = false;
53+
54+
const wrapper: ThrottleWrapper = function (this: any, ...args: any[]) {
55+
if (canceled) {
56+
return;
57+
}
58+
const elapsed = Date.now() - lastExec;
59+
60+
const exec = () => {
61+
lastExec = Date.now();
62+
callback.apply(this, args);
63+
};
64+
65+
if (timerId) {
66+
clearTimeout(timerId);
67+
timerId = undefined;
68+
}
69+
70+
if (elapsed > delay) {
71+
exec();
72+
} else {
73+
timerId = setTimeout(exec, delay - elapsed);
74+
}
75+
};
76+
77+
wrapper.cancel = () => {
78+
timerId && clearTimeout(timerId);
79+
lastExec = 0;
80+
timerId = undefined;
81+
canceled = true;
82+
};
83+
84+
return wrapper;
85+
}
86+
87+
/**
88+
* 函数防抖
89+
*
90+
* @param callback
91+
* @param delay
92+
* @returns
93+
*/
94+
export function debounce(callback: Function, delay: number): DebounceWrapper {
95+
let timerId: any = undefined;
96+
return function (this: any, ...args: any[]) {
97+
if (!timerId) {
98+
callback.apply(this, args);
99+
} else {
100+
clearTimeout(timerId);
101+
}
102+
timerId = setTimeout(() => {
103+
timerId = undefined;
104+
}, delay);
105+
};
106+
}

packages/utils/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export * from './string';
1818

1919
export * from './uid';
2020

21+
export * from './os';
22+
23+
export * from './func';
24+
2125
export const toLine = (name: string = '') => name.replace(/\B([A-Z])/g, '-$1').toLowerCase();
2226

2327
export function replacePropertyWithValue(obj: Record<string | number | symbol, any>, value: any, newValue: any) {

0 commit comments

Comments
 (0)