一个Android开发者的uni-app血泪史
一个Android开发者的uni-app血泪史
uni-app Canvas API 吐槽大全:一个Android开发者的血泪史
作为一名资深Android开发者,当我满怀信心地转战uni-app开发时,万万没想到会在Canvas API这里栽了个大跟头。今天就来深度吐槽一下这个让人又爱又恨的uni.canvasToTempFilePath。
🔥 开篇吐槽:文档与现实的鸿沟
官方文档说的很美好
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: (res) => {
console.log('导出成功!', res.tempFilePath)
}
})
看起来很简单对吧?就像Android的Bitmap.compress()一样简洁明了。然而现实是…
实际使用时的地狱模式
// 在微信小程序中运行
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: (res) => {
console.log('这行永远不会执行')
},
fail: (error) => {
console.error('canvasToTempFilePath:fail fail canvas is empty')
// 欢迎来到调试地狱 🔥
}
})
💀 死亡三连击:跨平台兼容性问题
第一击:API参数不统一
H5平台:
uni.canvasToTempFilePath({
canvasId: 'myCanvas', // 用字符串ID
x: 0, y: 0,
width: 300, height: 200,
success: (res) => { /* 正常工作 */ }
})
微信小程序:
// 方式1:老版本Canvas(已废弃但文档还在推荐)
uni.canvasToTempFilePath({
canvasId: 'myCanvas', // 经常莫名其妙失败
// ...
})
// 方式2:Canvas 2D(新版本但uni-app支持有问题)
uni.canvasToTempFilePath({
canvas: canvasInstance, // 需要Canvas实例,不是ID
// destWidth和destHeight可能导致崩溃
// ...
})
第二击:神秘的"canvas is empty"错误
// 明显Canvas上有内容,肉眼可见
ctx.fillStyle = '#FF0000'
ctx.fillRect(0, 0, 100, 100) // 绘制了一个红色方块
// 立即导出
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
fail: (error) => {
// 结果:canvas is empty
// 我:???红色方块是我眼花了吗?
}
})
第三击:TypeScript支持形同虚设
interface CanvasToTempFilePathOptions {
canvasId?: string
canvas?: any // 看到这个any了吗?就是在告诉你:自求多福
x?: number
y?: number
// ... 一堆可选参数,但不告诉你哪些是必需的
}
// 实际使用时
uni.canvasToTempFilePath({
canvasId: 'test'
// TypeScript:✅ 类型检查通过
// 运行时:💥 missing required parameter 'componentInstance'
// 我:🤬
})
🎭 平台差异大赏
Canvas 2D vs 传统Canvas
// 传统Canvas(将被废弃,但文档还在推荐)
<canvas canvas-id="oldCanvas" />
uni.canvasToTempFilePath({
canvasId: 'oldCanvas', // 字符串ID
// 在某些平台可能工作
})
// Canvas 2D(新版本,但坑更多)
<canvas type="2d" id="newCanvas" />
// 获取Canvas实例的仪式
const query = uni.createSelectorQuery()
query.select('#newCanvas')
.fields({ node: true }, (res) => {
const canvas = res.node
const ctx = canvas.getContext('2d')
// 设置Canvas尺寸的仪式
canvas.width = width * dpr
canvas.height = height * dpr
ctx.scale(dpr, dpr)
// 导出的仪式
uni.canvasToTempFilePath({
canvas: canvas, // 现在需要实例
// 但在微信小程序中可能还是失败
})
})
🚫 参数陷阱大集合
1. componentInstance:看似可选的必需参数
// 文档说这样就行
uni.canvasToTempFilePath({
canvasId: 'myCanvas'
})
// 实际上需要这样
uni.canvasToTempFilePath({
canvasId: 'myCanvas'
}, this) // 在页面中
// 或者
uni.canvasToTempFilePath({
canvasId: 'myCanvas'
}, getCurrentInstance()) // 在setup函数中
2. destWidth/destHeight:薛定谔的参数
// 在H5中:不设置就用Canvas原始尺寸
uni.canvasToTempFilePath({
canvasId: 'myCanvas'
// destWidth和destHeight可以不设置
})
// 在微信小程序中:不设置可能导出失败
uni.canvasToTempFilePath({
canvas: canvasInstance,
destWidth: 300, // 必须设置
destHeight: 200 // 必须设置
})
// 但是设置了又可能导致内存溢出
// 特别是在高DPI设备上
3. 设备像素比的迷惑行为
const dpr = uni.getSystemInfoSync().pixelRatio
// 看似正确的做法
canvas.width = width * dpr
canvas.height = height * dpr
ctx.scale(dpr, dpr)
uni.canvasToTempFilePath({
canvas: canvas,
destWidth: width * dpr, // 可能导致微信小程序崩溃
destHeight: height * dpr // 特别是在iPhone Pro Max上
})
// 实际需要的做法(通过无数次试错得出)
const safeDpr = Math.min(dpr, 2) // 限制DPR避免内存问题
// 然后在微信小程序中不设置destWidth/destHeight
// 但在H5中又必须设置
// 🤯
🔧 被迫的Workaround大全
1. 平台检测大法
// 被迫写出这样的代码
const platform = process.env.UNI_PLATFORM
if (platform === 'mp-weixin') {
// 微信小程序的特殊处理
exportWithWechatNative()
} else if (platform === 'h5') {
// H5的处理方式
exportWithUniApp()
} else {
// 其他平台... 祈祷能工作
exportAndPray()
}
2. 延迟导出大法
// 绘制完成后不能立即导出
ctx.fillRect(0, 0, 100, 100)
// 必须等待一段时间
setTimeout(() => {
uni.canvasToTempFilePath({
// ...
})
}, 500) // 这个时间是玄学,不同平台不一样
3. 原生API回退大法
// 在微信小程序中,uni-app API不行就用原生API
// @ts-ignore
if (typeof wx !== 'undefined' && wx.canvasToTempFilePath) {
wx.canvasToTempFilePath({
canvas: canvasInstance,
success: resolve,
fail: reject
})
} else {
// 回退到uni-app API
uni.canvasToTempFilePath(options, instance)
}
🎯 对比Android Canvas的优雅
// Android: 简洁、可靠、文档完善
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
// 绘制
canvas.drawRect(0f, 0f, 100f, 100f, paint)
// 导出
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
更多关于一个Android开发者的uni-app血泪史的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于一个Android开发者的uni-app血泪史的实战教程也可以访问 https://www.itying.com/category-93-b0.html
作为同样从Android转uni-app的开发者,非常理解你的痛苦。Canvas API确实是uni-app跨平台开发中最让人头疼的部分之一。
你提到的canvasToTempFilePath兼容性问题,本质上是uni-app在封装各平台Canvas差异时的不足。微信小程序的Canvas 2D与传统Canvas并存,H5的Canvas API又有所不同,导致uni-app难以提供统一的接口。
关于"canvas is empty"错误,这通常是因为Canvas渲染异步导致的。在微信小程序中,Canvas绘制完成后需要等待下一帧才能确保内容渲染完成。你的延迟导出方案是正确的,但更可靠的做法是使用requestAnimationFrame:
ctx.fillRect(0, 0, 100, 100)
requestAnimationFrame(() => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: (res) => {
console.log('终于成功了')
}
})
})
对于TypeScript支持问题,确实需要自己完善类型定义。可以扩展uni的接口:
interface CanvasToTempFilePathOptions {
canvasId?: string
canvas?: HTMLCanvasElement
x?: number
y?: number
width?: number
height?: number
destWidth?: number
destHeight?: number
fileType?: string
quality?: number
}

