背景
本人公司是做直播SaaS相关业务,和现在的各种直播平台比较类似,我们主要是提供金融相关的直播服务。最近公司在拓展小程序直播业务,由本人负责开发与现有H5版本的直播系统相同的微信小程序,由于没有完整开发直播项目的经验,特此记录踩坑,第一次写掘金,有瑕疵和不妥的地方还希望大家多多指正。
1. 直播间聊天功能的体验优化
- 问题
在一般聊天场景下,新发送的消息渲染在box底部,历史消息通过上拉加载渲染在顶部。该逻辑在JS中实现,直接将获取的历史聊天数据unshift到当前聊天数据并渲染即可,新增的dom会添加到顶部,同时跳回刚才的scrollTop就能实现很好的体验。使用相同的思路在微信小程序中实现,在跳转scrollTop位置时,会出现跳动的情况,影响体验和观感。
- 解决思路
初步分析原因是因为小程序的渲染层和逻辑层是分离的,由两个线程分别管理,在渲染时会有一定的延迟。同时发现在底部新增的数据,会无缝添加在底部并停留在当前位置,不需要跳转scrollTop,在这种情况下,可以将box进行translateZ(180deg)旋转,box内部item也进行translateZ(180deg)旋转,内容即可正常展示,新增的数据添加到list顶部,并将scrollTop置为0,可以实现新增聊天消息,实时显示,历史数据可以添加到list底部,从而避免跳动情况。
- 总结
使用该逻辑实现的聊天框功能,遇到一个偶先问题就是聊天box上覆盖有使用position定位的元素,会出现触摸滑动反向问题,需要使用transform开启独立图层,才不会相互影响。
- 相关参考
2. 直播间点赞动效的canvas实现
- 问题:
直播间内部有很多组件,在渲染很多组件以后,会出现卡顿问题,本身小程序的动画性能就不是很好,产品需要实现一个点赞漂浮动效,最初的逻辑是渲染view,给每个view添加过渡,transitionEnd以后删除自身,实现一个版本以后,发现很多机型出现了漂浮动画卡顿掉帧问题,效果如下图
- 解决思路
基于该问题,想到了使用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版本,晚点会更新代码和思路。