likes
comments
collection
share

小程序的聊天功能中如何让消息列表定位到未读消息?

作者站长头像
站长
· 阅读数 17

笔者要在微信小程序中开发一个聊天会话的功能,需求中有这样的一条:要将聊天消息定位到首个未读消息。看到这样的需求笔者灵机一动小程序的聊天功能中如何让消息列表定位到未读消息?:首先想到就是把聊天消息放到滚动容器中,然后根据已读的消息设置滚动条的滚动高度,如此一来就可以定位到未读消息啦。

wx.createSelectorQuery、SelectorQuery与NodesRef

可是这个解决方案中的关键点时啥啊?小程序的聊天功能中如何让消息列表定位到未读消息?想一想,无非就是要知道那些已读消息的高度。那么微信中有没有相关的API可以获取到wxml中元素(组件)的高度呢?还真有,那就是wx.createSelectorQuery

小程序的聊天功能中如何让消息列表定位到未读消息?温馨提示:下面的内容文字较多,可以看完后面的例子再回来看本部分内容!

wx.createSelectorQuery方法调用后会返回一个 SelectorQuery 对象实例,SelectorQuery对象实例用于查询节点信息的对象,提供了如下的方法(以下5个方法本文中都会使用,都需要关注! ):

  • SelectorQuery SelectorQuery.in(Component component):将选择器的选取范围更改为自定义组件 component 内。
  • NodesRef SelectorQuery.select(string selector):在当前页面下选择第一个匹配选择器 selector 的节点。返回一个 NodesRef 对象实例,可以用于获取节点信息。
  • NodesRef SelectorQuery.selectAll(string selector):在当前页面下选择匹配选择器 selector 的所有节点。
  • NodesRef SelectorQuery.selectViewport():选择显示区域。可用于获取显示区域的尺寸、滚动位置等信息。
  • NodesRef SelectorQuery.exec(function callback):执行所有的请求。请求结果按请求次序构成数组,在 callback 的第一个参数中返回。

我们看到对于select、selectAll、selectViewport还有exec方法都返回NodesRef类型的对象,它是用于获取 WXML 节点信息的对象,我们看一下这个类的实例可以调用的一个重要的方法:

  • SelectorQuery NodesRef.boundingClientRect(NodesRef.boundingClientRectCallback callback):添加节点的布局位置的查询请求。相对于显示区域,以像素为单位。其功能类似于 DOM 的 getBoundingClientRect。返回 NodesRef 对应的 SelectorQuery

阅读以上内容我们需要注意:SelectorQuery实例的select、selectAll、selectViewport方法调用后会返回NodesRef的实例,NodesRef实例的boundingClientRect方法调用后又会返回SelectorQuery实例。这也就是本文中接下来介绍的演示代码中可以链式调用的原因。

代码示例

如下所示:在data中定义了scrollTop用于控制scroll-view组件的滚动高度;定义了blockList数组用于循环渲染出view组件从而来模拟聊天消息,注意这里定义了9个已读消息,10个未读消息。

data: {
    scrollTop: 0,
    blockList: [
      {
        content: '1',
        hasRead: true
      },
      //....
      {
        content: '9',
        hasRead: true
      },
      {
        content: '10',
        hasRead: false
      },
      //....
      {
        content: '20',
        hasRead: false
      },
    ]
  },

如下所示的wxml代码中,在scroll-view组件中嵌套了一个固定的view组件和用wx:for指令循环生成的一组view组件。注意对于循环生成的view组件我们为其绑定了动态的class,如果是已读消息则class类名为read-class,如果是未读消息则class类名为unread-class:

<!--pages/test/test.wxml-->
<view class="test-container">这是页面主体
  <scroll-view class="test-scroll" 	scroll-top="{{scrollTop}}"	scroll-y="true" bindscrolltolower="loadMore">
    <view class="block-read" >我是测试内容块</view>
    <view wx:for="{{blockList}}" class="{{item.hasRead ? 'read-class': 'unread-class'}}" >
      {{item.content}}
    </view>
  </scroll-view>
</view>

如下所示的css代码中为固定的view组件定义了灰色(#ccc)的边框,为循环生成的代表已读消息的view组件定义了绿色边框,为循环生成的代表未读消息的view组件定义了红色边框:

.block-read {
  height: 400rpx;
  border: 1px solid #ccc;
}
.read-class {
  height: 200rpx;
  border: 1px solid green;
}

.unread-class {
  height: 180rpx;
  border: 1px solid red;
}

.test-scroll {
  height: 700px;
}

以上代码的执行效果如下图所示:

小程序的聊天功能中如何让消息列表定位到未读消息?

下面我们尝试使用wx.createSelectorQuery,SelectorQuery类的select、selectAll,exec方法,NodesRef的boundingClientRect方法来获取view组件的高度。

首先使用select选中未读消息,代码如下图所示:

getViewHeight() {
  const query = wx.createSelectorQuery().in(this)
  query.select('.read-class').boundingClientRect((res:any) => {
    console.log(res)
  }).exec()
},

由于select只会在当前页面下选择第一个匹配选择器的节点,所以可以打印出一个节点的信息,如下图所示:

小程序的聊天功能中如何让消息列表定位到未读消息?

下面代码中将select换成selectAll:

getViewHeight() {
  const query = wx.createSelectorQuery().in(this)
  query.selectAll('.read-class').boundingClientRect((res:any) => {
    console.log(res)
  }).exec()
},

selectAll可以在当前页面下选择匹配选择器的所有节点,所以可以打印出一个数组,如下图所示

小程序的聊天功能中如何让消息列表定位到未读消息?

有了所有节点的数组后我们就可以循环遍历数组元素对元素height值累加,这样就得到了所有已读消息的高度,滚动条的scrollTop赋值为这个值之后就可以定位到首个未读消息了:

getViewHeight() {
  const query = wx.createSelectorQuery().in(this)
  query.selectAll('.read-class').boundingClientRect((res:any) => {
    let scrollTop = 0
    res.forEach((item: any) => {
      scrollTop += item.height
    })
    console.log(scrollTop)
    this.setData({
      scrollTop:scrollTop
    })
  }).exec()
},
onLoad() {
  this.getViewHeight()
},

代码执行效果如下图所示:

小程序的聊天功能中如何让消息列表定位到未读消息?

最后再来看一下selectViewport的使用:

const query = wx.createSelectorQuery().in(this)
query.selectViewport().boundingClientRect((res) => {
  console.log(res)
}).exec()

selectViewport会选择显示区域,可用于获取显示区域的尺寸、滚动位置等信息,代码执行结果如下图所示:

小程序的聊天功能中如何让消息列表定位到未读消息?

可以看到显示区域的高度是721px ,显示区域的宽度是375px。使用调试器中的wxml进行元素选择得到的结果和代码是一致的,如下图所示:

小程序的聊天功能中如何让消息列表定位到未读消息?

总结

  • 要将聊天消息定位到首个未读消息的需求可以把聊天消息放到滚动容器中,然后根据已读的消息设置滚动条的滚动高度。
  • scroll-view组件的scroll-top属性用于设置竖向滚动条位置。
  • wx.createSelectorQuery方法调用后会返回一个 SelectorQuery 对象实例,SelectorQuery对象实例用于查询节点信息,提供了select、selectAll、selectViewport等方法。
  • NodesRef类型的对象是用于获取 WXML 节点信息的对象,可以调用boundingClientRect来查询节点的布局位置。
转载自:https://juejin.cn/post/7196690584287002680
评论
请登录