Skip to content

Commit 02d0847

Browse files
committed
fix: 修复文本解析
1 parent e3b56fb commit 02d0847

File tree

7 files changed

+261
-110
lines changed

7 files changed

+261
-110
lines changed

example/components/Test.tsx

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,62 @@ import styles from './test.less';
77
export const Test: FC = () => {
88
return (
99
<div id={'x-tag'}>
10-
<div className={styles.ellipsis} style={{ width: 50 }}>
11-
926024273276
10+
<td className="ant-table-cell cell___r1jPs">
11+
<span>
12+
<div className={styles.ellipsis} aria-label="34646878928">
13+
<span title="34646878928" aria-hidden="true">
14+
"346468"
15+
"..."
16+
</span>
17+
<div
18+
role="button"
19+
className="ant-typography-copy"
20+
aria-label="复制"
21+
style={{
22+
border: 0,
23+
background: 'transparent',
24+
padding: 0,
25+
display: 'inline-block',
26+
}}
27+
>
28+
<span role="img" aria-label="copy" className={styles.copy}>
29+
<svg
30+
viewBox="64 64 896 896"
31+
focusable="false"
32+
className=""
33+
data-icon="copy"
34+
width="1em"
35+
height="1em"
36+
fill="currentColor"
37+
aria-hidden="true"
38+
>
39+
<path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path>
40+
</svg>
41+
</span>
42+
</div>{' '}
43+
</div>
44+
</span>
45+
</td>
46+
<div className={styles.ellipsis}>
47+
<span title="34646878928" aria-hidden="true">
48+
346468...
49+
</span>
50+
<div role="button" className="ant-typography-copy" aria-label="复制">
51+
<span role="img" aria-label="copy" className={styles.copy}>
52+
<svg
53+
viewBox="64 64 896 896"
54+
focusable="false"
55+
className=""
56+
data-icon="copy"
57+
width="1em"
58+
height="1em"
59+
fill="currentColor"
60+
aria-hidden="true"
61+
>
62+
<path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path>
63+
</svg>
64+
</span>
65+
</div>
1266
</div>
1367
</div>
1468
);

example/components/test.less

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@
1515
}
1616

1717
.ellipsis {
18+
display: flex;
1819
overflow: hidden;
1920
white-space: nowrap;
2021
text-overflow: ellipsis;
2122
}
23+
24+
.copy {
25+
color: #1890ff;
26+
text-decoration: none;
27+
outline: none;
28+
cursor: pointer;
29+
transition: color 0.3s;
30+
margin-left: 4px;
31+
}

