微信小程序聲稱 2.9.0 起支持新的 Canvas 2D 接口,且官方推薦,使用性能更好的2d模式。
官網文檔地址 https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html
新的 canvas 2d,接口與 Web 一致,然而,官方的文檔上對新 api 的說明寥寥無幾,只能翻看 Web端api。
wxml
<canvas type="2d" id="canvas" style="width: 100%;height: 400rpx"/>
wxml 中就是簡單加個 id、type,以及樣式。但是官方的 bug 提示中
Bug & Tip
- tip: Canvas 2D(新接口)需要顯式設置畫布寬高 (默認為 300x150)
這里有個細節,后續會用。先按照正常流程往后走,取 ctx 對象
drawCanvas(){
const query = wx.createSelectorQuery()
query.select('#canvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
ctx.fillRect(0, 0, 100, 100)
})
}
標簽里定義了寬高,在這里又重新定義,難以理解,試着刪除 width、height 的設置。
然而后面的操作出現了變形,即使繪入一個正方形,在畫布上展示的比例也不對。打印出 canvasNode,如下
通過 style 設置的寬高 _width: 375,_height: 195
;實際寬高 width: 300,height: 150
,顯然來自默認寬高。
回想一下前面的官方bug 提示
- tip: Canvas 2D(新接口)需要顯式設置畫布寬高 (默認為 300x150)
大膽設想一下,style設置寬高,相當於設置組件的外部容器,js設置寬高,則是定義實際操作的畫布的寬高。。。
此外,dpr 設置像素比,縮放坐標系,把當前的坐標系橫縱軸放大、縮小到 drp 倍。官方demo中,采用的是設備像素比,旨在達到最佳像素尺寸的圖。
項目中,實際使用時,更多是750設計稿,不妨將其縮放比設為屏幕寬度/750標准寬度,此后的所有尺寸,都可以按照750設計稿上的尺寸來寫入。改寫一下
async drawCanvas(){
// 取 canvas 節點
// 此處使用 await
let canvasNode = await new Promise((resolve, reject)=>{
// 注意:若canvas是定義在在組件內,需改用
// const query = wx.createSelectorQuery().in(this)
const query = wx.createSelectorQuery()
query.select('#canvas')
.fields({ node: true, size: true })
.exec(res=>{
resolve(res[0])
})
})
let canvas = canvasNode.node,
ctx = canvas.getContext('2d')
this.canvas = canvas
this.ctx = ctx
// 獲取設備像素比調整畫布尺寸,並縮放坐標系
const dpr = wx.getSystemInfoSync().screenWidth / 750
canvas.width = canvasNode.width * dpr
canvas.height = canvasNode.height * dpr
// 設置 canvas 坐標原點
ctx.translate(width/2, height * 2 / 3);
ctx.scale(dpr, dpr)
}
此處,ctx.translate()
用來調整 canvas 坐標系,默認是左上角,ctx.translate(width/2, height * 2 / 3)
可設置坐標系原點為橫向中心,縱向2/3高度處。可根據實際應用做調增。
到這里,初步完成 canvas 的前期初始化,后面即可繪制
async render () {
let canvas = this.canvas
let ctx = this.ctx
// 實例化 canvas 圖像
let image = await new Promise((resolve)=>{
const img = canvas.createImage()
img.src = '../images/img-arrow.png'
img.onload = () => {
resolve(img)
}
})
let angle = -45 // 實際角度
let abs = 1 // 正反轉
// 循環執行
const renderLoop = () => {
if(angle>45){
abs = -1
}else if(angle<-45){
abs = 1
}
// 清楚畫布
ctx.clearRect(-375, -400, 750, 750)
// 保存當前幀,以備復原
ctx.save()
// 畫布旋轉
ctx.rotate(degree / 180 * Math.PI);
// 繪制圖像
ctx.drawImage(image, -110, -228, 221, 302)
// 操作完后復原至 save 步驟
ctx.restore()
angle += abs
}
// 動畫幀循環執行
canvas.requestAnimationFrame(()=>{
renderLoop()
})
}