cropperjs 裁剪/框选图片

1.效果

在这里插入图片描述

2.使用组件

<!-- 父级 -->
 <Cropper ref="cropperRef" :imgUrl="url" @searchImg="searchImg"></Cropper>

3.封装组件

<template>
  <el-dialog :title="title" :visible.sync="dialogVisible" width="1000px">
    <input ref="input" type="file" name="image" @change="setImage" />
    <div class="flex justify-around">
      <div class="w-480px h-270px flex justify-center items-center">
        <div
          v-show="!imgSrc"
          @click="showFileChooser"
          class="w-full h-full flex cursor-pointer justify-center items-center border-1px border-dashed border-gray-300 rounded-lg"
        >
          <i class="font-size-20px el-icon-plus avatar-uploader-icon"></i>
        </div>
        <!-- :aspect-ratio="16 / 16" -->
        <vue-cropper
          v-show="imgSrc"
          class="w-full h-full"
          ref="cropper"
          :src="imgSrc"
          alt="Source Image"
          @ready="ready"
          @cropstart="cropstart"
          @cropmove="cropmove"
          @cropend="cropend"
          @crop="crop"
          @zoom="zoom"
          preview=".preview"
          :autoCropArea="autoCropArea"
        >
        </vue-cropper>
      </div>
      <div class="w-420px">
        <div class="font-bold color-#666 ml-20px mb-10px">预览</div>
        <div v-show="!imgSrc" class="preview_empty ml-20px"></div>
        <div v-show="imgSrc" class="preview ml-20px"></div>
        <!-- <div>裁剪图片</div>
        <div class="cropped-image">
          <el-image class="h-180px" v-if="cropImg" :src="cropImg" alt="Cropped Image" />
          <div v-else class="crop-placeholder" />
        </div> -->
        <div class="actions mt-10px ml-10px">
          <el-button class="mb-10px ml-10px" type="primary" @click="zoom(0.2)" size="small">放大</el-button>
          <el-button class="mb-10px" type="primary" @click="zoom(-0.2)" size="small">缩小</el-button>
          <el-button class="mb-10px" type="primary" @click="move(-10, 0)" size="small">左移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(10, 0)" size="small">右移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(0, -10)" size="small">上移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(0, 10)" size="small">下移</el-button>
          <el-button class="mb-10px" type="primary" @click="rotate(90)" size="small">旋转90°</el-button>
          <el-button class="mb-10px" type="primary" @click="rotate(-90)" size="small">旋转-90°</el-button>
          <!-- <el-button class="mb-10px" type="primary" @click="flipX" size="small">水平翻转</el-button>
          <el-button class="mb-10px" type="primary" @click="flipY" size="small">垂直翻转</el-button> -->
          <!-- <el-button class="mb-10px" type="success" @click="cropImage" size="small">搜索</el-button> -->
          <el-button class="mb-10px" type="primary" @click="reset" size="small" plain>重置</el-button>
          <el-button
            v-if="!isHideFileChooser"
            class="mb-10px"
            type="success"
            @click="showFileChooser"
            size="small"
            plain
            >更换图片</el-button
          >

          <!-- <el-button class="mb-10px" type="primary" @click="getCropBoxData" size="small">获取裁剪框数据</el-button>
          <el-button class="mb-10px" type="primary" @click="setCropBoxData" size="small">设置裁剪框数据</el-button>
          <el-button class="mb-10px" type="primary" @click="getData" size="small">获取裁剪数据</el-button>
          <el-button class="mb-10px" type="primary" @click="setData" size="small">设置裁剪数据</el-button> -->
        </div>
      </div>
    </div>

    <span slot="footer" class="dialog-footer">
      <el-button size="small" @click="dialogVisible = false">取 消</el-button>
      <el-button size="small" type="primary" @click="cropImage">搜索</el-button>
    </span>
  </el-dialog>
</template>

<script>
import VueCropper from 'vue-cropperjs'
import 'cropperjs/dist/cropper.css'