src/function/nodeToSketchLayers.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,17 @@ export const nodeToSketchLayers = (node: Element): AnyLayer[] => {
113113
if (isText) {
114114
text = transferToText(node);
115115
console.info('[nodeToSketchLayers]转换为 Text:', text);
116-
layers.push(text);
116+
if (text instanceof Array) {
117+
for (let i = 0; i < text.length; i++) {
118+
const textElement = text[i];
119+
if (i !== 0) {
120+
textElement.x = text[i - 1].right;
121+
}
122+
layers.push(textElement);
123+
}
124+
} else {
125+
layers.push(text);
126+
}
117127
}
118128

119129
// 判断一下是否有伪类

src/helpers/text.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* 获取文本定界框与行数
3+
* @param textNode
4+
*/
5+
export const getTextContext = (textNode) => {
6+
// 创建Range 对象
7+
const rangeHelper = document.createRange();
8+
rangeHelper.selectNodeContents(textNode); // 选中文本节点
9+
10+
const textRanges = Array.from(rangeHelper.getClientRects());
11+
12+
const lines = textRanges.length;
13+
const textBCR = rangeHelper.getBoundingClientRect();
14+
15+
rangeHelper.detach();
16+
return {
17+
textBCR,
18+
lines,
19+
textRanges,
20+
};
21+
};
22+
23+
/**
24+
* 获取文本的绝对 BCR 定位值
25+
* @param parentNode
26+
* @param textNode
27+
*/
28+
export const getTextAbsBCR = (parentNode, textNode) => {
29+
const styles: CSSStyleDeclaration = getComputedStyle(parentNode);
30+
const nodeBCR = parentNode.getBoundingClientRect();
31+
32+
let x = nodeBCR.x;
33+
let y = nodeBCR.y;
34+
35+
const { lines, textBCR } = getTextContext(textNode);
36+
37+
const {
38+
lineHeight,
39+
display,
40+
textAlign,
41+
paddingRight,
42+
paddingLeft,
43+
borderLeftWidth,
44+
paddingTop,
45+
borderTopWidth,
46+
} = styles;
47+
48+
let textWidth = textBCR.width;
49+
const lineHeightInt = parseInt(lineHeight, 10);
50+
const textBCRHeight = textBCR.height;
51+
52+
let fixY = 0;
53+
54+
// center text inside a box
55+
if (lineHeightInt && textBCRHeight !== lineHeightInt * lines) {
56+
fixY = (textBCRHeight - lineHeightInt * lines) / 2;
57+
}
58+
59+
if (display !== 'inline') {
60+
// 如果是左对齐
61+
if (textAlign === 'left' || textAlign === 'start') {
62+
// 确认下 padding 的距离
63+
const pl = parseFloat(paddingLeft);
64+
x = x + pl;
65+
}
66+
// 如果是居中对齐
67+
if (textAlign === 'center') {
68+
x = x + nodeBCR.width / 2 - textWidth / 2;
69+
}
70+
// 如果是右对齐
71+
if (textAlign === 'right') {
72+
// 确认下 padding 的距离
73+
const pl = parseFloat(paddingRight);
74+
x = nodeBCR.right - textWidth;
75+
x = x - pl;
76+
}
77+
78+
// 添加左侧的 border 宽度
79+
x = x + parseFloat(borderLeftWidth);
80+
81+
// 处理内部高度
82+
const pt = parseFloat(paddingTop);
83+
y = y + pt;
84+
85+
// 处理顶部 border 宽度
86+
y = y + parseFloat(borderTopWidth);
87+
}
88+
89+
let textHeight = fixY < 0 ? textBCRHeight - fixY * 2 : textBCRHeight;
90+
91+
// 处理垂直居中的样式
92+
if (display === 'flex' || display === 'inline-flex') {
93+
y = y + (nodeBCR.height - textHeight) / 2;
94+
}
95+
96+
return { x, y, height: textHeight, width: textWidth };
97+
};
98+
/**
99+
* 获取一行宽度的文本
100+
* @param textNode
101+
* @param width
102+
*/
103+
export const getLineTextWithWidth = (textNode: ChildNode, width: number) => {
104+
const text = textNode.textContent;
105+
if (!text) return '';
106+
107+
let textContent = '';
108+
for (let i = 0; i < text.length; i++) {
109+
const charNode = textNode.cloneNode(true);
110+
charNode.textContent = textContent;
111+
document.body.appendChild(charNode);
112+
const { textBCR } = getTextContext(charNode);
113+
document.body.removeChild(charNode);
114+
if (textBCR.width < width) {
115+
textContent += text[i];
116+
}
117+
}
118+
return textContent;
119+
};

src/model/Layer/Text.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,46 @@ class Text extends Base {
133133
getTextBehaviour = () => {
134134
return SketchFormat.TextBehaviour;
135135
};
136+
137+
/**
138+
* 从节点中获取样式
139+
* @param node
140+
*/
141+
static getTextStyleFromNode = (node: Element) => {
142+
const styles: CSSStyleDeclaration = getComputedStyle(node);
143+
144+
const {
145+
// 字体
146+
fontFamily,
147+
fontWeight,
148+
fontSize,
149+
lineHeight,
150+
letterSpacing,
151+
textTransform,
152+
textDecorationLine,
153+
textAlign,
154+
justifyContent,
155+
display,
156+
color,
157+
} = styles;
158+
const textStyle: TextStyleParams = {
159+
fontFamily,
160+
fontSize: parseInt(fontSize, 10),
161+
lineHeight: lineHeight !== 'normal' ? parseFloat(lineHeight) : undefined,
162+
letterSpacing:
163+
letterSpacing !== 'normal' ? parseFloat(letterSpacing) : undefined,
164+
fontWeight: Text.parseFontWeight(fontWeight),
165+
color,
166+
textTransform,
167+
textDecoration: textDecorationLine,
168+
textAlign:
169+
display === 'flex' || display === 'inline-flex'
170+
? justifyContent
171+
: textAlign,
172+
skipSystemFonts: true,
173+
};
174+
return textStyle;
175+
};
136176
}
137177

138178
export default Text;

src/parser/pseudoText.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const parsePseudoText = (node: Element, pseudoElt: 'before' | 'after') => {
6666
width: textBCR.width,
6767
height: nodeBCR.height,
6868
text: pseudoText,
69+
style: Text.getTextStyleFromNode(node),
6970
multiline: false,
7071
});
7172
};

0 commit comments

Comments
 (0)