vue-cropper实现图片裁剪

一、什么是vue-cropper?

​​Vue-Cropper​​ 是一个基于 Vue.js 的图片裁剪组件库,专为 Web 应用设计。当你在网上搜索的时候发现还有一个叫cropper的库,下面是他们的区别:

特性 cropper.js vue-cropper
框架依赖 纯 JavaScript,无框架依赖 专为 Vue.js 设计
包体积 ~200 KB (含样式) ~45 KB (压缩后)
API 调用方式 原生 DOM 操作 Vue 组件式 API
响应式支持 需手动实现 原生支持 Vue 响应式
功能完整性 基础裁剪+扩展插件 基础裁剪+常用扩展
学习曲线 较高(需熟悉原生 API) 较低(Vue 开发者友好)
社区生态 GitHub 32k+ Star GitHub 3.5k+ Star
移动端支持 需额外配置 原生优化

vue-cropper的优势:

• 纯前端实现,减轻服务器压力

• 响应式设计,完美适配移动端

• 灵活的配置选项

• 实时预览功能

• 支持多种输出格式

我正在开发的项目是基于vue3的,所以就对于vue-cropper进行讲解

二、快速上手

安装

//npm 安装
npm install vue-cropper
//yarn 安装
yarn add vue-cropper

引入

//Vue 3 组件内引入

npm install vue-cropper@next
import 'vue-cropper/dist/index.css'
import { VueCropper }  from "vue-cropper";
//Vue3 全局引入

import VueCropper from 'vue-cropper'; 
import 'vue-cropper/dist/index.css'

const app = createApp(App)
app.use(VueCropper)
app.mount('#app')

示例代码

<template>
  <div class="container">
    <input type="file" @change="handleFileChange" />
    <vue-cropper
      ref="cropper"
      :img="imageSrc"
      :auto-crop="false"
      :aspect-ratio="16/9"
    />
    <button @click="getResult">获取裁剪结果</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const imageSrc = ref('')
const cropper = ref(null)

const handleFileChange = (e) => {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.onload = (e) => {
    imageSrc.value = e.target.result
  }
  reader.readAsDataURL(file)
}

const getResult = () => {
  cropper.value.getCropBlob(blob => {
    const formData = new FormData()
    formData.append('file', blob)
    // 上传逻辑...
  })
}
</script>

三、配置详解

基础配置项

const options = reactive({
  img: '',          // 图片源地址
  outputSize: 1,    // 输出质量(0-1)
  outputType: 'png',// 输出格式:png | jpeg | webp
  info: true,       // 显示裁剪信息
  canScale: true,   // 允许缩放
  autoCrop: true,   // 自动生成裁剪框
  fixed: false,     // 固定裁剪框尺寸
  fixedNumber: [4, 3], // 固定比例 [宽, 高]
  full: false       // 是否全屏模式
})

比例锁定实践

固定16:9的案例:

<vue-cropper
  :fixed="true"
  :fixed-number="[16, 9]"
  :img="imageSrc"
  @realTime="handlePreview"
/>

输出格式控制

const outputOptions = {
  types: ['png', 'jpeg', 'webp'],
  quality: {
    jpeg: 0.8,
    webp: 0.7
  }
}

const handleExport = (type) => {
  cropper.value.getCropData(type, (dataURL) => {
    const link = document.createElement('a')
    link.download = `cropped.${type}`
    link.href = dataURL
    link.click()
  })
}

四、案例

用户头像上传组件

<template>
  <div class="avatar-editor">
    <n-upload
      accept="image/*"
      :before-upload="handleUpload"
    >
      <n-button>选择头像</n-button>
    </n-upload>
    
    <n-modal v-model:show="showCropper">
      <n-card style="width: 600px">
        <vue-cropper
          ref="cropper"
          :img="tempImage"
          :fixed="true"
          :fixed-number="[1, 1]"
          :output-size="0.8"
        />
        <div class="action-buttons">
          <n-button @click="rotate(-90)">左旋转</n-button>
          <n-button @click="rotate(90)">右旋转</n-button>
          <n-button @click="saveAvatar">保存</n-button>
        </div>
      </n-card>
    </n-modal>
  </div>
</template>

