這是第二次寫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].widthconst height = res[0].height// 設置畫布寬高const canvas = res[0].nodeconst ctx = canvas.getContext('2d')canvas.width = widthcanvas.height = height// 幀渲染回調const draw = () => {const w = 40const 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 % 3000const dy = m / 1500const bl = 1 - dy / 3const y = height - (height) * dy / 2// -2 隨機生成 dy如何確定呢const x = item.fd * Math.sin(dy * Math.PI / 4) * (width - w * bl) / 2 + (width - w * bl) / 2return { ...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].widthconst height = res[0].height// 設置畫布寬高const canvas = res[0].nodeconst ctx = canvas.getContext('2d')canvas.width = widthcanvas.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 = 40const 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 % 3000const dy = m / 1500const bl = (1 - dy / 3) * 2const y = height - (height) * dy / 2// -2 隨機生成 dy如何確定呢const x = item.fd * Math.sin(dy * Math.PI / 4) * (width - w / bl) / 2 + (width - w / bl) / 2return { ...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 = blctx.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()}}
點擊效果可以查看小程序打卡模塊