直播业务场景下,微信小程序相关踩坑问题(2022-09-28更新)

960 阅读3分钟

背景

本人公司是做直播SaaS相关业务,和现在的各种直播平台比较类似,我们主要是提供金融相关的直播服务。最近公司在拓展小程序直播业务,由本人负责开发与现有H5版本的直播系统相同的微信小程序,由于没有完整开发直播项目的经验,特此记录踩坑,第一次写掘金,有瑕疵和不妥的地方还希望大家多多指正。

1. 直播间聊天功能的体验优化

  • 问题

在一般聊天场景下,新发送的消息渲染在box底部,历史消息通过上拉加载渲染在顶部。该逻辑在JS中实现,直接将获取的历史聊天数据unshift到当前聊天数据并渲染即可,新增的dom会添加到顶部,同时跳回刚才的scrollTop就能实现很好的体验。使用相同的思路在微信小程序中实现,在跳转scrollTop位置时,会出现跳动的情况,影响体验和观感。

  • 解决思路

初步分析原因是因为小程序的渲染层和逻辑层是分离的,由两个线程分别管理,在渲染时会有一定的延迟。同时发现在底部新增的数据,会无缝添加在底部并停留在当前位置,不需要跳转scrollTop,在这种情况下,可以将box进行translateZ(180deg)旋转,box内部item也进行translateZ(180deg)旋转,内容即可正常展示,新增的数据添加到list顶部,并将scrollTop置为0,可以实现新增聊天消息,实时显示,历史数据可以添加到list底部,从而避免跳动情况。

  • 总结

使用该逻辑实现的聊天框功能,遇到一个偶先问题就是聊天box上覆盖有使用position定位的元素,会出现触摸滑动反向问题,需要使用transform开启独立图层,才不会相互影响。

  • 相关参考

参考1 参考2

2. 直播间点赞动效的canvas实现

  • 问题:

直播间内部有很多组件,在渲染很多组件以后,会出现卡顿问题,本身小程序的动画性能就不是很好,产品需要实现一个点赞漂浮动效,最初的逻辑是渲染view,给每个view添加过渡,transitionEnd以后删除自身,实现一个版本以后,发现很多机型出现了漂浮动画卡顿掉帧问题,效果如下图

批注 2022-05-30 235730.png

  • 解决思路

基于该问题,想到了使用canvas渲染漂浮动效,性能比N个view性能要好,最终实现效果不错,大概逻辑:定义好需要的icon,同时定义初始尺寸和位置,监听点赞触发,每次触发渲染新图片到canvas中,在下一次requestAnimationFrame之前,对每个图片的数据进行更新,渲染在新的位置,代码如下,有什么问题希望大家可以指出,添加圆形微信头像icon的功能还未完成,后续会更新。

Component({
  properties: {
    likeShadowList: Number
  },
  lifetimes: {
    attached() {
      this.initData = []
      // 获取头像
      // this.avatar = wx.getStorageSync('userInfo').avatarUrl
      // 获取canvas节点
      this.createSelectorQuery()
      .select('.canvas')
      .fields({
        node: true,
        size: true,
      })
      .exec(this.init.bind(this))
    }
  },
  observers: {
    'likeShadowList'() {
      this.initData && this.initData.push({
        pic: this['pic' + Math.floor(Math.random() * 6)],
        x: 1,
        y: 350,
        z: [-4, 0, 4][Math.floor(Math.random() * 3)],
        width: 0,
        height: 0,
        speed: 0.001
      })
    }
  },
  methods: {
    init(res) {
      const width = res[0].width
      const height = res[0].height
      const canvas = res[0].node
      const ctx = canvas.getContext('2d')
      // 初始化dpr
      const dpr = wx.getSystemInfoSync().pixelRatio
      canvas.width = width * dpr
      canvas.height = height * dpr
      ctx.scale(dpr, dpr)
      // 初始化icon
      for (let i = 1; i < 7; i++) {
        const pic = canvas.createImage()
        if (i !== 7) {
          pic.src = '../../assets/imgs/icons/' + i + '.png'
        } else {
          pic.src = this.avatar
        }
        pic.onload = () => {
          this['pic' + (i - 1)] = pic
        }
      }
      // 开始渲染
      const renderLoop = () => {
        this.render(canvas, ctx)
        canvas.requestAnimationFrame(renderLoop)
      }
      canvas.requestAnimationFrame(renderLoop)
    },
    render(canvas, ctx) {
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      for (let i = 0; i < this.initData.length; i++) {
        this.drawIcon(ctx, this.initData[i].x, this.initData[i].y, this.initData[i].z, this.initData[i].pic, i)
      }
    },
    drawIcon(ctx, x, y, z, pic, i) {
      if (!pic || y < -50) return false
      if (100 - (y - 250) > 50 && 100 - (y - 250) < 85) {
        this.initData[i].width += 0.5
        this.initData[i].height += 0.5
      }
      if (y < 30) {
        this.initData[i].width -= 0.8
        this.initData[i].height -= 0.8
      }
      this.initData[i].speed += 0.001
      ctx.drawImage(pic, x + z + ((30 - this.initData[i].width) / 2), this.initData[i].y-=(0.5 + this.initData[i].speed), this.initData[i].width, this.initData[i].height)
    }
  }
})
  • 后续优化

该效果上线后,生产环境不是很满意,后续进行优化,加入正弦动画和爆炸发射效果,该小程序开发完成后,已同步到H5版本,晚点会更新代码和思路。