当前位置: 首页 > news >正文

uni-app + Vue3 实现折叠文本(超出省略 + 展开收起)

uni-app + Vue3 实现折叠文本(超出省略 + 展开收起)

实现一个“最多展示 N 行文本 + 展开 / 收起”组件,支持 H5 / 微信小程序 / App,适用于 uni-app + Vue3 + TypeScript

✅ 功能说明

  • 自动识别文本是否超出
  • 超出时显示“展开 ▼ / 收起 ▲”按钮
  • 支持自定义显示行数(默认 4 行)
  • 全平台通用(H5 / 小程序 / App)
  • TypeScript 类型友好

✅ 最终效果

使用方式如下:

<ClampText :text="item.name" :lines="4" />

短文本:不显示按钮
长文本:显示“展开 / 收起”并可切换


✅ 组件文件:components/ClampText.vue

建议放到 components/ClampText.vue

<template><view class="clamp-text-wrapper"><!-- 文本区域,折叠时应用 -webkit-line-clamp,多行省略 --><view:id="textId"data-clamp-textclass="text":class="{ [`clamp-${lines}-lines`]: !expanded }">{{ text }}</view><!-- 超出时显示展开 / 收起按钮 --><viewv-if="overflow"class="toggle-btn"@tap="toggle">{{ expanded ? '收起 ▲' : '展开 ▼' }}</view></view>
</template><script setup lang="ts">
import { ref, watch, nextTick } from 'vue'// Props 类型定义
interface Props {text: stringlines?: number
}// 设置默认显示行数为 4 行
const props = withDefaults(defineProps<Props>(), {lines: 4
})const expanded = ref(false) // 当前是否展开
const overflow = ref(false) // 文本是否超出// 为每个组件生成唯一 ID,方便获取高度
const textId = `text-${Math.random().toString(36).slice(2)}`/*** 把 rpx 转成 px(基于当前设备宽度)* 1 rpx = windowWidth / 750 px*/
const rpxToPx = (rpx: number): number => {try {const info = uni.getSystemInfoSync()const winWidth = info.windowWidth || 375return (rpx * winWidth) / 750} catch (e) {// 兜底值return rpx * (375 / 750)}
}/*** 检测一组带 .desc 的元素是否超过 clampLines* - clampRpxLine: CSS 中设置的每行行高(单位 rpx),务必与 CSS 中 line-height 保持一致* - clampLines: 行数(这里是 4)*/
const detectOverflow = async (clampRpxLine = 30, clampLines = 4) => {await nextTick() // 等待 DOM 渲染return new Promise<void>((resolve) => {const lineHeightPx = rpxToPx(clampRpxLine)const threshold = lineHeightPx * clampLines + 1 // +1 像素容错const query = uni.createSelectorQuery()// 选择所有 .desc 元素(跨端兼容)query.selectAll(`#${textId}`).boundingClientRect((rects: Array<{ height: number }>) => {if (!rects || !rects.length) {resolve()return}rects.forEach((rect, i) => {const h = rect?.height || 0// 如果高度大于阈值,认为超过 clampLinesoverflow.value[i] = h > threshold})resolve()}).exec()})
}/* 首次检测,和 menu 变化时重新检测 */
onMounted(async () => {// 等待数据可能异步加载完:如果你的 menu 是异步加载,确保在获取后再调用 detectOverflowawait nextTick()// 这里以 30rpx 行高(对应 CSS)和 4 行为例detectOverflow(20, 4)
})// 切换展开 / 收起
const toggle = () => {expanded.value = !expanded.valuenextTick(detectOverflow)
}// 文本变化时重新计算
watch(() => props.text, () => {expanded.value = falsenextTick(() => setTimeout(() => detectOverflow(20, 4), 50))
}, { immediate: true })
</script><style scoped lang="scss">
/* 外层包裹 */
.clamp-text-wrapper {margin-bottom: 10px;
}/* 文本样式 */
.text {font-size: 14px;line-height: 24px; /* 与 JS 检测高度保持一致 */
}/* 行数裁剪规则(支持 1-5 行,可扩展) */
.clamp-1-lines { display: -webkit-box; -webkit-line-clamp: 1; overflow: hidden; -webkit-box-orient: vertical; }
.clamp-2-lines { display: -webkit-box; -webkit-line-clamp: 2; overflow: hidden; -webkit-box-orient: vertical; }
.clamp-3-lines { display: -webkit-box; -webkit-line-clamp: 3; overflow: hidden; -webkit-box-orient: vertical; }
.clamp-4-lines { display: -webkit-box; -webkit-line-clamp: 4; overflow: hidden; -webkit-box-orient: vertical; }
.clamp-5-lines { display: -webkit-box; -webkit-line-clamp: 5; overflow: hidden; -webkit-box-orient: vertical; }/* 展开 / 收起按钮 */
.toggle-btn {font-size: 14px;margin-top: 4px;color: #007aff; /* iOS 风格蓝色 */
}
</style>

✅ 使用例子

<template><view><ClampTextv-for="(item, index) in list":key="index":text="item.text":lines="4"/></view>
</template><script setup lang="ts">
const list = ref([{ text: "短文本,不会显示按钮" },{ text: "一段非常非常非常非常非常非常长的文本……演示折叠效果……" }
])
</script>

🎯 亮点与细节

优势说明
✅ 全平台通用H5 / 小程序 / App
✅ 自动判断超出短文不出现按钮
✅ TS 强类型更符合工程化
✅ 真实高度检测不依赖纯 CSS
✅ nextTick + 延迟解决渲染滞后

📦 下一步可扩展

🚀 v-clamp 指令版教程

✨ 功能目标

  • 显示指定行数(如:3 行、4 行)
  • 自动检测文本是否溢出
  • 已溢出 → 显示省略号 + “展开”按钮
  • 展开后 → 显示完整内容 + “收起”按钮
  • “展开/收起” 按钮紧跟省略号之后
  • 可配置显示行数、按钮文案,支持 i18n
  • ✅ 不侵入 DOM 结构(比组件更优雅)

📂 目录结构

src/└─ directives/└─ clamp.ts
main.ts

✅ 最终使用效果

<viewv-clamp="{lines: 4,moreText: '展开',lessText: '收起'}"
>{{ longText }}
</view>

效果:默认最多显示 4 行,后面出现 ... 展开,点击变 收起


🧠 实现思路

步骤说明
1️⃣获取原始文本内容并记录
2️⃣先限制行数 -webkit-line-clamp
3️⃣计算 scrollHeight > clientHeight → 判断是否溢出
4️⃣如果溢出 → 显示省略号 + 展开按钮
5️⃣点击展开 → 取消 line-clamp,展示全文
6️⃣点击收起 → 恢复行数限制 + 省略号

UI 和逻辑全部由 自定义指令 控制,不改变模板结构。


🧩 v-clamp 指令源码 + 注释

📁 src/directives/clamp.ts

import { DirectiveBinding } from "vue";interface ClampOptions {lines?: number;      // 最大显示行数moreText?: string;   // 展开按钮文案lessText?: string;   // 收起按钮文案
}function applyClamp(el: HTMLElement, options: ClampOptions) {const { lines = 3, moreText = "展开", lessText = "收起" } = options;const fullText = el.innerText; // 保存原始内容let isExpanded = false;        // 展开状态const computeClamp = () => {if (isExpanded) {// ✅ 展开状态,显示全文el.innerHTML = `<span>${fullText}</span><a style="color:#007aff;margin-left:6px;">${lessText}</a>`;el.querySelector("a")?.addEventListener("click", () => {isExpanded = false;computeClamp();});} else {// ✅ 折叠状态,应用 CSS 行数限制el.style.display = "-webkit-box";el.style.webkitBoxOrient = "vertical";el.style.webkitLineClamp = String(lines);el.style.overflow = "hidden";el.innerHTML = `<span class="clamp-text">${fullText}</span><a style="color:#007aff;margin-left:6px;">${moreText}</a>`;const span = el.querySelector("span") as HTMLElement;// ⭐ 检测是否真的溢出,避免没超过还显示按钮setTimeout(() => {const needClamp = span.scrollHeight > span.clientHeight;if (!needClamp) {el.innerHTML = `<span>${fullText}</span>`;return;}// ✅ 注册“展开”按钮事件const btn = el.querySelector("a");btn?.addEventListener("click", () => {isExpanded = true;el.style.webkitLineClamp = "unset";computeClamp();});});}};computeClamp();
}export default {mounted(el: HTMLElement, binding: DirectiveBinding) {applyClamp(el, binding.value || {});},updated(el: HTMLElement, binding: DirectiveBinding) {applyClamp(el, binding.value || {});}
};

🌐 全局注册指令

📁 main.ts

import { createApp } from "vue";
import App from "./App.vue";
import clamp from "@/directives/clamp";const app = createApp(App);app.directive("clamp", clamp);app.mount("#app");

✅ 页面实际使用

<template><view class="text-box"v-clamp="{lines: 4,moreText: '展开',lessText: '收起'}">{{ longText }}</view>
</template><script setup lang="ts">
const longText = `
Uni-app 是一个使用 Vue 语法开发所有前端应用的框架,
可以编译到微信小程序、H5、APP 等多个端。
使用自定义指令能优雅实现文本折叠展开效果。
`;
</script>

🔍 原理讲解总结

技术点说明
CSS -webkit-line-clamp实现多行省略
JS scrollHeight > clientHeight判断文本是否溢出
动态 innerHTML动态插入按钮
事件监听控制展开/收起
自定义指令 directive让模板更干净,易复用

🧪 测试边界

情况效果
文本 < N 行不显示按钮 ✅
文本 > N 行省略 + 展开 ✅
展开后显示全文 ✅
切换语言按钮可更换 ✅

http://www.dtcms.com/a/574231.html

相关文章:

  • 云南微网站搭建wordpress插件安装不
  • 汽车行业网站设计chrome google
  • 好用的云电脑!手机怎么用UU远程云电脑玩电脑游戏?
  • 网站开发安装网站原型图软件
  • 坑#Spring Cloud Gateway#DataBufferLimitException
  • 15年做哪些网站能致富网页升级访问紧急通知狼
  • ping: baidu.com: 域名解析暂时失败
  • 上海网站设计方法有哪些网站上可以做试卷
  • 网站建设项目立项登记 表自己家的电脑宽带50m做网站服务器
  • 宜宾公司做网站建设一个电子文学网站资金多少
  • 效率提升的声音助手——工业物联网中的智能化变革
  • 普罗宇宙发布大白机器人2.0 及灵巧手,携手京东加速全球化落地
  • Java 集合框架:List 体系与实现类深度解析
  • 阿里云 ip 网站哈尔滨行业网站建设策划
  • 注册了网站怎么建设网站视听内容建设
  • 泉州专业做网站网上做网站怎么防止被骗
  • 使用 ECharts + ECharts-GL 生成 3D 环形图
  • 做电影网站视频放在那里南阳做那个网站好
  • 美德的网站建设局网站建设招标
  • 学校网站的建设论文怎么建网站做推广
  • 第四阶段通讯开发-7:TCPListener和TCPClient
  • 中国最权威的网站排名电脑网站安全证书有问题如何解决
  • 网站建设实训小结在线网站流量查询
  • 深圳网站建设自己人做1688网站到哪里找图片
  • C++ —— list
  • xv6 附录A
  • 【设计题】如何实现一个线程安全的缓存?
  • 网站透明效果wordpress广告插件中文
  • 网站建设费用进会计什么科目界面设计与制作是做什么的
  • 中小企业网站建设如何c 网站开发教程