使用插件:canvas中奖礼花

在这里插入图片描述

问题:微信小程序动画卡顿

优化思路:

动画刷新频次执行的window.requestAnimationFrame()函数,原代码的解决方案是如果没有该函数就1s执行60次,替换成小程序兼容的Canvas.requestAnimationFrame

Canvas.requestAnimationFrame需要使用新 Canvas 2D 接口,依据旧版画布迁移指南进行调整

步骤:

  1. canvas标签需要添加id和type属性
  2. 需要替换成使用this.createSelectorQuery()获取画布信息
  3. 获取画布后需要重新设置画布大小为canvas标签大小,否则计算时候使用的值会变成默认的300*150
  4. 画布的draw()方法在设置标签的type时候已被移除,这里使用的clearRect()替代,但是 clearRect()不兼容老版,需分开处理

完整代码(直接复制替换即可)

<template>
	<canvas :id="canvasId" :canvas-id="canvasId" type="2d"
		:style="{width: width + 'px', height: height + 'px', 'z-index': zIndex}" class="fire-canvas"
		@error="canvasIdErrorCallback"></canvas>
</template>

<script>
	export default {
		props: {
			canvasId: {
				type: String,
				default: 'FireCanvas'
			},
			/** 礼花层级 */
			zIndex: {
				type: [Number, String],
				default: 1
			},
			/** 宽 */
			width: {
				type: [Number, String],
				default: () => {
					return uni.getWindowInfo().windowWidth
				}
			},
			/** 高 */
			height: {
				type: [Number, String],
				default: () => {
					return uni.getWindowInfo().windowHeight
				}
			},
			/** 中心点-x */
			x: {
				type: [Number, String],
				default: () => {
					return uni.getWindowInfo().windowWidth / 2
				}
			},
			/** 中心点-y */
			y: {
				type: [Number, String],
				default: () => {
					return uni.getWindowInfo().windowHeight * 0.4
				}
			},
			/** 礼花数量(最好小于500,太多会卡顿) */
			particleCount: {
				type: [Number, String],
				default: 150
			},
			/** 取值	0-360
			    喷发角度示意图(简单说就是喷射方向)
			    礼花(90)
			    礼花(180)
				礼花0
			    礼花(270)
			*/
			angle: {
				type: [Number, String],
				default: 90
			},
			/** 爆炸范围 */
			spread: {
				type: [Number, String],
				default: 100
			},
			/** 喷发的初始速度 */
			startVelocity: {
				type: [Number, String],
				default: 45
			},
			/*
			    喷发的衰退时间,超出canvas会被清除,跟startVelocity配合使用
			*/
			decay: {
				type: [Number, String],
				default: 0.9
			},
			/** 刷新几次消失(其实是透明度为0),这个跟间隔的刷新频率有关 */
			ticks: {
				type: [Number, String],
				default: 100
			},
			/** 所有要随机的礼花颜色预选值*/
			colors: {
				type: Array,
				default: () => ['#5BC0EB', '#2176AE', '#FDE74C', '#9BC53D', '#E55934', '#FA7921', '#FF4242']
			},
		},
		data() {
			return {
				fireCanvasBox: null,
				fireCanvasNode: null,
				ribbon: [],
				/** 手机分辨率 */
				pixelRatio: uni.getWindowInfo().pixelRatio
			}
		},
		mounted() {
			this.$nextTick(() => {
				this.initCanvas()
			})
		},
		methods: {
			canvasIdErrorCallback(e) {
				console.error(e.detail.errMsg)
			},
			initCanvas() {
				this.createSelectorQuery()
					.select(`#${this.canvasId}`)
					.fields({
						node: true,
						size: true
					})
					.exec((res) => {
						this.fireCanvasBox = null
						// Canvas 对象
						this.fireCanvasNode = res[0].node
						if (this.fireCanvasNode) {
							this.fireCanvasNode.width = this.width
							this.fireCanvasNode.height = this.height
							this.fireCanvasBox = this.fireCanvasNode.getContext('2d')
						} else {
							this.fireCanvasBox = uni.createCanvasContext(this.canvasId, this)
						}
						this.fireworksDraw()
					})
			},
			fireworksDraw() {
				let particleCount = this.particleCount
				for (; particleCount--;) {
					let color = this.colors[particleCount % this.colors.length]
					let r = this.angle * (Math.PI / 180)
					let a = this.spread * (Math.PI / 180)
					let i = 0
					// 菜单位置初始化
					this.ribbon.push({
						x: this.x,
						y: this.y,
						depth: 0.5 * Math.random() + 0.6,
						wobble: 10 * Math.random(),
						velocity: 0.5 * this.startVelocity + Math.random() * this.startVelocity,
						angle2D: -r + (0.5 * a - Math.random() * a),
						tiltAngle: Math.random() * Math.PI,
						color: (i = (color + '').replace(/[^0-9a-f]/gi, ''), i.length < 6 && (i = i[0] + i[0] +
							i[1] + i[1] + i[2] + i[2]), { // 生成随机颜色
							r: parseInt(i.substring(0, 2), 16),
							g: parseInt(i.substring(2, 4), 16),
							b: parseInt(i.substring(4, 6), 16)
						}),
						tick: 0,
						totalTicks: this.ticks,
						decay: this.decay,
						random: Math.random() + 5,
						tiltSin: 0,
						tiltCos: 0,
						wobbleX: 0,
						wobbleY: 0
					})
				}
				this.drawRibbon()
			},
			drawRibbon() {
				if (!this.fireCanvasBox) return
				// #ifdef MP-WEIXIN
				this.fireCanvasBox.clearRect(0, 0, this.width, this.height)
				// #endif
				// #ifndef MP-WEIXIN
				this.fireCanvasBox.draw()
				// #endif
				this.fireCanvasBox.restore()
				this.ribbon = this.ribbon.filter((e) => {
					e.x += Math.cos(e.angle2D) * e.velocity
					e.y += Math.sin(e.angle2D) * e.velocity + 5 * e.depth
					e.wobble += 0.1
					e.velocity *= e.decay
					e.tiltAngle += 0.02 * Math.random() + 0.12
					e.tiltSin = Math.sin(e.tiltAngle)
					e.tiltCos = Math.cos(e.tiltAngle)
					e.random = Math.random() + 4
					e.wobbleX = e.x + 10 * Math.cos(e.wobble) * e.depth
					e.wobbleY = e.y + 10 * Math.sin(e.wobble) * e.depth
					// 开始画图
					this.fireCanvasBox.fillStyle =
						`rgba(${e.color.r},${e.color.g},${e.color.b},${(1 - (e.tick++) / e.totalTicks)})`
					this.fireCanvasBox.beginPath()
					this.fireCanvasBox.moveTo(Math.floor(e.x), Math.floor(e.y))
					this.fireCanvasBox.lineTo(Math.floor(e.wobbleX), Math.floor(e.y + e.random * e.tiltSin))
					this.fireCanvasBox.lineTo(Math.floor(e.wobbleX + e.random * e.tiltCos),
						Math.floor(e.wobbleY + e.random * e.tiltSin))
					this.fireCanvasBox.lineTo(Math.floor(e.x + e.random * e.tiltCos), Math.floor(e.wobbleY))
					this.fireCanvasBox.closePath()
					this.fireCanvasBox.fill()
					return e.tick < e.totalTicks
				})
				// 轮询调用或者释放掉
				if (this.ribbon.length) {
					this.AnimationFrame(() => {
						this.drawRibbon()
					})
				} else {
					this.fireCanvasBox = null
				}
			},
			AnimationFrame(t) {
				// #ifdef MP-WEIXIN
				this.fireCanvasNode.requestAnimationFrame(t)
				// #endif
				// #ifndef MP-WEIXIN
				/** 浏览器的最高刷新赔率(最后一个表示1秒刷新60次) */
				if (window) {
					if (window.requestAnimationFrame) {
						window.requestAnimationFrame(t)
					} else if (window.webkitRequestAnimationFrame) {
						window.webkitRequestAnimationFrame(t)
					} else if (window.mozRequestAnimationFrame) {
						window.mozRequestAnimationFrame(t)
					} else if (window.oRequestAnimationFrame) {
						window.oRequestAnimationFrame(t)
					} else if (window.msRequestAnimationFrame) {
						window.msRequestAnimationFrame(t)
					} else {
						setTimeout(t, 10)
					}
				}
				// #endif
			}
		}
	}
</script>

<style lang="scss" scoped>
	.fire-canvas {
		position: fixed;
		top: 0px;
		bottom: 0px;
		left: 0px;
		right: 0px;
		pointer-events: none;
	}
</style>

更多推荐