更新记录

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'
    }
})

上传前动态组装水印

水印配置可以在调用 generatechooseAndGenerate 前自己整理。比如业务组件里用 props.watermarkprops.watermarkLocationprops.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
}

不同预设的 widthMMheightMM 不同,其他默认行为一致。传入的 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 已同时写入 idcanvas-id,仍建议实测
百度/抖音/QQ/快手小程序 兼容待测 依赖各端 canvas 和图片 API 表现
Vue2 不支持 需要改写为 Vue2 Options API
nvue 不支持 nvue canvas 不是当前写法
uni-app x 不支持 当前不是 UTS/uni-app x 组件

组件使用以下 uni API:

  • uni.chooseImage
  • uni.getImageInfo
  • uni.getSystemInfoSync
  • uni.createCanvasContext
  • uni.canvasToTempFilePath
  • uni.showLoading
  • uni.hideLoading

如果使用相机、相册或保存相册,平台可能会触发相应权限。组件本身不自动保存图片到相册,保存权限由业务页面自行处理。

注意事项

  • 组件必须放在页面模板中,因为 canvas 导出需要组件实例上下文。
  • 建议页面中只放一个组件实例,并通过 ref 调用。
  • 高 DPI 大尺寸图片容易导致 canvas 过大,建议合理设置 maxOutputSize
  • watermark 默认叠加到裁剪结果上,不会自动跳过裁剪。
  • 只有传 watermarkOnly: true 时,组件才会跳过裁剪框,仅导出整图水印。
  • 小程序端不同基础库、不同机型的 canvas 行为可能存在差异,建议真机验证。

发布到插件市场前

当前插件 ID 使用了 bushicxy-printimageg。如果你发布页面填写了其他插件 ID,发布前需要同步修改:

  1. uni_modules/bushicxy-printimageg 目录名。
  2. package.json 里的 id
  3. package.json 里的 repositorycontact 等发布信息。
  4. 插件市场展示名称、截图、示例项目和说明文案。

插件 ID 建议使用 作者ID-插件英文名 的格式,并保持目录名和 package.jsonid 一致。

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
})

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机权限:通过 uni.chooseImage 使用 camera 来源选择图片时触发。 相册读取权限:通过 uni.chooseImage 从相册选择图片时触发。 相册写入权限:仅在业务方调用 uni.saveImageToPhotosAlbum 保存生成图片时触发。 插件本身不主动申请定位、通讯录、蓝牙、录音、网络请求等其他系统权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

本插件不采集用户数据,不上传图片,不发送任何服务器请求。 图片选择、裁剪、旋转、水印绘制、canvas 导出均在用户本地设备完成。插件仅返回生成后的本地临时图片路径,由接入方业务自行决定预览、保存或上传。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。