实现图文消息的正确加载

简介: 实现图文消息的正确加载

前言


昨天,在我的开源项目chat-system中查看聊天记录时,发现消息中如果有图片滚动条的位置就会算错,导致最后一条消息定位不准确。


经过一番排查后,终于解决了这个问题,本文就跟大家分享下我的解决方案与思路,欢迎各位感兴趣的开发者阅读本文。


问题分析


如下图所示,我们点开一个聊天窗口,最后一条消息是图片,滚动条位置计算有误,没有触底,导致图片没有显示完全,在上拉加载历史消息时也是因为图片导致的滚动条位置计算失误,没有正确定位到上次浏览的消息位置。


[ gif加载失败,感兴趣的开发者可移步原文进行查看 ]


滚动条触底分析


我们来看下触底时滚动条位置计算的实现代码:


nextTick().then(() => {
      let scrollHeight = 0;
      if (messagesContainer.value == null) return;
      // 获取消息容器滚动区域高度
      scrollHeight = messagesContainer.value.scrollHeight;
      // 当前滚动条在底部或者当前消息为发送端所发送的则修改滚动条位置
      if (isBottomOut.value || data.isSendMessages.value) {
        // 新消息渲染完成,修改滚动条位置
        messagesContainer.value.scrollTop = scrollHeight;
      }
    });


如上述代码所示,我们在nextTick回调中获取了消息容器的滚动区域高度,然后修改滚动条位置为滚动区域高度,这样滚动条就会触底了,逻辑上没问题,而且在纯文字的消息中是正常的。


那么,问题可能出在获取消息容器高度时,没有获取正确,于是我尝试了下将scrollHeight改为99999,这样它的滚动条就肯定在底部了。


640.png


然而,并没有我预想的那样顺利,改成99999后,滚动条的位置依然是错的。


那么,我想问题应该是nextTick()后滚动条确实到底部了,但是此时图片还没有加载完,图片加载完成后滚动条位置就又变了。


此时,我们就找到了问题,那么我们就可以得到下述解决思路:


  • 获取页面内的所有聊天图片
  • 遍历获取到的图片
  • 每一张图片加载完成后就获取可滚动容器的高度,然后修改滚动条位置


滚动条触顶分析


触顶加载数据时,也是因为图片的缘故,导致了滚动条位置计算失误,一开始我选择沿用触底的时的方案,等img加载完成后获取滚动容器的高度,然后用当前消息容器高度 - 上一次保存的消息容器高度,这样就能计算出上一次浏览消息时的滚动条位置。


按照上述思路实现后,滚动条的位置依然是错的,经过一番调试后,发现每次触顶时,dom都会重新加载,自然已经加载过的图片还会重新加载一次,滚动条的位置自然也就算错了。


经过一番思考后,我想到了一个解决方案,既然等图片加载完行不通,那我就用定时器吧。


  • nextTick()后,等待150ms,然后获取消息容器的可滚动高度.
  • 计算滚动条的位置
  • 修改滚动条位置


实现代码


接下来,我们来看下具体的实现代码。


滚动条触底


滚动条触底时的部分代码如下所示,完整代码请移步:messageParsing.ts


nextTick().then(() => {
    const scrollHeight = 0;
    // 获取页面内所有的聊天图片 
    const previewablePanel = document.getElementsByClassName("previewable");
    if (messagesContainer.value == null) return;
    for (let i = 0; i < previewablePanel.length; i++) {
      const item = previewablePanel.item(i) as HTMLImageElement;
      item.onload = () => {
          if (messagesContainer.value == null) return;
          // 置底滚动条
          bottomScrollBar(
            scrollHeight,
            messagesContainer as Ref<HTMLDivElement>,
            isBottomOut,
            msgListPanelHeight,
            isFirstLoading
          );
        };
    }
  });
const bottomScrollBar = (
  scrollHeight: number,
  messagesContainer: Ref<HTMLDivElement>,
  isBottomOut: Ref<boolean>,
  msgListPanelHeight: Ref<number>,
  isFirstLoading: Ref<boolean>
) => {
  const data = initData();
  // 显示消息内容
  data.msgShowStatus.value = "";
  // 获取容器高度
  scrollHeight = messagesContainer.value.scrollHeight;
  // 当前滚动条在底部或者当前消息为发送端所发送的则修改滚动条位置
  if (isBottomOut.value || data.isSendMessages.value) {
    // 新消息渲染完成,修改滚动条位置
    messagesContainer.value.scrollTop = scrollHeight;
    // 更新消息记录容器高度
    msgListPanelHeight.value = scrollHeight;
    // 修改组件第一次加载状态为false
    isFirstLoading.value = false;
    // 修改消息发送端状态为false
    data.isSendMessages.value = false;
  }
};


