这是第二次写canvas,基于微信小程序文档demo进行改写
demo效果为方块横向来回循环移动
我想做的是直播间那种点赞效果,竖向曲线移动、方块换成图片、点击添加绘制元素
第一阶段实现竖向曲线移动、点击添加绘制元素;下一阶段讲方块替换为图片
import { formatDate, getRandomInt } from "../../utils/util";
// components/stars/stars.ts
Component({
lifetimes: {
attached() {
this.createSelectorQuery().select("#myCanvas2").fields({
node: true,
size: true
}).exec(res => this.init(res));
},
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
click: [{ startTime: Date.now(), img: '', fd: getRandomInt(-3, 3), c: getRandomInt(100, 200) }
]
},
/**
* 组件的方法列表
*/
methods: {
handleAdd() {
this.data.click.push({ startTime: Date.now(), img: '', fd: getRandomInt(-3, 3), c: getRandomInt(0, 200) })
},
init(res) {
const width = res[0].width
const height = res[0].height
// 设置画布宽高
const canvas = res[0].node
const ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
// 帧渲染回调
const draw = () => {
const w = 40
const time = Date.now()
this.data.click = this.data.click.map((item, index) => {
// 计算经过的时间
const elapsed = time - item.startTime
// 计算动画位置
const n = Math.floor(elapsed / 3000)
const m = elapsed % 3000
const dy = m / 1500
const bl = 1 - dy / 3
const y = height - (height) * dy / 2
// -2 随机生成 dy如何确定呢
const x = item.fd * Math.sin(dy * Math.PI / 4) * (width - w * bl) / 2 + (width - w * bl) / 2
return { ...item, x, y, bl, w, n }
})
this.data.click = this.data.click.filter(item => {
if (item.y < 5) {
return false
}
return true
})
this.render(ctx, width, height, this.data.click)
// 注册下一帧渲染
canvas.requestAnimationFrame(draw)
}
draw()
},
render(ctx, width, height, click) {
ctx.clearRect(0, 0, width, height)
// 渲染
for (let i = 0; i < click.length; i++) {
if (!this.data.click[i]) continue;
const { x, y, bl, w, c } = this.data.click[i]
ctx.fillStyle = 'rgba(' + c + ', 0, 0,' + bl + ')';
ctx.fillRect(x, y, w * bl, w * bl);
}
}
}
})
替换图片
import { formatDate, getRandomInt } from "../../utils/util";
import { defaultIcon } from '../../pages/clockIn/defaultImg'
let imgsList: any = []
// components/stars/stars.ts
Component({
lifetimes: {
attached() {
this.createSelectorQuery().select("#myCanvas2").fields({
node: true,
size: true
}).exec(res => this.init(res));
},
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
click: [{ startTime: Date.now(), img: '', fd: getRandomInt(-3, 3), c: getRandomInt(100, 200) }
]
},
/**
* 组件的方法列表
*/
methods: {
handleAdd() {
this.data.click.push({ startTime: Date.now(), img: imgsList[getRandomInt(0, imgsList.length - 1)], fd: getRandomInt(-3, 3), c: getRandomInt(0, 200) })
},
init(res) {
const width = res[0].width
const height = res[0].height
// 设置画布宽高
const canvas = res[0].node
const ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
// 加载图片资源
let imgKeys = Object.keys(defaultIcon)
for (let k of imgKeys) {
const image = canvas.createImage()
image.onload = () => {
imgsList.push(image)
// ctx.drawImage(image, 0, 0, 40,40)
console.log(imgsList.length)
}
image.src = defaultIcon[k]
}
// 帧渲染回调
const draw = () => {
const w = 40
const time = Date.now()
this.data.click = this.data.click.map((item, index) => {
// 计算经过的时间
const elapsed = time - item.startTime
// 计算动画位置
const n = Math.floor(elapsed / 3000)
const m = elapsed % 3000
const dy = m / 1500
const bl = (1 - dy / 3) * 2
const y = height - (height) * dy / 2
// -2 随机生成 dy如何确定呢
const x = item.fd * Math.sin(dy * Math.PI / 4) * (width - w / bl) / 2 + (width - w / bl) / 2
return { ...item, x, y, bl, w, n }
})
this.data.click = this.data.click.filter(item => {
if (item.y < 5) {
return false
}
return true
})
this.render(ctx, width, height, this.data.click)
// 注册下一帧渲染
canvas.requestAnimationFrame(draw)
}
draw()
},
render(ctx, width, height, click) {
ctx.clearRect(0, 0, width, height)
// 渲染
for (let i = 0; i < click.length; i++) {
if (!this.data.click[i]) continue;
const { x, y, bl, w, c, img } = this.data.click[i]
if (img) {
ctx.drawImage(img, x, y, w / bl, w / bl)
ctx.globalAlpha = bl
}
}
},
}
})
<!--components/stars/stars.wxml-->
<view class="conponent-stars flex-cloumn-center">
<canvas type="2d" id="myCanvas2" style="width: 150px;height: 200px;"></canvas>
<view class="add" bind:tap="handleAdd"></view>
</view>
接下来的问题是canvas绘制的图片有锯齿,找了下解决办法:清晰多了
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);
由于找不到透明背景的切图,为了美观想给图片画个圆
目前只能加个边框,裁剪还不能多个同时裁剪
// 渲染
for (let i = 0; i < click.length; i++) {
if (!this.data.click[i]) continue;
const { x, y, bl, w, c, img } = this.data.click[i]
// ctx.fillStyle = 'rgba(' + c + ', 0, 0,' + bl + ')';
// ctx.fillRect(x, y, w / (3 * bl), w / (3 * bl));
if (img) {
ctx.drawImage(img, x, y, w / bl, w / bl)
ctx.globalAlpha = bl
ctx.beginPath()
ctx.arc(x + 0.5 * w / bl, y + 0.5 * w / bl, 20 / bl, 0, 2 * Math.PI)
ctx.stroke()
ctx.closePath()
// ctx.clip()
}
}
点击效果可以查看小程序打卡模块