|
|
@@ -1,12 +1,12 @@
|
|
|
<template>
|
|
|
<view>
|
|
|
<view :class="classes" @click="handleClick" ref="root" :id="'root' + refRandomId">
|
|
|
- <view v-if="!exceeded">{{ content }}</view>
|
|
|
+ <view class="nut-ellipsis-wordbreak" v-if="!exceeded">{{ content }}</view>
|
|
|
|
|
|
- <view v-if="exceeded && !expanded">
|
|
|
- {{ ellipsis.leading
|
|
|
+ <view v-if="exceeded && !expanded" class="nut-ellipsis-wordbreak">
|
|
|
+ {{ ellipsis.leading }}{{ ellipsis.leading && symbol
|
|
|
}}<view class="nut-ellipsis-text" v-if="expandText" @click.stop="clickHandle(1)">{{ expandText }}</view
|
|
|
- >{{ ellipsis.tailing }}
|
|
|
+ >{{ ellipsis.tailing && symbol }}{{ ellipsis.tailing }}
|
|
|
</view>
|
|
|
<view v-if="exceeded && expanded">
|
|
|
{{ content }}
|
|
|
@@ -14,15 +14,16 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <view
|
|
|
- class="nut-ellipsis-copy"
|
|
|
- @click="handleClick"
|
|
|
- ref="rootContain"
|
|
|
- :id="'rootContain' + refRandomId"
|
|
|
- :style="{ width: widthRef }"
|
|
|
- >
|
|
|
+ <view class="nut-ellipsis-copy" ref="rootContain" :id="'rootContain' + refRandomId" :style="{ width: widthRef }">
|
|
|
<view>{{ contantCopy }}</view>
|
|
|
</view>
|
|
|
+
|
|
|
+ <!-- 省略号 symbol -->
|
|
|
+ <view class="nut-ellipsis-copy" ref="symbolContain" :id="'symbolContain' + refRandomId" style="display: inline">{{
|
|
|
+ symbolText
|
|
|
+ }}</view>
|
|
|
+
|
|
|
+ <!-- 数字 9 英文 W -->
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
@@ -30,7 +31,7 @@
|
|
|
import { ref, reactive, toRefs, computed, onMounted, PropType, watch, unref } from 'vue';
|
|
|
import { createComponent } from '@/packages/utils/create';
|
|
|
import { useTaroRect } from '@/packages/utils/useTaroRect';
|
|
|
-import Taro from '@tarojs/taro';
|
|
|
+import Taro, { nextTick } from '@tarojs/taro';
|
|
|
const { componentName, create } = createComponent('ellipsis');
|
|
|
export type Direction = 'start' | 'end' | 'middle';
|
|
|
|
|
|
@@ -75,12 +76,13 @@ export default create({
|
|
|
setup(props, { emit }) {
|
|
|
const root = ref(null);
|
|
|
const rootContain = ref(null);
|
|
|
+ const symbolContain = ref(null);
|
|
|
let contantCopy = ref(props.content);
|
|
|
let container: any = null;
|
|
|
let maxHeight = 0; // 超出的最大高度
|
|
|
let lineHeight = 0; // 当行的最大高度
|
|
|
let originHeight = 0; // 原始高度
|
|
|
- const ellipsis = ref<EllipsisedValue>();
|
|
|
+ const ellipsis = reactive<EllipsisedValue>({});
|
|
|
const refRandomId = Math.random().toString(36).slice(-8);
|
|
|
let widthRef = ref('auto');
|
|
|
const state = reactive({
|
|
|
@@ -88,30 +90,39 @@ export default create({
|
|
|
expanded: false //是否折叠
|
|
|
});
|
|
|
|
|
|
+ let widthBase = [14, 10, 7, 8.4, 10]; // 中、英(大)、英(小)、数字、其他字符的基础宽度
|
|
|
+ let symbolTextWidth = widthBase[0] * 0.7921;
|
|
|
+ const chineseReg = /^[\u4e00-\u9fa5]+$/; // 汉字
|
|
|
+ const digitReg = /^[0-9]+$/; // 数字
|
|
|
+ const letterUpperReg = /^[A-Z]+$/; //字母
|
|
|
+ const letterLowerReg = /^[a-z]+$/; //字母
|
|
|
+
|
|
|
const classes = computed(() => {
|
|
|
const prefixCls = componentName;
|
|
|
return {
|
|
|
+ ell: true,
|
|
|
[prefixCls]: true
|
|
|
};
|
|
|
});
|
|
|
|
|
|
- watch(
|
|
|
- () => props.content,
|
|
|
- (newV, oldVal) => {
|
|
|
- if (newV != oldVal) {
|
|
|
- if (container) {
|
|
|
- // document.body.appendChild(container);
|
|
|
- }
|
|
|
- // createContainer();
|
|
|
- }
|
|
|
+ const symbolText = computed(() => {
|
|
|
+ if (props.direction == 'end' || props.direction == 'middle') {
|
|
|
+ return `${props.symbol}${props.expandText}`;
|
|
|
}
|
|
|
- );
|
|
|
+ return `${props.symbol}${props.expandText}${props.symbol}`;
|
|
|
+ });
|
|
|
|
|
|
onMounted(() => {
|
|
|
setTimeout(() => {
|
|
|
+ getSymbolInfo();
|
|
|
getReference();
|
|
|
- }, 500);
|
|
|
+ }, 100);
|
|
|
});
|
|
|
+ // 获取省略号宽度
|
|
|
+ const getSymbolInfo = async () => {
|
|
|
+ const refe = await useTaroRect(symbolContain, Taro);
|
|
|
+ symbolTextWidth = refe.width ? Math.ceil(refe.width) : Math.ceil(widthBase[0] * 0.7921);
|
|
|
+ };
|
|
|
|
|
|
const getReference = async () => {
|
|
|
let element = unref(root);
|
|
|
@@ -122,9 +133,10 @@ export default create({
|
|
|
.select(`#${(element as any).id}`)
|
|
|
.fields(
|
|
|
{
|
|
|
- computedStyle: ['width', 'height', 'lineHeight', 'paddingTop', 'paddingBottom']
|
|
|
+ computedStyle: ['width', 'height', 'lineHeight', 'paddingTop', 'paddingBottom', 'fontSize']
|
|
|
},
|
|
|
(res) => {
|
|
|
+ console.log(res);
|
|
|
lineHeight = pxToNumber(res.lineHeight === 'normal' ? props.lineHeight : res.lineHeight);
|
|
|
maxHeight = Math.floor(
|
|
|
lineHeight * (Number(props.rows) + 0.5) + pxToNumber(res.paddingTop) + pxToNumber(res.paddingBottom)
|
|
|
@@ -134,6 +146,10 @@ export default create({
|
|
|
|
|
|
widthRef.value = res.width;
|
|
|
|
|
|
+ // 设置基础字符
|
|
|
+ const bsize = pxToNumber(res.fontSize);
|
|
|
+ widthBase = [bsize, bsize * 0.72, bsize * 0.53, bsize * 0.4, bsize * 0.75];
|
|
|
+
|
|
|
calcEllipse();
|
|
|
}
|
|
|
)
|
|
|
@@ -151,93 +167,95 @@ export default create({
|
|
|
|
|
|
if (props.direction === 'middle') {
|
|
|
const end = props.content.length;
|
|
|
- tailorMiddle(
|
|
|
- [0, rowNum * (Number(props.rows) + 0.5)],
|
|
|
- [props.content.length - rowNum * (Number(props.rows) + 0.5), end]
|
|
|
- );
|
|
|
+ ellipsis.leading = tailorContent(0, rowNum * (Number(props.rows) + 0.5), 'end');
|
|
|
+ ellipsis.tailing = tailorContent(props.content.length - rowNum * (Number(props.rows) + 0.5), end, 'start');
|
|
|
} else if (props.direction === 'end') {
|
|
|
const end = rowNum * (Number(props.rows) + 0.5);
|
|
|
- tailor(0, end);
|
|
|
+ ellipsis.leading = tailorContent(0, end);
|
|
|
} else {
|
|
|
const start = props.content.length - rowNum * (Number(props.rows) + 0.5) - 5;
|
|
|
|
|
|
- tailor(start, props.content.length);
|
|
|
+ ellipsis.tailing = tailorContent(start, props.content.length);
|
|
|
}
|
|
|
+
|
|
|
+ // 进行兜底判断,是否符合要求
|
|
|
+ assignContent();
|
|
|
+ setTimeout(() => {
|
|
|
+ verifyEllipsis();
|
|
|
+ }, 100);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // 计算 start/end 省略
|
|
|
- const tailor = async (left: number, right: number) => {
|
|
|
- const actionText = state.expanded ? props.collapseText : props.expandText;
|
|
|
- const end = props.content.length;
|
|
|
-
|
|
|
- if (right - left <= 1) {
|
|
|
- state.exceeded = true;
|
|
|
- if (props.direction === 'end') {
|
|
|
- (ellipsis as any).value = {
|
|
|
- leading: props.content.slice(0, left) + props.symbol
|
|
|
- };
|
|
|
+ // 验证省略号
|
|
|
+ const verifyEllipsis = async () => {
|
|
|
+ const refe = await useTaroRect(rootContain, Taro);
|
|
|
+ if (refe && refe.height && refe.height > maxHeight) {
|
|
|
+ if (props.direction == 'end') {
|
|
|
+ ellipsis.leading = ellipsis.leading?.slice(0, ellipsis.leading.length - 1);
|
|
|
} else {
|
|
|
- (ellipsis as any).value = {
|
|
|
- tailing: props.symbol + props.content.slice(right, end)
|
|
|
- };
|
|
|
+ ellipsis.tailing = ellipsis.tailing?.slice(1, ellipsis.tailing.length);
|
|
|
}
|
|
|
- return false;
|
|
|
+
|
|
|
+ assignContent();
|
|
|
+ setTimeout(() => {
|
|
|
+ verifyEllipsis();
|
|
|
+ }, 100);
|
|
|
}
|
|
|
- const middle = Math.round((left + right) / 2);
|
|
|
+ };
|
|
|
+
|
|
|
+ const assignContent = () => {
|
|
|
+ contantCopy.value = `${ellipsis.leading || ''}${ellipsis.leading ? props.symbol : ''}${props.expandText || ''}${
|
|
|
+ ellipsis.tailing ? props.symbol : ''
|
|
|
+ }${ellipsis.tailing || ''}`;
|
|
|
+ };
|
|
|
+ // 计算省略号
|
|
|
+ const tailorContent = (left: number, right: number, type = '') => {
|
|
|
+ const threeDotWidth = symbolTextWidth;
|
|
|
|
|
|
- if (props.direction === 'end') {
|
|
|
- contantCopy.value = props.content.slice(0, middle) + props.symbol + actionText;
|
|
|
+ const direc = props.direction === 'middle' && type ? type : props.direction;
|
|
|
+
|
|
|
+ state.exceeded = true;
|
|
|
+
|
|
|
+ let widthPart = -1;
|
|
|
+ let start = left;
|
|
|
+ let end = right;
|
|
|
+ let cutoff = 0;
|
|
|
+ let marking = 0;
|
|
|
+
|
|
|
+ if (direc === 'end') {
|
|
|
+ marking = start;
|
|
|
+ cutoff = end;
|
|
|
} else {
|
|
|
- contantCopy.value = actionText + props.symbol + props.content.slice(middle, end);
|
|
|
+ marking = end;
|
|
|
+ cutoff = start;
|
|
|
}
|
|
|
- setTimeout(async () => {
|
|
|
- const refe = await useTaroRect(rootContain, Taro);
|
|
|
- if (refe.height <= maxHeight) {
|
|
|
- if (props.direction === 'end') {
|
|
|
- tailor(middle, right);
|
|
|
- } else {
|
|
|
- tailor(left, middle);
|
|
|
- }
|
|
|
+
|
|
|
+ let contentWidth = pxToNumber(widthRef.value) * Number(props.rows) - threeDotWidth;
|
|
|
+ let contentPartWidth = props.direction === 'middle' ? contentWidth / 2 : contentWidth;
|
|
|
+
|
|
|
+ while (widthPart < contentPartWidth) {
|
|
|
+ const zi = props.content[marking];
|
|
|
+ if (chineseReg.test(zi)) {
|
|
|
+ widthPart = Number(widthPart + widthBase[0]);
|
|
|
+ } else if (letterUpperReg.test(zi)) {
|
|
|
+ widthPart = Number(widthPart + widthBase[1]);
|
|
|
+ } else if (letterLowerReg.test(zi)) {
|
|
|
+ widthPart = Number(widthPart + widthBase[2]);
|
|
|
+ } else if (digitReg.test(zi)) {
|
|
|
+ widthPart = Number(widthPart + widthBase[3]);
|
|
|
} else {
|
|
|
- if (props.direction === 'end') {
|
|
|
- tailor(left, middle);
|
|
|
- } else {
|
|
|
- tailor(middle, right);
|
|
|
- }
|
|
|
+ widthPart = Number(widthPart + widthBase[4]);
|
|
|
}
|
|
|
- }, 10);
|
|
|
- };
|
|
|
- // 计算 middle 省略
|
|
|
- const tailorMiddle = async (leftPart: [number, number], rightPart: [number, number]) => {
|
|
|
- const actionText = state.expanded ? props.collapseText : props.expandText;
|
|
|
- const end = props.content.length;
|
|
|
- if (leftPart[1] - leftPart[0] <= 1 && rightPart[1] - rightPart[0] <= 1) {
|
|
|
- state.exceeded = true;
|
|
|
- (ellipsis as any).value = {
|
|
|
- leading: props.content.slice(0, leftPart[0]) + props.symbol,
|
|
|
- tailing: props.symbol + props.content.slice(rightPart[1], end)
|
|
|
- };
|
|
|
- return false;
|
|
|
+ cutoff = marking;
|
|
|
+
|
|
|
+ direc === 'end' ? marking++ : marking--;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (direc === 'end') {
|
|
|
+ return props.content.slice(0, cutoff);
|
|
|
+ } else {
|
|
|
+ return props.content.slice(cutoff, end);
|
|
|
}
|
|
|
- const leftPartMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2);
|
|
|
- const rightPartMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2);
|
|
|
-
|
|
|
- contantCopy.value =
|
|
|
- props.content.slice(0, leftPartMiddle) +
|
|
|
- props.symbol +
|
|
|
- actionText +
|
|
|
- props.symbol +
|
|
|
- props.content.slice(rightPartMiddle, end);
|
|
|
-
|
|
|
- setTimeout(async () => {
|
|
|
- const refe = await useTaroRect(rootContain, Taro);
|
|
|
- if (refe.height <= maxHeight) {
|
|
|
- tailorMiddle([leftPartMiddle, leftPart[1]], [rightPart[0], rightPartMiddle]);
|
|
|
- } else {
|
|
|
- tailorMiddle([leftPart[0], leftPartMiddle], [rightPartMiddle, rightPart[1]]);
|
|
|
- }
|
|
|
- }, 10);
|
|
|
};
|
|
|
|
|
|
const pxToNumber = (value: string | null) => {
|
|
|
@@ -266,9 +284,11 @@ export default create({
|
|
|
...toRefs(state),
|
|
|
root,
|
|
|
rootContain,
|
|
|
+ symbolContain,
|
|
|
ellipsis,
|
|
|
classes,
|
|
|
contantCopy,
|
|
|
+ symbolText,
|
|
|
clickHandle,
|
|
|
handleClick,
|
|
|
refRandomId,
|