更新记录
1.0.0(2026-06-10) 下载此版本
- 新增 print-image-generator easycom 组件。
- 支持图片选择、手动裁剪、自由裁剪、固定比例裁剪。
- 支持身份证、银行卡、户口本、A4、一寸照、二寸照等内置预设。
- 支持按裁剪框尺寸导出,或按 widthMM / heightMM / dpi 打印尺寸导出。
- 支持文字、时间、地点、多行水印和业务字段动态水印。
- 支持 watermarkOnly 仅添加水印模式。
平台兼容性
uni-app(3.8.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| × | √ | - | - | - | - | - | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | - | - | - | - | - | - | - | - | - | - | - |
打印图片裁剪生成器
bushicxy-printimageg 是一个 uni-app Vue3 前端组件插件,用于在业务页面中完成图片选择、手动裁剪、固定比例裁剪、旋转、双指缩放、水印叠加,以及按打印尺寸导出临时图片。
它适合身份证、银行卡、户口本、A4 文件、一寸照、二寸照、业务凭证、现场拍照留痕等场景。组件只生成本地临时图片路径,不会自动上传,也不会自动保存到相册。
特性
- 支持
uni.chooseImage选择相册或相机图片。 - 支持手动裁剪、自由裁剪、固定比例裁剪、固定裁剪框裁剪。
- 支持图片拖动、双指缩放、90 度旋转和还原。
- 支持身份证、银行卡、户口本、A4、一寸照、二寸照等内置尺寸。
- 支持自定义毫米尺寸和 DPI,适合打印图片导出。
- 支持无水印、有水印、仅水印不裁剪三种常见模式。
- 支持文字、时间、地点、多行水印。
- 支持在上传前按业务字段动态组装水印,例如客户、订单、定位、办理用途。
- 支持
easycom,安装到uni_modules后无需手动 import。 - 纯前端实现,不依赖云函数,不依赖原生插件。
目录结构
uni_modules/bushicxy-printimageg
├── package.json
├── readme.md
├── changelog.md
└── components
└── print-image-generator
└── print-image-generator.vue
安装方式
方式一:复制插件目录
把整个目录复制到项目的 uni_modules 下:
你的项目
└── uni_modules
└── bushicxy-printimageg
然后在页面中直接使用:
<print-image-generator ref="printGenRef"></print-image-generator>
方式二:插件市场导入
发布到 DCloud 插件市场后,可以通过 HBuilderX 的插件市场导入到当前项目。导入后仍然使用同一个组件标签:
<print-image-generator ref="printGenRef"></print-image-generator>
快速开始
<template>
<view>
<button @click="handleChoose">选择并裁剪</button>
<print-image-generator ref="printGenRef"></print-image-generator>
</view>
</template>
<script setup>
import { ref } from 'vue'
const printGenRef = ref()
const handleChoose = async () => {
const filePath = await printGenRef.value.chooseAndGenerate({
preset: 'idCard'
})
console.log('生成后的图片路径:', filePath)
}
</script>
完整页面示例
<template>
<view>
<button @click="handleFreeCrop">自由裁剪无水印</button>
<button @click="handleIdCardWatermark">身份证裁剪加水印</button>
<button @click="handleWatermarkOnly">原图只加水印</button>
<image v-if="resultPath" :src="resultPath" mode="aspectFit"></image>
<button v-if="resultPath" @click="handleSave">保存到相册</button>
<print-image-generator ref="printGenRef"></print-image-generator>
</view>
</template>
<script setup>
import { ref } from 'vue'
const printGenRef = ref()
const resultPath = ref('')
const generate = async (options) => {
try {
resultPath.value = await printGenRef.value.chooseAndGenerate(options)
uni.showToast({ title: '生成成功', icon: 'success' })
} catch (error) {
if (error && error.message === 'crop canceled') return
uni.showToast({ title: '生成失败', icon: 'none' })
console.error(error)
}
}
const handleFreeCrop = () => {
generate({
manualCrop: true,
lockCropRatio: false,
lockedCropBox: false,
watermark: null
})
}
const handleIdCardWatermark = () => {
generate({
preset: 'idCard',
watermark: {
text: '仅供办理业务使用',
color: 'rgba(255, 0, 0, 0.35)',
size: 24,
showTime: false,
showLocation: false
}
})
}
const handleWatermarkOnly = () => {
generate({
watermarkOnly: true,
watermark: {
text: '现场拍摄',
location: '上海市',
color: '#FFFFFF'
}
})
}
const handleSave = () => {
uni.saveImageToPhotosAlbum({
filePath: resultPath.value,
success: () => uni.showToast({ title: '已保存到相册', icon: 'success' })
})
}
</script>
API
组件通过 ref 暴露两个方法:
defineExpose({
generate,
chooseAndGenerate
})
chooseAndGenerate(options)
选择图片并生成结果图片。内部会先调用 uni.chooseImage,拿到图片路径后再调用 generate。
const filePath = await printGenRef.value.chooseAndGenerate({
preset: 'idCard'
})
可以通过 chooseOptions 传入 uni.chooseImage 参数:
const filePath = await printGenRef.value.chooseAndGenerate({
preset: 'idCard',
chooseOptions: {
count: 1,
sourceType: ['album', 'camera']
}
})
generate(options)
使用已有图片路径生成结果图片。适合你已经通过业务逻辑拿到本地图片路径的情况。
const filePath = await printGenRef.value.generate({
imagePath: tempFilePath,
preset: 'idCard'
})
常用模式
自由裁剪无水印
裁剪框可以拖动,也可以拖拽边角自由调整大小。
await printGenRef.value.chooseAndGenerate({
manualCrop: true,
lockCropRatio: false,
lockedCropBox: false,
watermark: null
})
自由裁剪加水印
await printGenRef.value.chooseAndGenerate({
manualCrop: true,
lockCropRatio: false,
lockedCropBox: false,
watermark: {
text: '仅供办理业务使用',
color: 'rgba(255, 0, 0, 0.35)',
size: 24,
showTime: false,
showLocation: false
}
})
身份证裁剪无水印
await printGenRef.value.chooseAndGenerate({
preset: 'idCard',
watermark: null
})
身份证裁剪加水印
await printGenRef.value.chooseAndGenerate({
preset: 'idCard',
watermark: {
text: '仅供办理业务使用',
color: 'rgba(255, 0, 0, 0.35)',
size: 24,
showTime: false,
showLocation: false
}
})
固定比例但允许调整裁剪框
例如身份证比例固定,但允许用户放大或缩小裁剪框。
await printGenRef.value.chooseAndGenerate({
preset: 'idCard',
lockedCropBox: false,
lockCropRatio: true
})
A4 打印尺寸导出
outputMode: 'print' 会按 widthMM / heightMM / dpi 计算导出像素尺寸。
await printGenRef.value.chooseAndGenerate({
preset: 'a4',
outputMode: 'print',
dpi: 150,
maxOutputSize: 1600
})
一寸照导出
await printGenRef.value.chooseAndGenerate({
preset: 'idPhoto1',
outputMode: 'print',
dpi: 300,
maxOutputSize: 1200
})
自动居中裁剪,不显示裁剪界面
manualCrop: false 会跳过手动裁剪界面,按目标比例从图片中心裁剪。
await printGenRef.value.chooseAndGenerate({
preset: 'idPhoto1',
manualCrop: false,
outputMode: 'print',
dpi: 300
})
只给原图添加水印,不裁剪
watermarkOnly: true 会显示确认界面,但不会显示裁剪框。
await printGenRef.value.chooseAndGenerate({
watermarkOnly: true,
watermark: {
text: '现场拍摄',
location: '上海市',
color: '#FFFFFF'
}
})
上传前动态组装水印
水印配置可以在调用 generate 或 chooseAndGenerate 前自己整理。比如业务组件里用 props.watermark、props.watermarkLocation、props.type 这些字段动态决定是否裁剪、裁剪预设和水印内容。
这个方式适合封装上传组件:先选图,再按资料类型裁剪或加水印,最后把生成后的图片路径交给上传逻辑。
const getWatermarkConfig = () => {
if (!props.watermark) return null
if (typeof props.watermark === 'string') {
return {
location: props.watermark
}
}
if (Array.isArray(props.watermark)) {
return {
items: props.watermark,
location: props.watermarkLocation
}
}
const config = typeof props.watermark === 'object' ? { ...props.watermark } : {}
if (props.watermarkLocation && !config.location) {
config.location = props.watermarkLocation
}
return config
}
const getUploadFilePath = (filePath) => {
const presetMap = {
idcard: 'idCard',
bankcard: 'bankCard',
household: 'householdRegister',
a4: 'a4',
a4HalfShort: 'a4HalfShort'
}
const preset = presetMap[props.type]
const watermark = getWatermarkConfig()
if ((!preset && !watermark) || !printGenRef.value) {
return Promise.resolve(filePath)
}
return printGenRef.value.generate({
imagePath: filePath,
preset,
outputMode: preset ? 'print' : 'crop',
dpi: 300,
watermark,
watermarkOnly: false,
cropQualityScale: props.cropQualityScale,
maxOutputSize: getExportMaxOutputSize(preset)
})
}
数组水印可以这样传:
watermark: [
{ key: '业务:', value: '资料采集' },
{ key: '客户:', value: customerName },
{ key: '时间:', type: 'time' },
{ key: '地点:', type: 'location' },
'仅供办理业务使用'
]
组件会在生成图片时把 type: 'time' 替换成当前时间,把 type: 'location' 替换成 watermark.location 或默认地点。
内置预设
| preset | 含义 | 尺寸 | 默认行为 |
|---|---|---|---|
idCard |
身份证 | 85.6 x 54 mm | 固定比例,锁定裁剪框 |
bankCard |
银行卡 | 85.6 x 53.98 mm | 固定比例,锁定裁剪框 |
householdRegister |
户口本 | 143 x 105 mm | 固定比例,锁定裁剪框 |
a4 |
A4 纸 | 210 x 297 mm | 固定比例,锁定裁剪框 |
a4HalfShort |
A4 半页短边 | 210 x 140 mm | 固定比例,锁定裁剪框 |
idPhoto1 |
一寸照 | 25 x 35 mm | 固定比例,锁定裁剪框 |
idPhoto2 |
二寸照 | 35 x 49 mm | 固定比例,锁定裁剪框 |
预设等价于自动合并这些配置:
{
widthMM: 85.6,
heightMM: 54,
lockCropRatio: true,
lockedCropBox: true
}
不同预设的 widthMM 和 heightMM 不同,其他默认行为一致。传入的 options 会覆盖预设值。
自定义预设
preset 可以传对象:
await printGenRef.value.chooseAndGenerate({
preset: {
widthMM: 100,
heightMM: 60,
lockCropRatio: true,
lockedCropBox: false
}
})
也可以不传 preset,直接传尺寸:
await printGenRef.value.chooseAndGenerate({
widthMM: 100,
heightMM: 60,
lockCropRatio: true,
lockedCropBox: false
})
参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
imagePath |
string |
必填 | 要处理的本地图片路径,chooseAndGenerate 可不传 |
chooseOptions |
object |
- | 传给 uni.chooseImage 的参数,仅 chooseAndGenerate 使用 |
preset |
string/object |
- | 内置预设名或自定义尺寸对象 |
manualCrop |
boolean |
true |
是否显示手动裁剪界面 |
widthMM |
number |
25 |
打印宽度,单位 mm |
heightMM |
number |
35 |
打印高度,单位 mm |
dpi |
number |
300 |
打印 DPI,主要用于 outputMode: 'print' |
outputMode |
'crop'/'print' |
'crop' |
crop 按裁剪框视觉尺寸导出,print 按 mm + dpi 导出 |
lockCropRatio |
boolean |
false |
是否锁定裁剪框比例 |
lockedCropBox |
boolean |
false |
是否锁定裁剪框,不允许拖动和缩放 |
resizeFromCenter |
boolean |
false |
锁比例缩放时是否从中心缩放 |
maxImageScale |
number |
4 |
图片双指缩放最大倍数 |
maxOutputSize |
number |
2400 |
导出图片最长边限制,防止 canvas 过大 |
cropQualityScale |
number |
2 |
outputMode: 'crop' 时的导出清晰度倍率 |
outputPixelRatio |
number |
- | cropQualityScale 的兼容别名 |
exportDelay |
number |
100 |
canvas draw 后延迟导出的时间,单位 ms |
exportTimeout |
number |
8000 |
canvas 导出超时时间,单位 ms |
showLoading |
boolean |
true |
生成阶段是否显示 loading |
watermark |
object/string/array/boolean/null |
null |
水印配置 |
watermarkOnly |
boolean |
false |
是否跳过裁剪,仅给整张图添加水印 |
裁剪参数关系
| 配置 | 效果 |
|---|---|
lockCropRatio: false + lockedCropBox: false |
自由裁剪,裁剪框可拖动和自由缩放 |
lockCropRatio: true + lockedCropBox: false |
固定比例裁剪,裁剪框可拖动和等比缩放 |
lockCropRatio: true + lockedCropBox: true |
固定比例裁剪,裁剪框固定,用户只移动和缩放图片 |
manualCrop: false |
不显示裁剪界面,自动按目标比例居中裁剪 |
watermarkOnly: true |
不显示裁剪框,只确认水印位置并导出整图 |
导出尺寸说明
outputMode: 'crop'
默认模式。导出尺寸基于用户看到的裁剪框视觉尺寸,再乘以 cropQualityScale。
await printGenRef.value.chooseAndGenerate({
outputMode: 'crop',
cropQualityScale: 2
})
适合业务预览、资料上传、普通图片压缩等场景。
outputMode: 'print'
打印模式。导出尺寸按毫米和 DPI 计算:
像素宽度 = widthMM / 25.4 * dpi
像素高度 = heightMM / 25.4 * dpi
示例:
await printGenRef.value.chooseAndGenerate({
widthMM: 25,
heightMM: 35,
dpi: 300,
outputMode: 'print'
})
maxOutputSize 会限制导出图片最长边,避免小程序端 canvas 过大导致失败。
水印配置
对象写法
watermark: {
text: '仅供办理业务使用',
location: '上海市',
remark: '业务受理',
color: '#FFFFFF',
shadowColor: 'rgba(0, 0, 0, 0.55)',
size: 24,
padding: 24,
lineHeight: 36,
bottomSafeGap: 32,
showTime: true,
showLocation: true
}
字段说明:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
boolean |
true |
传 false 时关闭水印 |
text |
string |
- | 第一行自定义文字 |
time / datetime |
string |
当前时间 | 时间文字 |
location |
string |
未知地点 |
地点文字 |
remark |
string |
- | 备注文字 |
lines |
string[] |
- | 完全自定义多行文字,优先级最高 |
items |
array |
- | 按对象数组生成多行文字 |
color |
string |
#FFFFFF |
水印文字颜色 |
shadowColor |
string |
rgba(0, 0, 0, 0.55) |
文字阴影颜色 |
size |
number |
根据图片宽度计算 | 字号,单位 px |
padding |
number |
根据字号计算 | 左侧和底部内边距 |
lineHeight |
number |
根据字号计算 | 多行水印行高 |
bottomSafeGap |
number |
根据行高计算 | 距离图片底部的额外间距 |
showTime |
boolean |
true |
是否显示时间 |
showLocation |
boolean |
true |
是否显示地点 |
数组写法
watermark: [
{ key: '时间:', type: 'time' },
{ key: '地点:', type: 'location' },
'仅供办理业务使用'
]
数组写法的每一项可以是字符串,也可以是对象。对象支持:
| 字段 | 说明 |
|---|---|
key / label / name |
行首文案,例如 客户: |
value |
当前行的值 |
type: 'time' |
当 value 为空时,自动使用当前时间 |
type: 'location' |
当 value 为空时,自动使用 watermark.location |
lines 写法
watermark: {
lines: [
'仅供办理业务使用',
'2026-06-10 12:00',
'上海市'
],
color: '#FFFFFF'
}
字符串写法
字符串会被当作 location 使用:
watermark: '上海市'
关闭水印
watermark: null
或者:
watermark: {
enabled: false
}
结果保存
组件返回的是临时图片路径:
const filePath = await printGenRef.value.chooseAndGenerate(options)
如果需要保存到相册:
uni.saveImageToPhotosAlbum({
filePath,
success: () => uni.showToast({ title: '已保存到相册', icon: 'success' }),
fail: (error) => {
uni.showToast({ title: '保存失败', icon: 'none' })
console.error(error)
}
})
保存到相册需要平台授权。建议在业务页面里统一处理授权失败、用户拒绝、系统设置页跳转等逻辑。
错误处理
推荐用 try/catch 包裹调用:
try {
const filePath = await printGenRef.value.chooseAndGenerate({
preset: 'idCard'
})
console.log(filePath)
} catch (error) {
if (error && error.message === 'crop canceled') {
return
}
uni.showToast({ title: '生成失败', icon: 'none' })
console.error(error)
}
常见错误:
| 错误 | 可能原因 |
|---|---|
imagePath is required |
调用 generate 时没有传 imagePath |
crop canceled |
用户点击了裁剪界面的取消 |
getImageInfo failed |
图片路径不可用,或当前平台无法读取图片信息 |
canvas export timeout |
canvas 生成超时,可能图片太大或平台性能不足 |
canvasToTempFilePath failed |
canvas 导出失败,可能 canvas 未绘制完成或平台限制 |
平台和权限
当前组件主要面向 uni-app Vue3 和微信小程序场景。发布到其他端前,建议用对应开发者工具或真机确认 canvas 导出、相册权限和图片路径表现。
| 平台/框架 | 当前状态 | 说明 |
|---|---|---|
| uni-app Vue3 | 支持 | 当前组件基于 <script setup> 和 defineExpose 实现 |
| 微信小程序 | 支持 | 当前主要验证目标 |
| App-Vue | 兼容待测 | 使用的是普通 Vue 页面能力,不支持 nvue |
| H5 | 兼容待测 | canvas 导出和跨域图片有浏览器限制,保存相册不可用 |
| 支付宝小程序 | 兼容待测 | canvas 已同时写入 id 和 canvas-id,仍建议实测 |
| 百度/抖音/QQ/快手小程序 | 兼容待测 | 依赖各端 canvas 和图片 API 表现 |
| Vue2 | 不支持 | 需要改写为 Vue2 Options API |
| nvue | 不支持 | nvue canvas 不是当前写法 |
| uni-app x | 不支持 | 当前不是 UTS/uni-app x 组件 |
组件使用以下 uni API:
uni.chooseImageuni.getImageInfouni.getSystemInfoSyncuni.createCanvasContextuni.canvasToTempFilePathuni.showLoadinguni.hideLoading
如果使用相机、相册或保存相册,平台可能会触发相应权限。组件本身不自动保存图片到相册,保存权限由业务页面自行处理。
注意事项
- 组件必须放在页面模板中,因为 canvas 导出需要组件实例上下文。
- 建议页面中只放一个组件实例,并通过
ref调用。 - 高 DPI 大尺寸图片容易导致 canvas 过大,建议合理设置
maxOutputSize。 watermark默认叠加到裁剪结果上,不会自动跳过裁剪。- 只有传
watermarkOnly: true时,组件才会跳过裁剪框,仅导出整图水印。 - 小程序端不同基础库、不同机型的 canvas 行为可能存在差异,建议真机验证。
发布到插件市场前
当前插件 ID 使用了 bushicxy-printimageg。如果你发布页面填写了其他插件 ID,发布前需要同步修改:
uni_modules/bushicxy-printimageg目录名。package.json里的id。package.json里的repository、contact等发布信息。- 插件市场展示名称、截图、示例项目和说明文案。
插件 ID 建议使用 作者ID-插件英文名 的格式,并保持目录名和 package.json 的 id 一致。
FAQ
为什么组件不自动保存到相册?
保存到相册属于业务动作,通常需要处理授权、失败重试、用户提示等流程。组件只返回生成后的临时路径,让业务页面自行决定保存、上传或预览。
传了 watermark 后还会裁剪吗?
会。watermark 默认只是叠加水印,不改变裁剪流程。只有传 watermarkOnly: true 才会跳过裁剪。
想要无水印怎么写?
不传 watermark,或传 watermark: null。
想让裁剪框可以拖动怎么写?
{
lockedCropBox: false
}
如果还想自由改变宽高比例:
{
lockCropRatio: false,
lockedCropBox: false
}
导出图片太大失败怎么办?
降低 dpi、降低 cropQualityScale,或设置更小的 maxOutputSize。
await printGenRef.value.chooseAndGenerate({
preset: 'a4',
outputMode: 'print',
dpi: 150,
maxOutputSize: 1600
})

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 2
赞赏 0
下载 12209969
赞赏 1918
赞赏
京公网安备:11010802035340号