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() {
},
cropImage() {
this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
const base64Data = this.cropImg.split(',')[1]
this.$emit('searchImg', base64Data)
this.dialogVisible = false
},
cropstart() {
},
cropmove() {
},
cropend() {
},
crop(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
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>