更新记录
v1.0.0(2026-08-18) 下载此版本
首次发布。支持多图裁剪、单指拖动、双指缩放、图片旋转、裁剪比例切换、删除图片及顺序导出;适配微信小程序、Android和iOS,支持uni-app Vue2和Vue3。
平台兼容性
uni-app(4.71)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | × | 5.0 | 13 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2.21.0 | - | - | - | - | - | - | - | - | - | - | - |
Wty 多图图片裁剪
wty-image-cropper 是经典 uni-app 的开源多图裁剪插件。插件使用 Vue 页面、Vue 组件和 Canvas 实现,适用于内容发布、商品图片、头像和相册图片等移动端裁剪流程。
插件不负责相册选择、图片上传和保存相册。业务传入本地图片路径,插件返回裁剪后的临时文件路径。
主要功能
- 一次处理一到二十张图片,默认最多九张。
- 每张图片独立保存裁剪比例、位置、缩放和旋转状态。
- 支持单指拖动、双指中心缩放和顺时针九十度旋转。
- 支持原图比例、固定比例和业务自定义比例。
- 支持重置当前图片和删除误选图片。
- 支持 JPEG、PNG 输出。
- 支持限制输出最长边和 JPEG 质量。
- 按图片顺序逐张导出,控制大图处理时的内存峰值。
- 提供 Promise 页面 API和全屏组件两种接入方式。
- 不采集数据、不发送网络请求、不主动申请系统权限。
兼容性
| 项目 | 支持情况 | 最低版本或说明 |
|---|---|---|
| uni-app Vue2 | 支持 | HBuilderX 4.71+ |
| uni-app Vue3 | 支持 | HBuilderX 4.71+ |
| 微信小程序 | 支持 | 微信基础库 2.21+ |
| Android App-vue | 支持 | Android 5.0+ |
| iOS App-vue | 支持 | iOS 13.0+ |
| App-nvue | 不支持 | Canvas接口不同 |
| uni-app x | 不支持 | 本插件是经典 uni-app 插件 |
| H5 | 不支持 | 未纳入发布和测试范围 |
| 其他小程序 | 不支持 | 未纳入发布和测试范围 |
安装
HBuilderX导入
从 uni-app 插件市场导入后,目录应为:
uni_modules/
└── wty-image-cropper/
├── components/
├── js_sdk/
├── pages/
├── static/
├── index.d.ts
├── index.js
├── package.json
├── pages_init.json
└── readme.md
导入时确认 HBuilderX 弹出的页面合并窗口。pages_init.json 会向业务项目注册:
{
"path": "uni_modules/wty-image-cropper/pages/cropper/cropper",
"style": {
"navigationStyle": "custom",
"disableScroll": true,
"app-plus": {
"bounce": "none"
}
}
}
如果未出现合并窗口,请手动把上面页面配置添加到业务项目的 pages.json。
Promise 页面 API
页面 API负责打开插件自带全屏页面。裁剪成功时 Promise 返回结果;取消时 Promise 拒绝,错误码为 USER_CANCEL。
import { openImageCropper } from '@/uni_modules/wty-image-cropper/index.js'
export default {
methods: {
/**
* 打开多图裁剪页面并保存裁剪结果。
*
* @returns {Promise<void>} 裁剪流程 Promise。
*/
async cropImages() {
try {
const result = await openImageCropper({
images: this.localImagePaths,
ratios: [
{ label: '原图', ratio: null },
{ label: '1:1', ratio: 1 },
{ label: '3:4', ratio: 3 / 4 },
{ label: '4:3', ratio: 4 / 3 }
],
maxCount: 9,
maxOutputLongEdge: 2048,
quality: 0.92,
fileType: 'jpg'
})
this.croppedFiles = result.files
} catch (error) {
if (error.code !== 'USER_CANCEL') {
uni.showToast({ title: error.message, icon: 'none' })
}
}
}
}
}
同一时刻只能打开一个页面裁剪会话。重复调用会拒绝并返回 BUSY。
全屏组件
组件模式适合业务自己管理页面和显示状态。所在页面必须使用自定义导航栏,否则原生导航栏可能覆盖组件顶部区域。
<template>
<view>
<button @tap="showCropper">开始裁剪</button>
<wty-image-cropper
v-if="cropperVisible"
:images="localImagePaths"
:ratios="ratios"
:max-output-long-edge="2048"
:quality="0.92"
file-type="jpg"
@confirm="handleCropConfirm"
@cancel="handleCropCancel"
@error="handleCropError"
/>
</view>
</template>
<script>
import WtyImageCropper from '@/uni_modules/wty-image-cropper/components/wty-image-cropper/wty-image-cropper.vue'
export default {
components: {
WtyImageCropper
},
/**
* 创建组件接入示例状态。
*
* @returns {Record<string, unknown>} 页面状态。
*/
data() {
return {
cropperVisible: false,
localImagePaths: [],
croppedFiles: [],
ratios: [
{ label: '原图', ratio: null },
{ label: '1:1', ratio: 1 },
{ label: '3:4', ratio: 3 / 4 }
]
}
},
methods: {
/**
* 显示全屏裁剪组件。
*
* @returns {void}
*/
showCropper() {
this.cropperVisible = true
},
/**
* 保存裁剪结果并关闭组件。
*
* @param {Record<string, unknown>} result 裁剪结果。
* @returns {void}
*/
handleCropConfirm(result) {
this.croppedFiles = result.files
this.cropperVisible = false
},
/**
* 取消并关闭组件。
*
* @returns {void}
*/
handleCropCancel() {
this.cropperVisible = false
},
/**
* 处理组件裁剪错误。
*
* @param {Record<string, unknown>} error 标准裁剪错误。
* @returns {void}
*/
handleCropError(error) {
uni.showToast({ title: error.message, icon: 'none' })
}
}
}
</script>
组件显示期间不要修改 images。需要更换图片列表时,先销毁组件,再传入新数组重新创建。
完整选图接入
选图属于业务职责。下面代码与示例工程使用方式一致:
import { openImageCropper } from '@/uni_modules/wty-image-cropper/index.js'
export default {
methods: {
/**
* 选择图片并立即进入裁剪页面。
*
* @returns {void}
*/
chooseAndCrop() {
uni.chooseMedia({
count: 9,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['original', 'compressed'],
/**
* 选图成功后打开裁剪页面并处理结果。
*
* @param {Record<string, unknown>} chooseResult 选图结果。
* @returns {Promise<void>} 裁剪流程 Promise。
*/
success: async (chooseResult) => {
const images = chooseResult.tempFiles.map((file) => file.tempFilePath || file.path)
try {
const cropResult = await openImageCropper({ images })
this.uploadImages(cropResult.files)
} catch (error) {
if (error.code !== 'USER_CANCEL') {
uni.showToast({ title: error.message, icon: 'none' })
}
}
}
})
},
/**
* 把裁剪结果交给业务上传流程。
*
* @param {Array<Record<string, unknown>>} files 裁剪文件数组。
* @returns {void}
*/
uploadImages(files) {
this.pendingUploadFiles = files
}
}
}
微信小程序基础库 2.21+ 推荐使用 uni.chooseMedia。App需要在业务项目 manifest.json 中启用相机和相册模块。插件本身不调用选图接口,不申请这些权限。
比例配置
默认比例
不传 ratios 时使用:原图、1:1、3:4、4:3。
固定比例
只传一个比例时,底部比例栏自动隐藏:
const options = {
images: this.localImagePaths,
ratios: [
{ label: '3:4', ratio: 3 / 4 }
]
}
自定义比例
const options = {
images: this.localImagePaths,
ratios: [
{ label: '原图', ratio: null },
{ label: '5:7', ratio: 5 / 7 },
{ label: '16:9', ratio: 16 / 9 }
],
initialRatioIndex: 1
}
ratio 使用宽除以高。null 表示当前图片的原图比例。
参数
| 参数 | 类型 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|
images |
string[] |
必填 | 一到 maxCount 项 |
本地临时路径或应用可读取路径 |
ratios |
CropRatio[] |
原图、1:1、3:4、4:3 | 一到十二项 | 可选裁剪比例 |
initialRatioIndex |
number |
0 |
比例数组有效位置 | 初始比例位置 |
maxCount |
number |
9 |
1到20 | 最大输入图片数 |
maxOutputLongEdge |
number |
2048 |
64到8192 | 输出最长边像素上限 |
quality |
number |
0.92 |
0.01到1 | JPEG输出质量,PNG不保证使用此参数 |
fileType |
string |
jpg |
jpg、png |
输出格式 |
maxScale |
number |
4 |
1到10 | 相对最小覆盖缩放的最大倍数 |
allowRotate |
boolean |
true |
- | 是否显示旋转操作 |
allowDelete |
boolean |
true |
- | 是否允许删除图片,最后一张不可删除 |
theme |
CropperTheme |
黑色主题 | - | 页面颜色配置 |
CropRatio
| 字段 | 类型 | 说明 |
|---|---|---|
label |
string |
底部显示名称 |
ratio |
number | null |
宽除以高;null 表示原图比例 |
CropperTheme
| 字段 | 默认值 | 说明 |
|---|---|---|
backgroundColor |
#050505 |
页面主背景 |
foregroundColor |
#ffffff |
主要文字颜色 |
accentColor |
#ff2442 |
完成按钮和选中状态颜色 |
maskColor |
rgba(0, 0, 0, 0.58) |
遮罩预留颜色配置 |
组件事件
| 事件 | 参数 | 触发时机 |
|---|---|---|
ready |
{ count } |
首张图片加载并完成初始化 |
active-change |
{ index, sourceIndex } |
当前图片切换完成 |
delete |
{ sourcePath, sourceIndex } |
删除一张图片 |
confirm |
CropResult |
全部图片导出成功 |
cancel |
{ code, message } |
用户点击取消 |
error |
{ code, message, details, index? } |
参数、图片或导出发生错误 |
EXPORT_FAILED 不会自动关闭组件或页面。用户可以再次点击完成重试。
返回结果
{
files: [
{
sourcePath: '原始本地路径',
sourceIndex: 0,
tempFilePath: '裁剪结果临时路径',
width: 1536,
height: 2048,
ratio: 0.75,
rotation: 90
}
],
deleted: [
{
sourcePath: '被删除的原始路径',
sourceIndex: 1
}
]
}
files 按原始传入顺序返回,已删除图片不包含在内。sourceIndex 始终指向首次传入 images 时的位置。
错误码
| 错误码 | 含义 | 处理建议 |
|---|---|---|
INVALID_OPTIONS |
参数不合法 | 检查图片数量、路径、比例和输出参数 |
BUSY |
已有页面裁剪会话 | 等待当前会话完成或取消 |
ROUTE_NOT_REGISTERED |
插件页面未注册或跳转失败 | 检查 pages_init.json 和业务 pages.json |
IMAGE_LOAD_FAILED |
图片信息或预览读取失败 | 检查路径有效期、文件权限和格式 |
EXPORT_FAILED |
Canvas绘制或导出失败 | 降低图片数量、最长边或重试 |
USER_CANCEL |
用户主动取消 | 一般无需提示错误 |
错误对象结构:
type CropperError = Error & {
code: CropperErrorCode
details: Record<string, unknown>
}
输出规则
- 输出构图与裁剪框一致。
- 输出尺寸取裁剪区域可用源像素,不主动放大小图。
- 输出最长边不超过
maxOutputLongEdge。 - JPEG透明区域使用白色背景。
- PNG保留 Canvas 可以表达的透明通道。
- 多张图片按顺序导出,不并行创建多个大尺寸 Canvas。
- 图片方向通过
uni.getImageInfo和运行平台图片解码结果处理。
页面和安全区
Promise 页面 API已经配置自定义导航栏、禁止页面滚动和 App回弹。
组件模式页面需要设置:
{
"path": "pages/example/example",
"style": {
"navigationStyle": "custom",
"disableScroll": true,
"app-plus": {
"bounce": "none"
}
}
}
组件会读取状态栏和安全区,适配刘海屏及底部手势区域。
权限与隐私
插件只调用以下接口:
uni.getImageInfouni.createCanvasContextuni.canvasToTempFilePathuni.getWindowInfo或兼容接口- Promise页面模式下的
uni.navigateTo、uni.navigateBack
插件不调用相册、相机、定位、网络、存储和用户信息接口。
业务若使用 uni.chooseMedia,需要自行完成 Android、iOS和微信小程序隐私声明。具体内容见 privacy.md。
临时文件
tempFilePath 是临时文件路径。应用重启、运行环境清理缓存或小程序回收后,文件可能失效。
需要长期使用时,应在 confirm 或 Promise成功后立即:
- 上传到业务文件服务;或
- 调用目标平台支持的文件持久化接口。
插件不会自动上传,也不会自动保存到系统相册。
性能建议
- 社交图片建议
maxCount不超过九张。 - 普通发布场景建议最长边使用默认
2048。 - 低端设备避免使用
8192输出。 - 输入超大图片时优先让业务选图接口返回压缩图。
- 导出中不要返回页面或重复点击完成。
- Canvas导出失败时先降低
maxOutputLongEdge再重试。
Vue2和Vue3
组件内部使用 Options API,不依赖 Composition API。
- Vue3示例使用
createSSRApp。 - Vue2业务项目继续使用自身已有
new Vue入口,不需要修改插件源码。 - 两个版本的组件属性和事件完全一致。
- 不使用
v-model,避免 Vue2和Vue3双向绑定协议差异。
常见问题
页面 API返回 ROUTE_NOT_REGISTERED
检查业务 pages.json 是否存在插件裁剪页面。旧项目未自动合并时,需要手动添加本文安装章节中的页面配置。
顶部被原生导航栏遮挡
组件所在页面没有设置 navigationStyle: custom。页面 API不受此问题影响。
图片路径读取失败
确认路径仍在有效期内,且不是未配置下载域名的网络地址。推荐传入相册选择接口返回的本地临时路径。
大图导出失败
降低 maxOutputLongEdge,减少单次图片数量,或让业务选图时使用压缩图。不同设备的 Canvas内存上限不同。
取消为何进入 catch
Promise 页面 API参考 uni-app媒体接口行为,取消使用拒绝结果。通过 error.code === 'USER_CANCEL' 静默处理。
删除后怎样对应原图片
读取 sourceIndex。它始终对应首次传入数组的位置,不会因删除而改变。
修改比例后为什么图片会自动移动
插件会重新计算最小覆盖缩放并限制边界,避免裁剪框出现空白区域。
开源协议
插件使用 MIT License。工具栏图标来自 Lucide,使用 ISC License。完整文本见 license.md 和 third-party-notices.md。

收藏人数:
https://gitee.com/cserwang/wty-image-cropper
下载插件并导入HBuilderX
赞赏(0)
下载 0
赞赏 0
下载 12515110
赞赏 1943
赞赏
京公网安备:11010802035340号