加载中...

微信小程序列表实现文字内容超出隐藏并显示全文/收起按钮

吴佳
2020-01-16 13:51:04
分类:小程序
163
3
0

前言

几个月没有发过文章了,最近确实挺忙!!这几个月一直在搞公司的一个小程序项目,所以一直没抽出时间,后面我会把遇到的问题及踩到的坑也会提出来,今天咱们不说踩过的坑,说一个比较常见及常用的功能,就是内容的展开和收起。可能大家就开始想了,这么简单一功能还用看别人教吗?咱们先废话不多说,先来看看这个是怎样的一个功能~

针对这个功能,产品的需求如下

由于我们项目是教育类产品所以这里这个功能会用在发动态这里,就像微信朋友圈那样,我们叫班级圈。
用户发班级圈时,可以发布文字内容、图片、视频,文字可以输入500个字。这样的话,如果列表一次展示全部文字,如果用户真有500个文字就基本一条动态占用了整屏高度,对于用户体验来说,用户一眼看到的东西太少了,所以产品就提出了需要折叠文字需求,并说明文字需要满6行的时候折叠,并显示全文按钮,当用户点击全文按钮时要展开所有内容,当点击收起按钮又变回折叠状态。

说完需求,现在来说说问题

首先,开发过小程序的朋友都知道,小程序内部是无法操作dom的,也就是说根本没有dom这个东西。
但是,需求是需要内容满足6行,并且满足6行后才显示全文按钮,才可以操作,如果是不满足六行的,是不应该显示全文按钮的,直接正常展示就行,这里问题就来了。

  1. 如何折叠文本,让文本超出隐藏?
  2. 如果小程序不能操作dom,如何判断显示全文按钮?

我的解决方案

第一个问题

其实第一个问题不是问题,因为我们都知道文本多行超出隐藏是可以直接使用css就能解决的,所以下面我就直接贴出实现代码

  display: -webkit-box;
  word-break: break-all;
  text-overflow: ellipsis;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 7;

第二个问题

下面我们来说说第二个问题,如何去判断满足6行就显示全文按钮。
虽说小程序是不能操作dom的,但是能不能获取到dom的一些信息呢?
答案是可以的,小程序API确实提供了一套能够去获取节点上的一些信息接口,接口名叫createSelectorQuery,创建一个选择器对象,通过选择器对象提供的方法去做一些操作,这里我们需要用到选择器对象的两个方法

  • select('css selector')
  • boundingClientRect(fn)

首先,通过select去选择我们需要获取信息的节点,参数是css的选择器,只能选择单个节点,如果需要选择多个节点可使用selectAll。然后再通过boundingClientRect方法查询布局的位置请求,相对于显示区域,以像素为单位。其功能类似于DOM的getBoundingClientRect
该方法参数可接受一个回调,回调参数接受一个查询到的信息对象,其对象有以下信息

  • id :节点ID
  • dataset: 节点的dataset
  • left: 节点的左边界坐标
  • right: 节点的右边界坐标
  • top:节点的上边界坐标
  • bottom:节点的下边界坐标
  • width: 节点的宽度
  • height: 节点的高度

下面是一个完整示例

Page({
  getRect () {
    wx.createSelectorQuery().select('#the-id').boundingClientRect(function(rect){
      rect.id      // 节点的ID
      rect.dataset // 节点的dataset
      rect.left    // 节点的左边界坐标
      rect.right   // 节点的右边界坐标
      rect.top     // 节点的上边界坐标
      rect.bottom  // 节点的下边界坐标
      rect.width   // 节点的宽度
      rect.height  // 节点的高度
    }).exec()
  },
  getAllRects () {
    wx.createSelectorQuery().selectAll('.a-class').boundingClientRect(function(rects){
      rects.forEach(function(rect){
        rect.id      // 节点的ID
        rect.dataset // 节点的dataset
        rect.left    // 节点的左边界坐标
        rect.right   // 节点的右边界坐标
        rect.top     // 节点的上边界坐标
        rect.bottom  // 节点的下边界坐标
        rect.width   // 节点的宽度
        rect.height  // 节点的高度
      })
    }).exec()
  }
})

下面说一下这个exec(),它的作用是执行所有的请求,请求结果按请求次序构成数组,在callback的第一个参数中返回出来。
以上说完了获取节点信息的方法,可能大家心中会有疑问,为什么我要提这些东西?因为我们需要判断用户现在的文本高度,所以需要获取当前用户发布的文字内容高度,通过高度去评判内容是否满足来6行。

