uni-app使用canvas技术实现弹幕(思路+代码)

之前在公司有一个项目,需要使用uni-app实现一个弹幕的效果,展示已付款用户。

当时项目赶时间,使用了一个插件,后来琢磨了一下实现思路:

分析

一个弹幕功能,首先需要拿到弹幕内容,这个不用多说。

除此以外,还要有画布的总宽度,让数据一开始处于画布区域之外。

uni-app使用canvas技术实现弹幕(思路+代码)_第1张图片
然后让数据以不同的速度,向canvas区域内容滑动,知道完全穿过这个canvas区域。

当然数据不会只有一条,所以还要设置y轴的间距,如图所示:

uni-app使用canvas技术实现弹幕(思路+代码)_第2张图片

实现思路

整理数据

接口中返回的数据,一般只包含弹幕内容,所以速度和x轴,y轴这些属性,只能靠前端自己对数据做进一步处理。

例如接口提供的信息如下:

data(){
	return {
		bullet:[
			{bullet:"弹幕内容1"},
			{bullet:"弹幕内容2"},
			{bullet:"弹幕内容3"},
			...
		]
	}	
}

而我们需要的数据格式却应该是

data(){
	return {
		bullet:[
			{bullet:"弹幕内容1",speed:Math.random()*1+1,x:画布宽度,y:随机高度},
			{bullet:"弹幕内容2",speed:Math.random()*1+1,x:画布宽度,y:随机高度},
			{bullet:"弹幕内容3",speed:Math.random()*1+1,x:画布宽度,y:随机高度},
			...
		]
	}	
}

这里我使用了Object.assign去合并数据

this.bullet = this.bullet.map(item=>{
    return Object.assign(item,{
        speed:Math.random()*1+1,
        x: 750,
        y: Math.random()*100+20
        }
    )
})

绘制

创建canvas绘制箱

const ctx = uni.createCanvasContext("bulletCanvas")

绘制前清空整个画布

ctx.clearRect(0,0,750,300)

因为有多个bullet内容,所以首先考虑的是循环绘制,将每个循环到的内容配置进去。

考虑到移动端性能参差不齐,所以配合requestAnimationFrame来实现动画。

this.bullet.forEach(item=>{
	// 绘制文字内容及位置
	ctx.fillText(item.bullet,item.x,item.y)
	// 每次达到渲染频率,就让x轴向左移动一段随机的宽度,即速度
	item.x -= item.speed
	// 调用measureText()来获取TextMertics对象
	let textMertics = ctx.measureText(item.bullet)
	// 根据TextMertics对象获取文字宽度
	let textWidth = textMertics.width
	// 当弹幕内容滚动到画布之外的时候,让其再次回到右边
	if(item.x<-textWidth){
		item.x = 750
	}
})

演示代码

<template>
	<view class="cart">
		<view class="bullet">
			<canvas style="width:750rpx;height:300rpx" canvas-id="bulletCanvas"></canvas>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				bullet:[
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"},
					{bullet:"内容1"}
				]
			}
		},
		onLoad() {
		},
		onReady() {
			this.order()
			.then(()=>{
				this.draw()
			})
		},
		methods: {
			// 整理数据
			order(){
				return new Promise((resolve,reject)=>{
					resolve(
						this.bullet = this.bullet.map(item=>{
							return Object.assign(item,{
								speed:Math.random()*1+1,
								x: 750,
								y: Math.random()*100+20
							})
						})
					)
				})
			},
			draw(){
				const ctx = uni.createCanvasContext("bulletCanvas")
				
				function anim(){
					ctx.setFontSize(20)
					// 擦除整个画布
					ctx.clearRect(0,0,750,300)
					// 循环绘制
					this.bullet.forEach(item=>{
						ctx.fillText(item.bullet,item.x,item.y)
						item.x -= item.speed
						// 调用measureText()来获取TextMertics对象
						let textMertics = ctx.measureText(item.bullet)
						// 根据TextMertics对象获取文字宽度
						let textWidth = textMertics.width
						// 当弹幕滚动到画布之外的时候,再次回到右边
						if(item.x<-textWidth){
							item.x = 750
						}
					})
					ctx.draw()
					requestAnimationFrame(anim.bind(this))
				}
				anim.call(this)
			}
		}
	}
</script>

<style>
</style>

你可能感兴趣的:(javascript,canvas,数据可视化,javascript,vue.js)