不对,为什么保存的图片是空白的:downloadFile(url) {
return new Promise((resolve, reject) => {
if (url.startsWith('wxfile://')) {
resolve(url)
} else {
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200) resolve(res.tempFilePath)
else reject(new Error(`下载失败: ${res.statusCode}`))
},
fail: (err) => {
reject(new Error(`网络错误: ${JSON.stringify(err)}`))
}
})
}
})
},
getImageInfo(src) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src,
success: resolve,
fail: reject
})
})
},
getRect(selector) {
return new Promise((resolve, reject) => {
this.$nextTick(() => {
uni
.createSelectorQuery()
.in(this)
.select(selector)
.boundingClientRect((rect) => {
if (rect) resolve(rect)
else reject(new Error(`无法获取 ${selector} 的布局信息`))
})
.exec()
})
})
},
canvasToTempFilePath(canvas, opts) {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath(
{
...opts,
canvas,
success: (res) => resolve(res.tempFilePath),
fail: (err) => reject(err)
},
this
)
})
},
async saveAndPreview() {
if (!this.fileImage) {
return uni.showToast({ title: '请先上传图片', icon: 'none' })
}
showLoading('正在生成')
try {
// Step 1: 下载用户图片到本地
const imgLocalPath = await this.downloadFile(this.fileImage)
const modalLocalPath = await this.downloadFile(this.modalArr[this.currentIndex].imgUrl)
// Step 2: 获取原图信息
const imageInfo = await this.getImageInfo(imgLocalPath)
const { width: imgW, height: imgH } = imageInfo
// Step 3: 获取容器尺寸(.upload-view)
const rect = await this.getRect('.upload-view')
const viewW = rect.width
const viewH = rect.height
// Step 4: 设置 canvas 大小(放大2倍以提高清晰度)
const dpr = uni.getSystemInfoSync().pixelRatio || 2
const canvasW = viewW * dpr
const canvasH = viewH * dpr
// Step 5: 创建 2D Canvas 并获取 context
const query = uni.createSelectorQuery().in(this)
const res = await new Promise((resolve, reject) => {
query
.select('#previewCanvas')
.node((result) => {
resolve(result)
})
.exec()
})
const canvas = res.node
const ctx = canvas.getContext('2d')
// 设置 canvas 实际绘制分辨率
canvas.width = canvasW
canvas.height = canvasH
// 清屏并填充白色背景
ctx.clearRect(0, 0, canvasW, canvasH)
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, canvasW, canvasH)
// 计算图片显示尺寸(保持比例)
const imgAspect = imgW / imgH
const viewAspect = viewW / viewH
let displayWidth, displayHeight
if (imgAspect > viewAspect) {
displayWidth = viewW
displayHeight = viewW / imgAspect
} else {
displayHeight = viewH
displayWidth = viewH * imgAspect
}
// 缩放到 canvas 分辨率
displayWidth *= dpr
displayHeight *= dpr
// 中心点 + 偏移(也乘以 dpr)
const centerX = canvasW / 2 + this.imageTransform.translateX * dpr
const centerY = canvasH / 2 + this.imageTransform.translateY * dpr
// 开始绘制变换:平移 -> 旋转 -> 缩放(含镜像)
ctx.save()
ctx.translate(centerX, centerY)
ctx.rotate((this.imageTransform.rotate * Math.PI) / 180)
// 注意:scale 控制缩放与翻转(负值表示镜像)
ctx.scale(
this.imageTransform.scale * this.imageTransform.flipHorizontal * dpr,
this.imageTransform.scale * this.imageTransform.flipVertical * dpr
)
// 绘制用户图片(以中心为基准)
const img = canvas.createImage()
img.src = imgLocalPath
ctx.drawImage(
img,
-displayWidth / 2 / dpr / this.imageTransform.scale, // 调整坐标适应 scale
-displayHeight / 2 / dpr / this.imageTransform.scale,
displayWidth / dpr / this.imageTransform.scale,
displayHeight / dpr / this.imageTransform.scale
)
ctx.restore()
// 绘制模板层(覆盖整个 canvas)
const modalImg = canvas.createImage()
modalImg.src = modalLocalPath
ctx.drawImage(modalImg, 0, 0, canvasW, canvasH)
// Step 6: 导出为临时文件路径
setTimeout(async () => {
try {
// 将 canvas 内容转为临时文件路径
const tempFilePath = await this.canvasToTempFilePath(canvas, {
x: 0,
y: 0,
width: canvasW,
height: canvasH,
destWidth: canvasW,
destHeight: canvasH,
fileType: 'png',
quality: 1
})
// 调用你的上传逻辑
this.getPhotoUpload({
width: canvasW,
height: canvasH,
src: tempFilePath
})
uni.hideLoading()
} catch (err) {
console.error('导出失败:', err)
uni.hideLoading()
uni.showToast({ title: '保存失败,请查看控制台', icon: 'none' })
}
}, 500)
console.log('🔍 当前图片变换参数:', { ...this.imageTransform })
} catch (err) {
uni.hideLoading()
console.error('【详细错误】', {
message: err.message,
stack: err.stack,
name: err.name,
toString: err.toString()
})
uni.showToast({ title: '处理失败', icon: 'none' })
}
} 修复这个代码
最新发布