uni-app弹幕卡顿主因是跨平台canvas限制:非H5端不支持requestAnimationFrame和DOM操作,需预计算坐标、限并发数≤30、用drawImage替代fillText;曲线用正弦查表、斜线用线性方程;点击靠AABB检测;小程序video遮挡需fixed定位canvas。

uni-app 里用 canvas 渲染弹幕为什么总卡顿?
因为 uni-app 的 canvas 在非 H5 平台(尤其是小程序和 App)上不支持原生 DOM 操作,也不能直接用 requestAnimationFrame 做高帧率动画;多数人误以为只要 canvas 绘制就行,结果在 iOS 小程序里一开弹幕就掉到 10fps。
- 真机调试时优先用
uni.createCanvasContext+drawImage(而非fillText)绘制弹幕文字,后者在安卓低端机上极易卡顿 - 每条弹幕必须预计算好生命周期:起始 x 坐标 = 屏幕宽度 + 文字宽度,结束 x 坐标 = -文字宽度,避免运行时反复 measureText
- 限制最大并发弹幕数(建议 ≤ 30),超出的直接丢弃,不要堆队列——uni-app 的 JS 线程和渲染线程耦合紧,积压会雪崩
- H5 平台可降级为
div+transform: translateX,但小程序/APP 必须走 canvas
怎么让弹幕沿曲线或斜线飞过屏幕?
B站式弹幕不是纯水平移动,而是带轻微 Y 轴偏移的“抖动轨迹”,算法本质是给每个弹幕绑定一个独立的 y(t) 函数,而非全局统一路径。
- 推荐用正弦扰动:
y = baseY + amplitude * Math.sin(2 * Math.PI * (x / speed) * frequency),其中baseY是行基准线(按弹幕行号 × 行高算),amplitude控制抖动幅度(通常 2~5px),frequency控制抖动密度(0.5~1.2) - 千万别在 draw 循环里实时调用
Math.sin——提前把整条轨迹点存成数组,每帧查表取值,否则 iOS 小程序性能断崖下跌 - 斜线弹幕只需让
y随x线性变化:y = k * x + b,k 由发射角度决定(如 tan(15°) ≈ 0.268),注意 b 要根据弹幕入场位置动态偏移 - 所有坐标运算必须基于 canvas 像素单位,别混用 rpx ——
uni.upx2px只在初始化时调一次,运行时不许出现
onTouchStart 拦截弹幕后如何精准定位点击位置?
canvas 是位图,没有 DOM 事件冒泡,直接绑 touchstart 只能拿到画布区域坐标,无法判断点中哪条弹幕。
- 必须维护一份运行时弹幕对象数组,每个对象含
{id, x, y, width, height, timestamp},且每帧更新其x/y - 触摸时用
canvas.getBoundingClientRect()把屏幕坐标转为 canvas 坐标,再遍历当前存活弹幕,用 AABB 碰撞检测(touchX > item.x && touchX ) - 遍历时加 early return:先筛掉
timestamp超过 2s 的旧弹幕,再筛 y 坐标偏差 > 10px 的,避免全量遍历 - 点击反馈要异步:检测到命中后,用
setTimeout(() => { /* 显示气泡 */ }, 0),否则阻塞 canvas 绘制导致下一帧丢弃
微信小程序里弹幕层被 video 组件遮挡怎么办?
小程序 video 是原生组件,层级永远最高,canvas 若在 video 同级或子级,必然被盖住。
- 唯一解法:把 canvas 放在
video的父容器外,用 CSS 定位强行叠上去,且必须设置style="position: fixed; z-index: 999;"(小程序里z-index只对 fixed 生效) - canvas 宽高必须用
uni.getSystemInfoSync().windowWidth/Height动态设置,不能写死 rpx 或百分比——否则横屏时错位 - 监听
uni.onWindowResize重置 canvas 缓冲区尺寸,并清空当前弹幕池(否则旧坐标会飞出屏幕外) - App 平台无此问题,但需注意
video的enable-danmu属性要关掉,否则自带弹幕和你的 canvas 弹幕冲突
事情说清了就结束。弹幕最难的从来不是画出来,而是活下来——每条弹幕的创建、运动、碰撞、销毁,都得卡在 uni-app 的跨平台约束缝里做取舍。










