123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- <template>
- <view class="t-wrap">
- <!-- 虚拟view用于计算,计算完成则消失 -->
- <view class="t-txt-hide" :id="hid" v-if="!isCompute" :style="[computeStyle(0),{'text-align':oneRowTextAlign}]">
- <text space="nbsp">{{testContent?testContent:content}}{{showSymbol?'...':''}}</text><text
- v-if="expandText && collapseText && showSymbol" class="t-button">
- {{expandText}}
- </text><text></text>
- </view>
- <!-- 真实显示的内容 -->
- <view class="t-ellipsis" :id="id"
- :style="[!isCompute?computeStyle(1):computeStyle(2),{'text-align':oneRowTextAlign}]" @click="contentClick">
- <text space="nbsp">{{(!isCompute || expand)?content:(actualContent+(showSymbol?'...':''))}}</text><text
- v-if="expandText && collapseText && showSymbol" class="t-button" @click.stop="changeCollapse"
- :style="{'color':actionFontColor}">{{!expand?expandText:collapseText}}</text><text></text>
- </view>
- <!-- 这里加入了自定义的动态骨架屏友好反馈 -->
- <view v-if="!isCompute && rows>0" class="t-skeleton">
- <view class="skeletons" v-for="(item,index) in rows" :key="index">
- <view class="empty"></view>
- </view>
- </view>
- </view>
- </template>
- <script>
- //为了兼容部分老机型,增加扰动因子参数
- const factor = 5;
- export default {
- name: "KevyEllipsis",
- props: {
- /**
- * 文本唯一标识符,非必填
- */
- textId: {
- type: [String, Number],
- default: ''
- },
- /**
- * 文本内容,默认''
- */
- content: {
- type: String,
- default: ''
- },
- /**
- * 字体大小,单位rpx,默认28
- */
- fontSize: {
- type: [String, Number],
- default: 28
- },
- /**
- * 字体颜色,默认#666666
- */
- fontColor: {
- color: String,
- default: '#666666'
- },
- /**
- * 收起操作的文案,默认''
- */
- collapseText: {
- type: String,
- default: ''
- },
- /**
- * 展开操作的文案,默认''
- */
- expandText: {
- type: String,
- default: ''
- },
- /**
- * 收起、展开操作文字颜色,默认'#007aff'
- */
- actionFontColor: {
- color: String,
- default: '#007aff'
- },
- /**
- * 展示行数,默认1
- */
- rows: {
- type: Number,
- default: 1
- },
- /**
- * 只有一行时文本对齐方式,支持left、right、justify
- */
- oneRowTextAlign: {
- type: String,
- default: "justify"
- },
- },
- data() {
- return {
- //是否展开
- expand: false,
- //是否已计算
- isCompute: false,
- //内容高度
- h: undefined,
- //内容宽度
- w: undefined,
- //实际显示内容
- actualContent: '',
- //高度探测内容
- testContent: undefined,
- //是否显示省略号
- showSymbol: false,
- //hid和id,唯一标识符
- hid: 'hid' + Math.random().toString(36).substr(2),
- id: 'id' + Math.random().toString(36).substr(2),
- };
- },
- mounted() {
- this.$nextTick(() => {
- this.initEllipsis();
- })
- },
- computed: {
- //动态计算组件样式
- computeStyle() {
- return b => {
- let lines = this.rows > 0 ? this.rows : 1;
- let obj = {};
- if (b == 1) {
- obj = {
- '-webkit-line-clamp': lines,
- 'display': '-webkit-box',
- 'text-overflow': 'ellipsis',
- 'overflow': 'hidden',
- '-webkit-box-orient': 'vertical'
- };
- } else if (b == 2) {
- obj = {
- 'position': 'relative',
- 'left': '0rpx',
- ...obj
- };
- }
- return {
- 'font-size': this.fontSize + 'rpx',
- 'color': this.fontColor,
- ...obj
- }
- }
- }
- },
- watch: {
- content(newVal, oldVal) {
- this.expand=false;
- this.isCompute=false;
- this.h=undefined;
- this.w=undefined;
- this.actualContent='';
- this.showSymbol=false;
- this.initEllipsis();
- }
- },
- methods: {
- //初始化
- initEllipsis() {
- if (this.content?.length > 0) {
- this.$nextTick(() => {
- this.init(this, () => {
- this.compute(this);
- })
- })
-
- }
- },
- //收起展开状态切换
- changeCollapse() {
- this.expand = !this.expand;
- },
- //文本点击事件
- contentClick() {
- this.$emit('contentClick', this.textId);
- },
- //组件参数初始化
- init($this, callback, isali) {
- if (isali) {
- uni.createSelectorQuery().in().select('#' + $this.id).boundingClientRect(d => {
- $this.h = Number(d.height.toFixed(1));
- $this.w = Number(d.width.toFixed(1));
- if (callback) {
- callback()
- }
- }).exec();
- } else {
- uni.createSelectorQuery().in($this).select('#' + $this.id).boundingClientRect(d => {
- $this.h = Number(d.height.toFixed(1));
- $this.w = Number(d.width.toFixed(1));
- if (callback) {
- callback()
- }
- }).exec();
- }
- },
- //动态计算组件内容
- computeContent($this, isali, dr) {
- $this.$nextTick(() => {
- $this.getH($this, isali, (ch) => {
- if (ch - factor > $this.h) {
- if (dr === -1) {
- $this.testContent = $this.content.substring(0, $this.testContent.length -
- 1);
- $this.computeContent($this, isali, dr);
- } else {
- $this.actualContent = $this.content.substring(0, $this.testContent.length -
- 1);
- $this.isCompute = true;
- }
- } else {
- if (dr === -1) {
- $this.actualContent = $this.testContent;
- $this.isCompute = true;
- } else {
- $this.testContent = $this.content.substring(0, $this.testContent.length +
- 1);
- $this.computeContent($this, isali, dr);
- }
- }
- })
- });
- },
- //计算工具方法
- compute($this, isali) {
- let {
- rows,
- fontSize,
- content,
- h,
- w
- } = $this;
- $this.testContent = content;
- $this.$nextTick(() => {
- $this.getH($this, isali, (ch) => {
- if (ch - factor > h) {
- let lh = h / rows;
- let fn = Math.floor(w / $this.rpx2px(fontSize));
- let sfn = fn * rows;
- let i = $this.fontNum(content, sfn * 2 - ($this.expandText ? $this.fontNum(
- $this.expandText) :
- 0) - 3);
- $this.showSymbol = true;
- $this.testContent = content.substring(0, i);
- $this.$nextTick(() => {
- $this.getH($this, isali, (ch1) => {
- if (ch1 - factor > h) {
- $this.testContent = content.substring(0, $this
- .testContent.length - 1);
- $this.computeContent($this, isali, -1);
- } else {
- $this.testContent = content.substring(0, $this
- .testContent.length + 1);
- $this.computeContent($this, isali, 1);
- }
- });
- });
- } else {
- $this.isCompute = true;
- $this.actualContent = content;
- }
- })
- });
- },
- //动态计算字符数
- fontNum(val, limit) {
- let c = 0;
- for (let i = 0; i < val.length; i++) {
- let a = val.charAt(i);
- if (a.match(/[^\x00-\xff]/ig) != null) {
- if (limit) {
- if (c + 2 > limit) {
- return i;
- } else {
- c += 2;
- }
- } else {
- c += 2;
- }
- } else {
- if (limit) {
- if (c + 1 > limit) {
- return i;
- } else {
- c += 1;
- }
- } else {
- c += 1;
- }
- }
- }
- if (!limit) {
- return c;
- }
- },
- rpx2px(rpx) {
- return uni.getSystemInfoSync().windowWidth * Number(rpx) / 750;
- },
- getH($, isali, callback) {
- if (isali) {
- uni.createSelectorQuery().in().select('#' + $.hid).fields({
- size: true
- }, d => {
- if(d && d.height){
- callback(Number(d.height.toFixed(1)));
- }
- }).exec();
- } else {
- uni.createSelectorQuery().in($).select('#' + $.hid).fields({
- size: true
- }, d => {
- if(d && d.height){
- callback(Number(d.height.toFixed(1)));
- }
- }).exec();
- }
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .t-wrap {
- width: 100%;
- box-sizing: border-box;
- position: relative;
- }
- .t-txt-hide {
- box-sizing: border-box;
- word-break: break-all;
- position: absolute;
- top: 999999px;
- left: 999999px;
- z-index: -1000;
- top: 0rpx;
- width: 100%;
- margin: 0rpx;
- text-align: justify;
- white-space: pre-line;
- line-height: 1.5 !important;
- .t-button {
- float: right;
- clear: both;
- }
- }
- .t-ellipsis {
- text-align: justify;
- box-sizing: border-box;
- width: 100%;
- word-break: break-all;
- position: relative;
- left: 99999px;
- white-space: pre-line;
- line-height: 1.5 !important;
- .t-button {
- float: right;
- clear: both;
- font-weight: 500;
- }
- }
- .t-skeleton {
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- position: absolute;
- top: 0rpx;
- left: 0rpx;
- }
- .skeletons:first-child {
- margin-top: 0rpx !important;
- }
- .skeletons {
- position: relative;
- display: block;
- overflow: hidden;
- width: 100%;
- height: 32rpx;
- margin-top: 12rpx;
- background-color: rgba(0, 0, 0, 0.06);
- box-sizing: border-box;
- }
- .skeletons .empty {
- display: block;
- position: absolute;
- width: 100%;
- height: 100%;
- -webkit-transform: translateX(-100%);
- transform: translateX(-100%);
- background: linear-gradient(90deg, transparent, rgba(216, 216, 216, 0.753), transparent);
- -webkit-animation: loading .8s infinite;
- animation: loading .8s infinite;
- }
- @keyframes loading {
- 100% {
- -webkit-transform: translateX(100%);
- transform: translateX(100%);
- }
- }
- </style>
|