|
|
@@ -43,7 +43,7 @@
|
|
|
'align-items': getWordAlignItems(word, block),
|
|
|
}"
|
|
|
>
|
|
|
- <span class="pinyin" :style="getPinyinStyle(word, block)">
|
|
|
+ <span class="pinyin" :style="[getPinyinStyle(word, block), getWholePinyinStyle(word, block)]">
|
|
|
{{ getPinyinText(word) }}
|
|
|
</span>
|
|
|
<span class="py-char" :style="getCharStyle(word, block)">{{ convertText(word.text) }}</span>
|
|
|
@@ -829,7 +829,12 @@ export default {
|
|
|
},
|
|
|
// 兼容历史数据
|
|
|
getPinyinText(item) {
|
|
|
- return this.checkShowPinyin(item.showPinyin) ? item.pinyin : '\u200B';
|
|
|
+ if (!this.checkShowPinyin(item.showPinyin)) return '\u200B';
|
|
|
+ const raw = item.pinyin || '';
|
|
|
+ // 整词渲染时去掉首尾占位空格(这些空格只在逐字对齐时有意义),
|
|
|
+ // 避免占位空格撑宽拼音导致居中偏移
|
|
|
+ const text = String(raw).replace(/^[\u3000\s]+|[\u3000\s]+$/g, '');
|
|
|
+ return text || '\u200B';
|
|
|
},
|
|
|
shouldShowWordPinyin(item) {
|
|
|
return this.checkShowPinyin(item && item.showPinyin);
|
|
|
@@ -855,6 +860,68 @@ export default {
|
|
|
}
|
|
|
return 'center';
|
|
|
},
|
|
|
+ // 判断字符是否半宽(ASCII / 拉丁字母及声调字符等),用于估算文字视觉宽度
|
|
|
+ isHalfWidthChar(char) {
|
|
|
+ const code = char.charCodeAt(0);
|
|
|
+ // 基础拉丁、拉丁扩展 A/B(拼音带声调字符大多在此区间)
|
|
|
+ if (code <= 0x024f) return true;
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ // 字符视觉宽度单位:中文/全角=1,英文/数字/拉丁字母≈0.5,组合音标不占宽
|
|
|
+ getCharWidthUnit(char) {
|
|
|
+ const code = char.charCodeAt(0);
|
|
|
+ if (code >= 0x0300 && code <= 0x036f) return 0;
|
|
|
+ return this.isHalfWidthChar(char) ? 0.5 : 1;
|
|
|
+ },
|
|
|
+ // 整词渲染时,让拼音只横跨非标点文字区域并居中;
|
|
|
+ getWholePinyinStyle(word, block) {
|
|
|
+ const letterSpacing = block?.styleObj?.letterSpacing;
|
|
|
+ if (letterSpacing && letterSpacing !== '0' && letterSpacing !== '0px') {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+ const text = word.text || '';
|
|
|
+ const chars = Array.from(text);
|
|
|
+ if (chars.length === 0) {
|
|
|
+ return { alignSelf: 'stretch', textAlign: 'center' };
|
|
|
+ }
|
|
|
+
|
|
|
+ let totalUnits = 0;
|
|
|
+ let leadingUnits = 0;
|
|
|
+ let trailingUnits = 0;
|
|
|
+
|
|
|
+ chars.forEach((c) => {
|
|
|
+ totalUnits += this.getCharWidthUnit(c);
|
|
|
+ });
|
|
|
+
|
|
|
+ for (let i = 0; i < chars.length; i += 1) {
|
|
|
+ PUNCT_REGEX.lastIndex = 0;
|
|
|
+ if (PUNCT_REGEX.test(chars[i])) {
|
|
|
+ leadingUnits += this.getCharWidthUnit(chars[i]);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (let i = chars.length - 1; i >= 0; i -= 1) {
|
|
|
+ PUNCT_REGEX.lastIndex = 0;
|
|
|
+ if (PUNCT_REGEX.test(chars[i])) {
|
|
|
+ trailingUnits += this.getCharWidthUnit(chars[i]);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const nonPunctUnits = totalUnits - leadingUnits - trailingUnits;
|
|
|
+ if (nonPunctUnits <= 0 || totalUnits <= 0 || (leadingUnits === 0 && trailingUnits === 0)) {
|
|
|
+ return { alignSelf: 'stretch', textAlign: 'center' };
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ alignSelf: 'flex-start',
|
|
|
+ width: `${(nonPunctUnits / totalUnits) * 100}%`,
|
|
|
+ marginLeft: `${(leadingUnits / totalUnits) * 100}%`,
|
|
|
+ textAlign: 'center',
|
|
|
+ };
|
|
|
+ },
|
|
|
// 拼音固定为拼音字体,跟随汉字的字号、颜色、粗细的样式
|
|
|
getPinyinStyle(item, block) {
|
|
|
const styles = {};
|