diff --git a/packages/vant/src/text-ellipsis/README.md b/packages/vant/src/text-ellipsis/README.md
index 5f1592914..aa0bd6aeb 100644
--- a/packages/vant/src/text-ellipsis/README.md
+++ b/packages/vant/src/text-ellipsis/README.md
@@ -81,17 +81,64 @@ export default {
};
```
+### Custom Collapse Position
+
+- Collapse the beginning part of the content:
+
+```html
+
+```
+
+```js
+export default {
+ setup() {
+ const text =
+ "That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.";
+ return { text };
+ },
+};
+```
+
+- Collapse the middle part of the content:
+
+```html
+
+```
+
+```js
+export default {
+ setup() {
+ const text =
+ "That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.";
+ return { text };
+ },
+};
+```
+
## API
### Props
-| Attribute | Description | Type | Default |
-| ------------- | ------------------------ | ------------------ | ------- |
-| rows | Number of rows displayed | _number \| string_ | `1` |
-| content | The text displayed | _string_ | - |
-| expand-text | Expand operation text | _string_ | - |
-| collapse-text | Collapse operation text | _string_ | - |
-| dots `v4.2.0` | Text content of ellipsis | _string_ | `'...'` |
+| Attribute | Description | Type | Default |
+| --- | --- | --- | --- |
+| rows | Number of rows displayed | _number \| string_ | `1` |
+| content | The text displayed | _string_ | - |
+| expand-text | Expand operation text | _string_ | - |
+| collapse-text | Collapse operation text | _string_ | - |
+| dots `v4.2.0` | Text content of ellipsis | _string_ | `'...'` |
+| position `v4.6.2` | Can be set to `start` `middle` | _string_ | `'end'` |
### Events
diff --git a/packages/vant/src/text-ellipsis/README.zh-CN.md b/packages/vant/src/text-ellipsis/README.zh-CN.md
index 9bfc2f038..44b91b2dc 100644
--- a/packages/vant/src/text-ellipsis/README.zh-CN.md
+++ b/packages/vant/src/text-ellipsis/README.zh-CN.md
@@ -76,17 +76,66 @@ export default {
};
```
+### 自定义省略位置
+
+通过设置 `position` 控制省略位置。
+
+- 头部省略:
+
+```html
+
+```
+
+```js
+export default {
+ setup() {
+ const text =
+ '那一天我二十一岁,在我一生的黄金时代。我有好多奢望。我想爱,想吃,还想在一瞬间变成天上半明半暗的云。后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消失,最后变得像挨了锤的牛一样。可是我过二十一岁生日时没有预见到这一点。我觉得自己会永远生猛下去,什么也锤不了我。';
+ return { text };
+ },
+};
+```
+
+- 中部省略:
+
+```html
+
+```
+
+```js
+export default {
+ setup() {
+ const text =
+ '那一天我二十一岁,在我一生的黄金时代。我有好多奢望。我想爱,想吃,还想在一瞬间变成天上半明半暗的云。后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消失,最后变得像挨了锤的牛一样。可是我过二十一岁生日时没有预见到这一点。我觉得自己会永远生猛下去,什么也锤不了我。';
+ return { text };
+ },
+};
+```
+
## API
### Props
-| 参数 | 说明 | 类型 | 默认值 |
-| ------------- | ---------------- | ------------------ | ------- |
-| rows | 展示的行数 | _number \| string_ | `1` |
-| content | 需要展示的文本 | _string_ | - |
-| expand-text | 展开操作的文案 | _string_ | - |
-| collapse-text | 收起操作的文案 | _string_ | - |
-| dots `v4.2.0` | 省略号的文本内容 | _string_ | `'...'` |
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| rows | 展示的行数 | _number \| string_ | `1` |
+| content | 需要展示的文本 | _string_ | - |
+| expand-text | 展开操作的文案 | _string_ | - |
+| collapse-text | 收起操作的文案 | _string_ | - |
+| dots `v4.2.0` | 省略号的文本内容 | _string_ | `'...'` |
+| position `v4.6.2` | 省略位置,可选值为 `start` `middle` | _string_ | `'end'` |
### Events
diff --git a/packages/vant/src/text-ellipsis/TextEllipsis.tsx b/packages/vant/src/text-ellipsis/TextEllipsis.tsx
index efdd56a0d..82f09a977 100644
--- a/packages/vant/src/text-ellipsis/TextEllipsis.tsx
+++ b/packages/vant/src/text-ellipsis/TextEllipsis.tsx
@@ -1,6 +1,7 @@
import {
ref,
watch,
+ computed,
onMounted,
defineComponent,
type ExtractPropTypes,
@@ -20,6 +21,7 @@ export const textEllipsisProps = {
content: makeStringProp(''),
expandText: makeStringProp(''),
collapseText: makeStringProp(''),
+ position: makeStringProp('end'),
};
export type TextEllipsisProps = ExtractPropTypes;
@@ -37,6 +39,10 @@ export default defineComponent({
const hasAction = ref(false);
const root = ref();
+ const actionText = computed(() =>
+ expanded.value ? props.expandText : props.collapseText
+ );
+
const pxToNum = (value: string | null) => {
if (!value) return 0;
const match = value.match(/^\d*(\.\d*)?/);
@@ -70,33 +76,104 @@ export default defineComponent({
container: HTMLDivElement,
maxHeight: number
) => {
- const { dots, content, expandText } = props;
+ const { content, position, dots } = props;
+ const end = content.length;
- let left = 0;
- let right = content.length;
- let res = -1;
+ const calcEllipse = () => {
+ // calculate the former or later content
+ const tail = (left: number, right: number): string => {
+ if (right - left <= 1) {
+ if (position === 'end') {
+ return content.slice(0, left) + dots;
+ }
+ return dots + content.slice(right, end);
+ }
- while (left <= right) {
- const mid = Math.floor((left + right) / 2);
- container.innerText = content.slice(0, mid) + dots + expandText;
- if (container.offsetHeight <= maxHeight) {
- left = mid + 1;
- res = mid;
- } else {
- right = mid - 1;
+ const middle = Math.round((left + right) >> 1);
+
+ // Set the interception location
+ if (position === 'end') {
+ container.innerText =
+ content.slice(0, middle) + dots + actionText.value;
+ } else {
+ container.innerText =
+ dots + content.slice(middle, end) + actionText.value;
+ }
+
+ // The height after interception still does not match the rquired height
+ if (container.offsetHeight > maxHeight) {
+ if (position === 'end') {
+ return tail(left, middle);
+ }
+ return tail(middle, right);
+ }
+
+ if (position === 'end') {
+ return tail(middle, right);
+ }
+
+ return tail(left, middle);
+ };
+
+ container.innerText = tail(0, end);
+ };
+
+ const middleTail = (
+ leftPart: [number, number],
+ rightPart: [number, number]
+ ): string => {
+ if (
+ leftPart[1] - leftPart[0] <= 1 &&
+ rightPart[1] - rightPart[0] <= 1
+ ) {
+ return (
+ content.slice(0, leftPart[1]) +
+ dots +
+ dots +
+ content.slice(rightPart[1], end)
+ );
}
- }
- return content.slice(0, res) + dots;
+
+ const leftMiddle = Math.floor((leftPart[0] + leftPart[1]) >> 1);
+ const rightMiddle = Math.ceil((rightPart[0] + rightPart[1]) >> 1);
+
+ container.innerText =
+ props.content.slice(0, leftMiddle) +
+ props.dots +
+ actionText.value +
+ props.dots +
+ props.content.slice(rightMiddle, end);
+
+ if (container.offsetHeight >= maxHeight) {
+ return middleTail(
+ [leftPart[0], leftMiddle],
+ [rightMiddle, rightPart[1]]
+ );
+ }
+
+ return middleTail(
+ [leftMiddle, leftPart[1]],
+ [rightPart[0], rightMiddle]
+ );
+ };
+
+ const middle = (0 + end) >> 1;
+ props.position === 'middle'
+ ? (container.innerText = middleTail([0, middle], [middle, end]))
+ : calcEllipse();
+ return container.innerText;
};
+ // Calculate the interceptional text
const container = cloneContainer();
if (!container) return;
-
const { paddingBottom, paddingTop, lineHeight } = container.style;
- const maxHeight =
+ const maxHeight = Math.ceil(
(Number(props.rows) + 0.5) * pxToNum(lineHeight) +
- pxToNum(paddingTop) +
- pxToNum(paddingBottom);
+ pxToNum(paddingTop) +
+ pxToNum(paddingBottom)
+ );
+
if (maxHeight < container.offsetHeight) {
hasAction.value = true;
text.value = calcEllipsisText(container, maxHeight);
@@ -121,7 +198,7 @@ export default defineComponent({
onMounted(calcEllipsised);
- watch(() => [props.content, props.rows], calcEllipsised);
+ watch(() => [props.content, props.rows, props.position], calcEllipsised);
useEventListener('resize', calcEllipsised);
diff --git a/packages/vant/src/text-ellipsis/demo/index.vue b/packages/vant/src/text-ellipsis/demo/index.vue
index 9cd4a1873..12fb62b0b 100644
--- a/packages/vant/src/text-ellipsis/demo/index.vue
+++ b/packages/vant/src/text-ellipsis/demo/index.vue
@@ -13,6 +13,9 @@ const t = useTranslate({
collapseText: '收起',
expandCollapse: '展开/收起',
customRows: '自定义展示行数',
+ collapsePosition: '自定义省略位置',
+ collapseStart: '头部省略',
+ collapseMiddle: '中部省略',
},
'en-US': {
text1:
@@ -25,6 +28,9 @@ const t = useTranslate({
collapseText: 'collapse',
expandCollapse: 'Expand/Collapse',
customRows: 'Customize Rows',
+ collapsePosition: 'Custom Collapse Position',
+ collapseStart: 'Head Area Collapse Position',
+ collapseMiddle: 'Middle Area Collapse Position',
},
});
@@ -50,6 +56,28 @@ const t = useTranslate({
:collapse-text="t('collapseText')"
/>
+
+
+
+
+
+
+
+
+
+