Skip to content

图片裁剪示例

使用 Vue Cropper 实现图片裁剪和上传功能。

功能特性

  • ✂️ 图片裁剪 - 支持自由裁剪、固定比例裁剪
  • 🔄 旋转翻转 - 支持图片旋转和翻转
  • 🔍 缩放调整 - 支持图片缩放
  • 📤 实时预览 - 实时预览裁剪效果

安装依赖

bash
npm install vue-cropper

基础用法

vue
<template>
  <div class="cropper-container">
    <vue-cropper
      ref="cropperRef"
      :img="imageUrl"
      :output-size="1"
      :output-type="'jpeg'"
      :info="true"
      :can-scale="true"
      :auto-crop="true"
      :auto-crop-width="200"
      :auto-crop-height="200"
      :fixed="true"
      :fixed-number="[1, 1]"
    />
    
    <div class="actions">
      <el-button @click="rotateLeft">向左旋转</el-button>
      <el-button @click="rotateRight">向右旋转</el-button>
      <el-button @click="getCropBlob">获取裁剪结果</el-button>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { VueCropper } from 'vue-cropper'

const cropperRef = ref()
const imageUrl = ref('https://example.com/image.jpg')

const rotateLeft = () => {
  cropperRef.value.rotateLeft()
}

const rotateRight = () => {
  cropperRef.value.rotateRight()
}

const getCropBlob = () => {
  cropperRef.value.getCropBlob((blob) => {
    // 处理裁剪后的图片
    console.log(blob)
  })
}
</script>

完整示例

vue
<template>
  <div class="page-container">
    <div class="toolbar">
      <h2 class="page-title">图片裁剪示例</h2>
      <div class="actions">
        <el-upload
          accept="image/*"
          :auto-upload="false"
          :on-change="handleImageChange"
          :show-file-list="false"
        >
          <el-button type="primary">选择图片</el-button>
        </el-upload>
        
        <el-button @click="rotateLeft">
          <el-icon><RefreshLeft /></el-icon>向左旋转
        </el-button>
        <el-button @click="rotateRight">
          <el-icon><RefreshRight /></el-icon>向右旋转
        </el-button>
        <el-button @click="scaleX">
          水平翻转
        </el-button>
        <el-button @click="scaleY">
          垂直翻转
        </el-button>
        <el-button type="success" @click="getCropData">
          获取裁剪结果
        </el-button>
      </div>
    </div>
    
    <div class="cropper-wrapper">
      <div class="cropper-box">
        <vue-cropper
          ref="cropperRef"
          :img="imageUrl"
          :output-size="1"
          output-type="png"
          :info="true"
          :can-scale="true"
          :auto-crop="true"
          :auto-crop-width="300"
          :auto-crop-height="300"
          :fixed="fixed"
          :fixed-number="fixedNumber"
          :center-box="true"
          @real-time="realTime"
        />
      </div>
      
      <div class="preview-box">
        <h3>预览</h3>
        <div class="preview-img" :style="previewStyle">
          <img :src="previews.url" :style="previews.img">
        </div>
        
        <div class="options">
          <el-form label-width="100px">
            <el-form-item label="固定比例">
              <el-switch v-model="fixed" />
            </el-form-item>
            <el-form-item label="比例" v-if="fixed">
              <el-radio-group v-model="ratio">
                <el-radio-button :label="[1, 1]">1:1</el-radio-button>
                <el-radio-button :label="[16, 9]">16:9</el-radio-button>
                <el-radio-button :label="[4, 3]">4:3</el-radio-button>
              </el-radio-group>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </div>
    
    <!-- 裁剪结果弹窗 -->
    <el-dialog v-model="resultVisible" title="裁剪结果" width="400px">
      <img :src="resultUrl" style="width: 100%;">
      <template #footer>
        <el-button @click="resultVisible = false">关闭</el-button>
        <el-button type="primary" @click="uploadImage">上传</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { VueCropper } from 'vue-cropper'
import { RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'

const cropperRef = ref()
const imageUrl = ref('')
const fixed = ref(true)
const ratio = ref([1, 1])
const resultVisible = ref(false)
const resultUrl = ref('')
const previews = ref({})

const fixedNumber = computed(() => {
  return fixed.value ? ratio.value : undefined
})

const previewStyle = computed(() => {
  return {
    width: '300px',
    height: `${300 / ratio.value[0] * ratio.value[1]}px`,
    overflow: 'hidden'
  }
})

const handleImageChange = (file) => {
  const reader = new FileReader()
  reader.onload = (e) => {
    imageUrl.value = e.target.result
  }
  reader.readAsDataURL(file.raw)
}

const rotateLeft = () => {
  cropperRef.value.rotateLeft()
}

const rotateRight = () => {
  cropperRef.value.rotateRight()
}

const scaleX = () => {
  cropperRef.value.scaleX(-1)
}

const scaleY = () => {
  cropperRef.value.scaleY(-1)
}

const realTime = (data) => {
  previews.value = data
}

const getCropData = () => {
  cropperRef.value.getCropData((data) => {
    resultUrl.value = data
    resultVisible.value = true
  })
}

const uploadImage = () => {
  cropperRef.value.getCropBlob((blob) => {
    // 创建 FormData 上传
    const formData = new FormData()
    formData.append('file', blob, 'cropped.png')
    
    // 调用上传 API
    // uploadApi(formData)
    
    ElMessage.success('上传成功')
    resultVisible.value = false
  })
}
</script>

<style scoped>
.page-container {
  padding: 20px;
}

.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.actions {
  display: flex;
  gap: 10px;
}

.cropper-wrapper {
  display: flex;
  gap: 20px;
}

.cropper-box {
  flex: 1;
  height: 500px;
  background: #f5f5f5;
}

.preview-box {
  width: 350px;
}

.preview-img {
  background: #f5f5f5;
  margin-bottom: 20px;
}

.options {
  padding: 20px;
  background: #f9f9f9;
  border-radius: 4px;
}
</style>

Props 配置

参数类型默认值说明
imgString''图片地址
outputSizeNumber1输出图片质量 0-1
outputTypeString'jpeg'输出图片格式
autoCropBooleantrue是否自动裁剪
autoCropWidthNumber200裁剪框宽度
autoCropHeightNumber200裁剪框高度
fixedBooleanfalse是否固定比例
fixedNumberArray[1, 1]比例 [宽, 高]
centerBoxBooleanfalse裁剪框是否限制在图片内
canScaleBooleantrue是否允许缩放

方法

javascript
// 向左旋转
cropperRef.value.rotateLeft()

// 向右旋转
cropperRef.value.rotateRight()

// 水平翻转
cropperRef.value.scaleX(-1)

// 垂直翻转
cropperRef.value.scaleY(-1)

// 获取 base64
cropperRef.value.getCropData((data) => {
  console.log(data)
})

// 获取 blob
cropperRef.value.getCropBlob((blob) => {
  console.log(blob)
})

// 获取裁剪后的图片文件
cropperRef.value.getCropFile((file) => {
  console.log(file)
})

基于 MIT 许可发布