滚动条触顶


滚动条触顶时的部分代码如下所示,完整代码请移步:messageParsing.ts


nextTick().then(() => {
      // 隐藏消息内容
      data.msgShowStatus.value = "hidden";
      if (data.pageNo.value > 20) {
        // 数据加载超过20条,加载时间改为400ms
        loadingTime = 400;
      }
      setTimeout(() => {
        if (messagesContainer.value == null) return;
        scrollHeight = messagesContainer.value.scrollHeight;
        // 加载历史消息,修改滚动条位置:当前消息记录容器高度 - 消息记录容器高度
        messagesContainer.value.scrollTop =
          scrollHeight - msgListPanelHeight.value;
        // 一条消息渲染完成,待渲染消息总条数自减
        msgTotals.value--;
        // 判断消息是否渲染完成
        if (msgTotals.value === 0) {
          // 显示消息内容
          data.msgShowStatus.value = "";
          // 关闭加载动画
          isLoading.value = false;
          // 加载历史消息完成,更新消息记录容器高度
          msgListPanelHeight.value = scrollHeight;
        }
      }, loadingTime);
    });


在上述代码中,定时器的时间是动态的,是因为我发现当加载的消息超过20页时,等待150ms已经拿不到正确的可滚动容器高度了,需要等待400ms。


实现效果


接下来,我们来看下最终的实现效果。


滚动条触顶


在上述实现代码中,我还做了一个优化,nextTick后我隐藏了消息内容,滚动条位置计算完成后,让消息内容再显示出来。


至于为什么要做这个优化,我通过gif图来描述下吧,我们先来看下没做优化时的触顶加载效果,如下所示:


[ gif加载失败,感兴趣的开发者可移步原文进行查看 ]


如上图所示,未优化时加载消息会先闪一下错误位置的消息,然后才会展示正确的消息,看着很难受。


接下来,我们来看下优化后的效果,如下所示:


[ gif加载失败,感兴趣的开发者可移步原文进行查看 ]


优化后,视觉效果相比未优化时要好上很多,虽然还是有点瑕疵,会闪烁一下😂,目前想不到其他解决方案了,只能先这样了,如果大家有更好的方案,可在评论区一起讨论。


滚动条触底


滚动条触底时,由于是需要等图片加载完成后修改滚动条的位置,图片未加载完成时,界面会先闪一下错误位置的消息,然后才是正确的消息。


触底时,我采用了与触顶时相同的解决方案,滚动条位置计算完成后才让聊天记录显示,实现效果如下所示:

640.gif


项目地址


  • 在线体验地址:chat-system
  • GitHub地址:chat-system-github


写在最后


  • 文中部分gif无法展示,感兴趣的开发者可移步原文进行查看
  • 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊
相关文章
|
6月前
【微信公众平台对接】有关【上传图文消息内的图片获取URL】调用示例
【微信公众平台对接】有关【上传图文消息内的图片获取URL】调用示例
168 0
|
6月前
|
Windows
微信消息实现自动推送--方式一 成功啦 进来学
微信消息实现自动推送--方式一 成功啦 进来学
638 1
|
6月前
调用阿里云接口实现短信消息的发送源码——CSDN博客
调用阿里云接口实现短信消息的发送源码——CSDN博客
|
XML 缓存 语音技术
08 公众号开发 - 接收和回复其它普通消息(图片视频等)
08 公众号开发 - 接收和回复其它普通消息(图片视频等)
38 0
|
存储 JSON 小程序
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
418 0
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
|
程序员 数据安全/隐私保护
“不能初始化photoshop,因为暂存盘已满”图文解决方案
“不能初始化photoshop,因为暂存盘已满”图文解决方案
677 0
“不能初始化photoshop,因为暂存盘已满”图文解决方案
另类保存微信公众平台历史消息的方法 - 星标消息
  前面怎样把微信聊天记录导出备份到电脑【微信公众平台技巧】介绍的通过复制源代码来保存微信公众平台历史消息的方法,有网友反映说不会用批量替换、不会批量换行保存,一头雾水。这里我们就说个简单的方法,直接用星标消息保存,打开微信公众号消息管理,鼠标移动到你要保存的消息,右侧会显示几个菜单,其中有个星星形状的,默认是灰色的,如下图红框所示,点击后变成黄色的小星星,这样微信公众号星标信息就完成了,so easy!哪里要存点哪里!      腾讯客服提供的回答“微信公众号星标信息有什么用?”   他们的解释是: 在“用户管理”中可以对成员进行“标记星标”,便于查看和找到该成员。
1041 1