使用element-ui + cropper.js自己封装一个图片裁剪组件__Vue.js
发布于 3 年前 作者 banyungong 1503 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

本文主要使用了element-ui的图片上传组件获取图片,然后使用cropper.js进行图片裁剪,在裁剪完以后进行格式转换,然后自行图片上传,文中的css使用了stylus编译,如果不是请自行更改

裁剪效果

裁剪效果

1.插件安装

npm install cropperjs --save

element-ui的安装就不予以赘述了,主要使用了element-ui的图片上传以及$message

2.组件编写

  • 目录新建

components文件夹下新建upload文件夹,然后在upload文件夹下新建 cropperBox.vue 以及 upCover.vue

  • 编写cropperBox.vue

cropperBox.vue中写入以下内容,(finishCropImage方法中的blob的数据格式请自行选择以下两种,第一种是blob,第二种是普通文件方式。)

``<template>

<el-progress :text-inside="true" :stroke-width="30" :percentage="uploadProgress"> </el-progress> <button type="button" class="cropper-btn" @click="finishCropImage" :disabled="btnTips.disable" :class="{'btn-bg': uploading}"> {{ btnTips.value }} </button>
</template> <script> import Cropper from 'cropperjs' export default { name: 'cropper-box', props: { options: { default: { aspectRatio: 1 / 1, preview: '#cropperRes', zoomOnWheel: false, minCropBoxWidth: 50, viewMode:2, } }, uploadProgress: { default: 0 } }, data () { return { cropper: null, imgHasLoad: false, cropperHasInit: false, uploading: false, rawFile: null } }, watch: { imgHasLoad (val) { if (!val) { this.uploading = false } } }, computed: { btnTips () { if (this.uploading) { return { value: '正在上传,请稍等', disable: true } } return { value: '裁剪完成,立即上传', disable: false } } }, methods: { show () { this.imgHasLoad = true }, close () { this.imgHasLoad = false }, loadCropper (rawFile) { this.rawFile = rawFile const URL = window.URL || window.webkitURL const blobURL = URL.createObjectURL(rawFile) var image = this.$refs.uploadPreview if (!this.cropper) this.cropper = new Cropper(image, this.options) this.cropper.reset().replace(blobURL) }, // 完成裁剪,将文件进行格式转换,发送给父组件,请自行选择普通文件格式还是blob格式,格式转换方法已封装,请看下面的两个方法 finishCropImage () { this.uploading = true const croppedCanvas = this.cropper.getCroppedCanvas() const croppedDataUrl = croppedCanvas.toDataURL(this.rawFile.type) const blob = this.base64toFile(croppedDataUrl) this.$emit('finishCropImage', blob) }, // dataUrl 转 blob dataURLtoBlob (dataurl) { /* eslint-disable */ var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1]; var bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type:mime}); /* eslint-enable */ }, //base64转为普通文件格式 base64toFile (dataurl, filename = 'file') { let arr = dataurl.split(',') let mime = arr[0].match(/:(.*?);/)[1] let suffix = mime.split('/')[1] let bstr = atob(arr[1]) let n = bstr.length let u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], `${filename}.${suffix}`, { type: mime }) }, } } </script> <style lang="stylus"> @import "cropperjs/dist/cropper.min.css" .cropper-wrap .cropper-alert-mask position: fixed top: 0 left: 0 right: 0 bottom: 0 z-index: 2000 background-color: rgba(#000000, .5) visibility: hidden height: 0 transition: all .3s ease .cropper-alert-mask.show visibility: visible height: 100% .cropper-alert opacity: 0 transition: all .3s ease visibility: hidden padding: 10px position: fixed z-index: 2000 top: 50px left: 50% transform: translateX(-50%) scale(2) background-color: #ffffff border-radius: 5px overflow: hidden width: 100% height: 100% max-width: 600px max-height: 530px &.show opacity: 1 visibility: visible transform: translateX(-50%) scale(1) .cropper position: relative max-width: 600px max-height: 460px height: 100% padding: 10px padding-right: 120px @media (max-width: 1324px) padding-top: 120px padding-right: 10px background-color: #f9fbfc .layout-icon-wrap position: absolute cursor: pointer right: 0px top: 0px font-size: 20px .cropper-box position: relative width: 100% height: 100% background-color: #ffffff .cropper-res-wrap position: absolute top: 50% transform: translateY(-50%) @media (max-width: 1324px) top: 0 left: 50% transform: translateX(-50%) right: 0 width: 100px height: auto padding: 10px background-color: #a8a8a8 box-sizing: content-box .cropper-res width: 100px height: 100px overflow: hidden background-color: #ffffff .cropper-btns-wrap position: relative margin-top: 20px .cropper-btn position: absolute left: 0 top: 0 width: 100% height: 30px line-height: 1 background: #ffffff border: 1px solid #e1e1e1 border-radius: 15px color: #666666 cursor: pointer .btn-bg background: #FF000000 </style>``
  • 编写upCover.vue

upCover.vue中写入以下内容,(finishCropImage方法中请自行补充图片上传调用接口的方法)

`<template>

<el-upload ref="upload" class="cover-uploader" :show-file-list="false" :before-upload="beforeVipImageUpload" :auto-upload="false" :on-change="onFileChange" :on-progress="onUploadProgress">
{{ tip }}
</el-upload> <cropperBox ref="cropperBox" :options="options" :uploadProgress="uploadProgress" [@finishCropImage](/user/finishCropImage)="finishCropImage"> </cropperBox>
</template> <script> import uploadImage from "../../mixins/uploadImage"; import cropperBox from './cropperBox' import {upload} from "../../api/base"; export default { name: 'up-cover', mixins:[uploadImage], components: { cropperBox }, props: { defaultImg: String, ratio: { // 裁剪结果宽高比 default: 1 }, width: [Number,String], height: [Number,String], WHRatio: { // 组件宽高比 default: 1 }, cropBoxResizable:Boolean, //是否可以修改裁剪框的尺寸 maxWidth: String, maxHeight: String, tip: { default: '上传图片' }, maxSize: { // 最大选择图片的大小,单位M default: 3 } }, data () { return { cropper: null, newFile: null, options: { aspectRatio: 2.166, preview: '#cropperRes', zoomOnWheel: false, cropBoxResizable:false, minCropBoxWidth: 50, viewMode:2 }, token: {}, uploadProgress: 0, calcHeight: 0 } }, created() { this.options.aspectRatio = this.ratio; this.options.cropBoxResizable = this.cropBoxResizable; if (this.height) { this.calcHeight = this.height } }, computed: { imageUrl () { return this.defaultImg } }, mounted() { if (!this.calcHeight) { if (this.width) { this.calcHeight = this.width / this.WHRatio } else { this.calcHeight = this.$refs.coverOutWrap.offsetWidth / this.WHRatio } } }, methods: { //截取图片上传的事件,有图片的情况就打开裁剪框,并且将文件传入到裁剪框中 onFileChange (file, fileList) { if (file.status === 'ready') { this.$refs.cropperBox.show(); this.$refs.cropperBox.loadCropper(file.raw) } }, //文件裁剪,nerFile就是最终拿到的文件,自行调接口进行图片上传 finishCropImage (newFile) { this.newFile = newFile; //图片上传调接口请自行补充 }, //上传图片之前的大小检测,文件大小通过prop传递,可在组件使用自定义文件大小限制 beforeVipImageUpload(file){ const isLt3M = file.size / 1024 / 1024 < this.maxSize; if (!isLt3M) { this.$message.error('上传图片大小不可大于' + this.maxSize + 'M'); } return isLt3M; let uploadFile = new window.File([this.newFile], file.name, { type: this.newFile.type }); uploadFile.uid = this.newFile.uid; return Promise.resolve(uploadFile) }, //文件上传进度 onUploadProgress (event, file, fileList) { this.uploadProgress = parseInt(event.percent) - 1 } } } </script> <style lang="stylus"> .cover-upload-wrap position: relative width: 100% max-width: 300px height: 150px border-radius: 5px .cover-uploader width: 100% height: 100% .el-upload width: 100% height: 100% overflow: hidden border-radius: 5px cursor: pointer border: 1px solid #dddddd .img-wrap position: relative width: 100% height: 100% &:hover .img-mask-default opacity: 1 background-color: rgba(0, 0, 0, 0.5) color: #ffffff .cover position: relative width: 100% height: 100% border-radius: 5px .img-mask-default position: absolute left: 0 top: 0 width: 100% height: 100% padding-left: 10px padding-right: 10px background-color: #ffffff color: #555555 display: flex flex-direction: column justify-content: center font-size: 12px transition: all .2s linear .el-icon-upload font-size: 18px .img-mask opacity: 0 </style>`

使用方法

  • 引入插件

import UP from '@/components/upload/upCover' export default { components:{UP}, }

  • 在页面中使用

pictureUrl就是图片上传成功以后显示的回调地址,请自行填写,maxSize可以限定上传图片时候的尺寸大小,width和height限定组件大小,

<UP class="upload-cover" :default-img="pictureUrl?pictureUrl:''" ratio="2.166" :cropBoxResizable="true" width="156" height="72" tip="上传等级图标" maxSize="3" [@uploadSuccess](/user/uploadSuccess)="uploadSuccess"> </UP>

uploadSuccess方法中可以拿到图片上传成功以后的回调,具体操作自行补充啦

uploadSuccess(res){ //this.pictureUrl=res.data },

更多操作

javascript操作

this.myCropper.getCroppedCanvas().toDataURL('image/jpeg') //拿到裁剪后的base64的图片 this.myCropper.getCropBoxData(); //获取裁剪框数据 this.myCropper.setCropBoxData(); //设置裁剪框数据 this.myCropper.getCanvasData(); //获取图片数据 this.myCropper.setCanvasData(); //设置图片数据

配置对象

  • viewMode 视图控制

    • 0 无限制
    • 1 限制裁剪框不能超出图片的范围
    • 2 限制裁剪框不能超出图片的范围 且图片填充模式为 cover 最长边填充
    • 3 限制裁剪框不能超出图片的范围 且图片填充模式为 contain 最短边填充
  • dragMode 拖拽图片模式

    • crop 形成新的裁剪框
    • move 图片可移动
    • none 什么也没有
  • initialAspectRatio 裁剪框宽高比的初始值 默认与图片宽高比相同 只有在aspectRatio没有设置的情况下可用

  • aspectRatio 设置裁剪框为固定的宽高比

  • data 之前存储的裁剪后的数据 在初始化时会自动设置 只有在autoCrop设置为true时可用

  • preview 预览 设置一个区域容器预览裁剪后的结果

    • Element, Array (elements), NodeList or String (selector)
  • responsive 在窗口尺寸调整后 进行响应式的重渲染 默认true

  • restore 在窗口尺寸调整后 恢复被裁剪的区域 默认true

  • checkCrossOrigin 检查图片是否跨域 默认true 如果是 会在被复制的图片元素上加上属性crossOrigin 并且在src上加上一个时间戳 避免重加载图片时因为浏览器缓存而加载错误

  • checkOrientation 检查图片的方向信息(仅JPEG图片有)默认true 在旋转图片时会对图片方向值做一些处理 以解决IOS设备上的一些问题

  • modal 是否显示图片和裁剪框之间的黑色蒙版 默认true

  • guides 是否显示裁剪框的虚线 默认true

  • center 是否显示裁剪框中间的 ‘+’ 指示器 默认true

  • highlight 是否显示裁剪框上面的白色蒙版 (很淡)默认true

  • background 是否在容器内显示网格状的背景 默认true

  • autoCrop 允许初始化时自动的裁剪图片 配合 data 使用 默认true

  • autoCropArea 设置裁剪区域占图片的大小 值为 0-1 默认 0.8 表示 80%的区域

  • movable 是否可以移动图片 默认true

  • rotatable 是否可以旋转图片 默认true

  • scalable 是否可以缩放图片(可以改变长宽) 默认true

  • zoomable 是否可以缩放图片(改变焦距) 默认true

  • zoomOnTouch 是否可以通过拖拽触摸缩放图片 默认true

  • zoomOnWheel 是否可以通过鼠标滚轮缩放图片 默认true

  • wheelZoomRatio 设置鼠标滚轮缩放的灵敏度 默认 0.1

  • cropBoxMovable 是否可以拖拽裁剪框 默认true

  • cropBoxResizable 是否可以改变裁剪框的尺寸 默认true

  • toggleDragModeOnDblclick 是否可以通过双击切换拖拽图片模式(move和crop)默认true 当拖拽图片模式为none时不可切换 该设置必须浏览器支持双击事件

  • minContainerWidth(200)、minContainerHeight(100)、minCanvasWidth(0)、minCanvasHeight(0)、minCropBoxWidth(0)、minCropBoxHeight(0) 容器、图片、裁剪框的 最小宽高 括号内为默认值 注意 裁剪框的最小高宽是相对与页面而言的 并非相对图片

方法

  • crop() 手动显示裁剪框

  • reset() 重置图片和裁剪框为初始状态

  • replace(url[, hasSameSize]) 替换图片路径并且重建裁剪框

    • url 新路径
    • hasSameSize 默认值false 设置为true表示新老图片尺寸一样 只需要更换路径无需重建裁剪框
  • enable() 解冻 裁剪框

  • disable() 冻结 裁剪框

  • destroy() 摧毁裁剪框并且移除cropper实例

  • move(offsetX[, offsetY]) 移动图片指定距离 一个参数代表横纵向移动距离一样

  • moveTo(x[, y]) 移动图片到一个指定的点 一个参数代表横纵向移动距离一样

  • zoom(ratio) 缩放 ratio大于零是放大 小于零缩小

  • zoomTo(ratio[, pivot]) 缩放并设置中心点的位置

  • rotate(degree) 旋转 类似css

  • rotateTo(degree) 旋转到绝对角度

  • scale(scaleX[, scaleY])、scaleX(scaleX)、scaleY(scaleY) 缩放 一个参数代表横纵向缩放值一样

  • getData([rounded]) 返回裁剪区域基于原图片!原尺寸!的位置和尺寸 rounded默认为false 表示是否显示四舍五入后的数据 有了这些数据可以直接在原图上进行裁剪显示

  • setData(data) 改变裁剪区域基于原图的位置和尺寸 仅当viewMode 不为0时有效

  • getContainerData()、getImageData()、getCanvasData()、setCanvasData(data)、getCropBoxData()、setCropBoxData(data) 容器、图片容器(画布)、图片、裁剪区域相对容器的数据设置和获取

  • getCroppedCanvas([options]) 得到被裁剪图片的一个canvas对象 options设置这个canvas的一些数据

    • width、height、minWidth、minHeight、maxWidth、maxHeight、fillColor、imageSmoothingEnabled (图片是否是光滑的 默认true)、imageSmoothingQuality(图片的质量 默认low 还有medium、high)
  • setAspectRatio(aspectRatio) 改变裁剪区域的宽高比

  • setDragMode([mode]) 设置拖拽图片模式

事件

  • ready 渲染前(图片已经被加载、cropper实例已经准备完毕)的准备工作事件

  • cropstart、cropmove、cropend、crop 开始画裁剪框(或画布)、画裁剪框(或画布)的中途、裁剪框(或画布)画完、进行裁剪事件 event.detail.originalEvent、event.detail.action

    • 当autoCrop为true crop事件会在ready之前触发
  • zoom 裁剪框缩放事件

本文参考于https://www.cnblogs.com/eightFlying/p/cropper-demo.html

本文使用 mdnice 排版

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 辉夜真是太可爱啦 原文链接:https://juejin.im/post/6844904205678116877

回到顶部