<script setup>
// 完整业务逻辑
const showCropper = ref(false)
const tempImage = ref('')

const handleUpload = async (file) => {
  const reader = new FileReader()
  reader.onload = (e) => {
    tempImage.value = e.target.result
    showCropper.value = true
  }
  reader.readAsDataURL(file)
  return false
}

const rotate = (degree) => {
  cropper.value.rotate(degree)
}

const saveAvatar = async () => {
  cropper.value.getCropBlob(async (blob) => {
    const formData = new FormData()
    formData.append('avatar', blob)
    
    try {
      await axios.post('/api/upload-avatar', formData)
      message.success('头像更新成功')
      showCropper.value = false
    } catch (error) {
      message.error('上传失败')
    }
  })
}
</script>

五、扩展

主题自定义

/* 自定义主题 */
.vue-cropper {
  --crop-border: 2px dashed #f00;
  --preview-bg: #f8f9fa;

  .crop-box {
    box-shadow: 0 0 10px rgba(0,0,0,0.5);
  }

  .info-item {
    color: #1890ff;
    font-size: 12px;
  }
}

性能优化技巧

// 压缩处理函数
const optimizeImage = (blob) => {
  return new Promise((resolve) => {
    const img = new Image()
    img.onload = () => {
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width = 800
      canvas.height = (800 * img.height) / img.width
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
      canvas.toBlob(resolve, 'image/webp', 0.8)
    }
    img.src = URL.createObjectURL(blob)
  })
}

移动端适配方案

<vue-cropper
  :touch="true"
  :movable="false"
  :zoom-on-touch="true"
  :zoom-on-wheel="false"
/>
/* 移动端优化 */
@media (max-width: 768px) {
  .vue-cropper {
    --crop-box-width: 300px;
    --crop-box-height: 300px;
    
    .action-buttons {
      flex-direction: column;
      button {
        margin: 5px 0;
      }
    }
  }
}

六、常见问题

裁剪框比例不生效?

解决方案:

// 确保同时配置
fixed: true,
fixedNumber: [3, 2], 
aspectRatio: 3/2

图片显示变形?

处理方案:

const checkImage = (file) => {
  return new Promise((resolve) => {
    const img = new Image()
    img.onload = () => {
      const ratio = img.width / img.height
      if (Math.abs(ratio - 3/2) > 0.1) {
        message.warning('建议选择3:2比例的图片')
      }
      resolve(true)
    }
    img.src = URL.createObjectURL(file)
  })
}

七、props

以下是全部的参数以及取值要求

名称 功能 默认值 可选值
img 裁剪图片的地址 url 地址, base64, blob
outputSize 裁剪生成图片的质量 1 0.1 ~ 1
outputType 裁剪生成图片的格式 jpg (需传入 jpeg) jpeg, png, webp
info 裁剪框的大小信息 true true, false
canScale 图片是否允许滚轮缩放 true true, false
autoCrop 是否默认生成截图框 false true, false
autoCropWidth 默认生成截图框宽度 容器的 80% 0 ~ max
autoCropHeight 默认生成截图框高度 容器的 80% 0 ~ max
fixed 是否开启截图框宽高固定比例 false true, false
fixedNumber 截图框的宽高比例(开启 fixed 生效) [1, 1] [ 宽度 , 高度 ]
full 是否输出原图比例的截图 false true, false
fixedBox 固定截图框大小 不允许改变 true, false
canMove 上传图片是否可以移动 true true, false
canMoveBox 截图框能否拖动 true true, false
original 上传图片按照原始比例渲染 false true, false
centerBox 截图框是否被限制在图片里面 false true, false
high 是否按照设备的 dpr 输出等比例图片 true true, false
infoTrue 展示真实输出图片宽高 false true, false
maxImgSize 限制图片最大宽度和高度 2000 0 ~ max
enlarge 图片根据截图框输出比例倍数 1 0 ~ max(建议不要太大)
mode 图片默认渲染方式 contain contain, cover, 100px, 100% auto
limitMinSize 裁剪框限制最小区域 10 Number, Array, String
fillColor 导出时背景颜色填充 #ffffff, white

你可能感兴趣的:(vue.js,前端,javascript)