更新记录

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:13:44: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 jpgpng 输出格式
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.getImageInfo
  • uni.createCanvasContext
  • uni.canvasToTempFilePath
  • uni.getWindowInfo 或兼容接口
  • Promise页面模式下的 uni.navigateTouni.navigateBack

插件不调用相册、相机、定位、网络、存储和用户信息接口。

业务若使用 uni.chooseMedia,需要自行完成 Android、iOS和微信小程序隐私声明。具体内容见 privacy.md

临时文件

tempFilePath 是临时文件路径。应用重启、运行环境清理缓存或小程序回收后,文件可能失效。

需要长期使用时,应在 confirm 或 Promise成功后立即:

  1. 上传到业务文件服务;或
  2. 调用目标平台支持的文件持久化接口。

插件不会自动上传,也不会自动保存到系统相册。

性能建议

  • 社交图片建议 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.mdthird-party-notices.md

隐私、权限声明

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

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

插件不采集任何数据

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

许可协议

MIT License

Copyright (c) 2026 wty-image-cropper contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

暂无用户评论。