实现

第一实现方式

我发布了一个超过六行文本动态,然后分别打印出了在安卓和ios机器上的六行高度,这样就有了判断依据,先获取当前文本高度,如果等于了六行高度就显示全文按钮,下面是代码

  <text class="x-collapse-txt x-clr-dgray {{isOpen ? 'x-collapse-open' : ''}}">
    {{content}}
  </text>
  <view class="x-collapse-btn" catchtap="open" wx:if="{{height == 133 || height == 154}}">{{!isOpen ? '全文' : '收起'}}</view>
  <script>
  const {pageImgHost} = getApp()
  Component({
    options: {
      styleIsolation: 'isolated'
    },
    properties: {
      content: {
        type: String,
        value: ''
      }
    },
    data: {
      pageImgHost,
      height: '',
      isOpen: false
    },
    methods: {
      open () {
        this.setData({
          isOpen: !this.data.isOpen
        })
      },
      getHeight (cls) {
        return new Promise(resolve => {
          const query = wx.createSelectorQuery().in(this)
          query.select(cls).boundingClientRect(res => {
            resolve(res.height)
          }).exec()
        })
      }
    },
    attached () {
      this.getHeight('.x-collapse-txt').then(height => {
        this.setData({
          height
        })
      })
    }
  })
  </script>

现在如果像上面那样写是有兼容问题,就是安卓机型太多了,有的设备六行高度是不一样的,不可能给不一样高度的手机都获取一个高度写成判断,也就是说我们把判断依据的目标值写死了,这样是很要命的,所以这个就直接被pass了。

第二实现方式

经过上面暴露的问题,我们把判断的目标值写死了这样是不行的,那怎么样可以不写死呢?下面我是这样做的,我在结构里面多写了一个节点,然后定位到了看不见到地方,这个节点输入了很多文本内容,让撑满6行高度,然后通过获取高度的方式直接获取这个节点高度,这样一来我们就不管什么什么机型手机都能拿到当前这个机器上6行高度都目标值,这样一来就有了一个精准都目标值,就有了精确条件了。修改上方代码后,是这样的

  <text class="x-collapse-txt x-clr-dgray {{isOpen ? 'x-collapse-open' : ''}}">
    {{content}}
  </text>
  <text class="x-collapse-ctxt x-clr-dgray x-p">我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我我</text>
  <view class="x-collapse-btn" catchtap="open" wx:if="{{height === h}}">{{!isOpen ? '全文' : '收起'}}</view>

  <script>
  const {pageImgHost} = getApp()
  Component({
    options: {
      styleIsolation: 'isolated'
    },
    properties: {
      content: {
        type: String,
        value: ''
      }
    },
    data: {
      pageImgHost,
      height: '',
      isOpen: false,
      h: 0
    },
    methods: {
      open () {
        this.setData({
          isOpen: !this.data.isOpen
        })
      },
      getHeight (cls) {
        return new Promise(resolve => {
          const query = wx.createSelectorQuery().in(this)
          query.select(cls).boundingClientRect(res => {
            console.log(res.height)
            resolve(res.height)
          }).exec()
        })
      }
    },
    attached () {
      Promise.all([
        this.getHeight('.x-collapse-ctxt'),
        this.getHeight('.x-collapse-txt')
      ]).then(([h, height]) => {
        this.setData({
          h,
          height
        })
      })
    }
  })
  </script>

 <style>
  .x-collapse-txt, .x-collapse-ctxt {
    line-height: 40rpx;
    font-size: 28rpx;
    margin-top: -24rpx;
    display: -webkit-box;
    word-break: break-all;
    text-overflow: ellipsis;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 7;
  }
  .x-collapse-open {
    display: block;
  }
  .x-collapse-btn-wrap {
    display: flex;
    justify-content: flex-end;
  }
  .x-collapse-btn {
    font-size: 28rpx;
    width: 60rpx;
    line-height: 60rpx;
    color: #5b6a91;
  }
  .x-p {
    position: fixed;
    top: 200%;
    z-index: -10;
  }
  </style>

结语

至此,如果用第二实现方式去做的话对于不同机型手机高度不一样的问题就不存在了,其实还有个弊端问题,就是如果用户输入的文本刚好是6行不多也不少,这样的话还是会显示全文按钮,但是展开后没有其它内容了,这个就没得办法去做判断了。如果各位有更好的方法去实现,可以下方留言,我们一起讨论哦~

3

发表评论(共0条评论)

请输入评论内容
啊哦,暂无评论数据~