update at 2026-02-08 18:28:39
This commit is contained in:
131
miniprogram/utils/mp/canvas-export.js
Normal file
131
miniprogram/utils/mp/canvas-export.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const {
|
||||
canvasToTempFilePath,
|
||||
saveImageToPhotosAlbum,
|
||||
writeFile,
|
||||
openSetting,
|
||||
showModal,
|
||||
} = require('./wx-promisify')
|
||||
|
||||
function getWindowDpr() {
|
||||
if (typeof wx.getWindowInfo === 'function') {
|
||||
return wx.getWindowInfo().pixelRatio || 1
|
||||
}
|
||||
const info = wx.getSystemInfoSync()
|
||||
return info.pixelRatio || 1
|
||||
}
|
||||
|
||||
function queryCanvasNode(page, selector) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = wx.createSelectorQuery().in(page)
|
||||
query
|
||||
.select(selector)
|
||||
.fields({ node: true, size: true })
|
||||
.exec((result) => {
|
||||
const target = result && result[0]
|
||||
if (!target || !target.node) {
|
||||
reject(new Error('未找到导出画布节点'))
|
||||
return
|
||||
}
|
||||
resolve(target)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function writeSvgTempFile(svgString) {
|
||||
const path = `${wx.env.USER_DATA_PATH}/font2svg_preview_${Date.now()}.svg`
|
||||
await writeFile(path, svgString, 'utf8')
|
||||
return path
|
||||
}
|
||||
|
||||
async function exportSvgToPngByCanvas(page, options) {
|
||||
const {
|
||||
svgString,
|
||||
width,
|
||||
height,
|
||||
selector = '#exportCanvas',
|
||||
backgroundColor = '#ffffff',
|
||||
} = options
|
||||
|
||||
if (!svgString) {
|
||||
throw new Error('缺少 SVG 内容')
|
||||
}
|
||||
|
||||
const canvasNode = await queryCanvasNode(page, selector)
|
||||
const canvas = canvasNode.node
|
||||
const ctx = canvas.getContext('2d')
|
||||
const dpr = getWindowDpr()
|
||||
|
||||
const renderWidth = Math.max(1, Math.min(2048, Math.round(width || canvasNode.width || 1024)))
|
||||
const renderHeight = Math.max(1, Math.min(2048, Math.round(height || canvasNode.height || 1024)))
|
||||
|
||||
canvas.width = renderWidth * dpr
|
||||
canvas.height = renderHeight * dpr
|
||||
|
||||
if (typeof ctx.setTransform === 'function') {
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
||||
} else {
|
||||
ctx.scale(dpr, dpr)
|
||||
}
|
||||
|
||||
ctx.fillStyle = backgroundColor
|
||||
ctx.fillRect(0, 0, renderWidth, renderHeight)
|
||||
|
||||
const svgPath = await writeSvgTempFile(svgString)
|
||||
const image = canvas.createImage()
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
image.onload = resolve
|
||||
image.onerror = () => reject(new Error('加载 SVG 到画布失败'))
|
||||
image.src = svgPath
|
||||
})
|
||||
|
||||
ctx.drawImage(image, 0, 0, renderWidth, renderHeight)
|
||||
|
||||
const fileRes = await canvasToTempFilePath(
|
||||
{
|
||||
canvas,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: renderWidth,
|
||||
height: renderHeight,
|
||||
destWidth: renderWidth,
|
||||
destHeight: renderHeight,
|
||||
fileType: 'png',
|
||||
},
|
||||
page
|
||||
)
|
||||
|
||||
return fileRes.tempFilePath
|
||||
}
|
||||
|
||||
async function savePngToAlbum(filePath) {
|
||||
try {
|
||||
await saveImageToPhotosAlbum(filePath)
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
const errMsg = String(error && error.errMsg ? error.errMsg : error)
|
||||
const needAuth = errMsg.includes('auth deny') || errMsg.includes('authorize')
|
||||
|
||||
if (needAuth) {
|
||||
const modalRes = await showModal({
|
||||
title: '需要相册权限',
|
||||
content: '请在设置中开启“保存到相册”权限后重试。',
|
||||
confirmText: '去设置',
|
||||
})
|
||||
if (modalRes.confirm) {
|
||||
await openSetting()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
needAuth,
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exportSvgToPngByCanvas,
|
||||
savePngToAlbum,
|
||||
}
|
||||
Reference in New Issue
Block a user