export default {
  name: 'Cropper',
  components: { VueCropper },
  props: {
    title: {
      type: String,
      default: '图片框选'
    },
    imgUrl: {
      type: String,
      default: ''
    },
    autoCropArea: {
      type: Number,
      default: 0.6
    },
    isHideFileChooser: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      imgSrc: '',
      dialogVisible: false,
      cropImg: ''
    }
  },
  watch: {
    imgUrl(val) {
      if (val) {
        this.imgSrc = val
        console.log('🚀 ~ imgUrl ~ this.imgSrc:', this.imgSrc)
      }
    }
  },
  methods: {
    open() {
      if (!this.imgUrl) {
        this.imgSrc = ''
      }
      this.dialogVisible = true
    },
    handleClose() {
      this.$emit('close')
    },
    ready() {
      // console.log('🚀 ~ ready ~ this.$refs.cropper:', this.$refs.cropper)
    },
    cropImage() {
      // get image data for post processing, e.g. upload or setting image src
      this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
      const base64Data = this.cropImg.split(',')[1]
      this.$emit('searchImg', base64Data)
      this.dialogVisible = false
    },
    cropstart() {
      // console.log('🚀 ~ cropstart ~')
    },
    cropmove() {
      // console.log('🚀 ~ cropmove ~')
    },
    cropend() {
      // console.log('🚀 ~ cropend ~')
    },
    crop(data) {
      // console.log('🚀 ~ crop ~ data:', data)
    },
    flipX() {
      const dom = this.$refs.flipX
      let scale = dom.getAttribute('data-scale')
      scale = scale ? -scale : -1
      this.$refs.cropper.scaleX(scale)
      dom.setAttribute('data-scale', scale)
    },
    flipY() {
      const dom = this.$refs.flipY
      let scale = dom.getAttribute('data-scale')
      scale = scale ? -scale : -1
      this.$refs.cropper.scaleY(scale)
      dom.setAttribute('data-scale', scale)
    },
    getCropBoxData() {
      this.data = JSON.stringify(this.$refs.cropper.getCropBoxData(), null, 4)
    },
    getData() {
      this.data = JSON.stringify(this.$refs.cropper.getData(), null, 4)
      console.log('🚀 ~ getData ~ this.data:', this.data)
    },
    move(offsetX, offsetY) {
      this.$refs.cropper.move(offsetX, offsetY)
    },
    reset() {
      this.$refs.cropper.reset()
    },
    rotate(deg) {
      this.$refs.cropper.rotate(deg)
    },
    setCropBoxData() {
      if (!this.data) return
      this.$refs.cropper.setCropBoxData(JSON.parse(this.data))
    },
    setData() {
      if (!this.data) return
      this.$refs.cropper.setData(JSON.parse(this.data))
    },
    setImage(e) {
      const file = e.target.files[0]
      if (file.type.indexOf('image/') === -1) {
        alert('Please select an image file')
        return
      }
      if (typeof FileReader === 'function') {
        const reader = new FileReader()
        reader.onload = (event) => {
          this.imgSrc = event.target.result
          // rebuild cropperjs with the updated source
          this.$refs.cropper.replace(event.target.result)
        }
        reader.readAsDataURL(file)
      } else {
        alert('Sorry, FileReader API not supported')
      }
    },
    showFileChooser() {
      this.$refs.input.click()
    },
    zoom(percent) {
      this.$refs.cropper.relativeZoom(percent)
    }
  },
  mounted() {
    this.imgSrc = this.imgUrl
  }
}
</script>

<style lang="scss" scoped>
input[type='file'] {
  display: none;
}

.preview-area {
  width: 100%;
}

.preview-area p {
  font-size: 1.25rem;
  margin: 0;
  margin-bottom: 1rem;
}

.preview-area p:last-of-type {
  margin-top: 1rem;
}

.preview {
  width: 270px;
  height: calc(270px * (9 / 16));
  overflow: hidden;
  background-color: #f5f5f5;
}

.preview_empty {
  width: 270px;
  height: calc(270px * (9 / 16));
  overflow: hidden;
  background-color: #f5f5f5;
}
.crop-placeholder {
  width: 100%;
  height: 200px;
  background: #ccc;
}

.cropped-image img {
  max-width: 100%;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值