基于cropper.js封装vue在线图片裁剪组件
github:demo下载
cropper.js
github:cropper.js
官网(demo)
cropper.js 安装
- npm或bower安装
npm install cropper
# or
bower install cropper
- clone下载:下载地址
git clone https://github.com/fengyuanchen/cropper.git
引用cropper.js
- 主要引用cropper.js跟cropper.css两个文件
<script src="/path/to/jquery.js"></script><!-- jQuery is required -->
<link href="/path/to/cropper.css" rel="stylesheet">
<script src="/path/to/cropper.js"></script>
注意:必须先引入jquery文件,才能使用cropper.js插件
简单使用
- 构建截图所要用到的div容器
<!-- Wrap the image or canvas element with a block element (container) -->
<div>![](picture.jpg)
</div>
- 添加容器的样式,让img填充满整个容器(很重要)
/* Limit image width to avoid overflow the container */
img {max-width: 100%; /* This rule is very important, please do not ignore this! */
}
- 调用cropper.js方法,初始化截图控件
$('#image').cropper({aspectRatio: 16 / 9,crop: function(e) {// Output the result data for cropping image.console.log(e.x);console.log(e.y);console.log(e.width);console.log(e.height);console.log(e.rotate);console.log(e.scaleX);console.log(e.scaleY);}
});
其他详细api请参考:github:cropper.js
封装成vue组件
封装成vue组件中需解决的问题
- cropper.js相关
- 模拟input框点击选择图片并对选择的图片进行格式、大小限制
- 重新选择图片裁剪
- 确认裁剪并获取base64格式的图片信息
- vue相关
- 非父子组件之间的通信问题
模拟input框点击选择图片并对选择的图片进行格式、大小限制
- 构建一个隐藏的input标签,然后模拟点击此input,从而达到能选择图片的功能
<!-- input框 -->
<input id="myCropper-input" type="file" :accept="imgCropperData.accept" ref="inputer" @change="handleFile">
//模拟点击
document.getElementById('myCropper-input').click();
- 给input绑定一个监听内容变化的方法,拿到上传的文件,并进行格式、大小校验
// imgCropperData: {// accept: 'image/gif, image/jpeg, image/png, image/bmp',
// }
handleFile (e) {let _this = this;let inputDOM = this.$refs.inputer;// 通过DOM取文件数据_this.file = inputDOM.files[0];// 判断文件格式if (_this.imgCropperData.accept.indexOf(_this.file.type) == -1) {_this.$Modal.error({title: '格式错误',content: '您选择的图片格式不正确!'});return;}// 判断文件大小限制if (_this.file.size > 5242880) {_this.$Modal.error({title: '超出限制',content: '您选择的图片过大,请选择5MB以内的图片!'});return;}var reader = new FileReader();// 将图片将转成 base64 格式reader.readAsDataURL(_this.file);reader.onload = function () {_this.imgCropperData.imgSrc = this.result;_this.initCropper();}
}
参考:从0开始做一个的Vue图片/ 文件选择(上传)组件[基础向]
重新选择图片裁剪
- 当第一次选择图片之后,肯定会面临需要重选图片的问题,那么就会面临如何替换掉裁剪框中的图片,上面的步骤选择了图片后通过FileRender()方法拿到了图片的主要信息,现在就需要重新构建裁剪框就可以解决问题了,查看cropper.js给出的官方demo,发现官方是使用动态添加裁剪容器的方法,进行操作的,这里我们仿照官方进行实现。
// 初始化剪切initCropper () {let _this = this;// 初始化裁剪区域_this.imgObj = $('![](' + _this.imgCropperData.imgSrc + ')');let $avatarPreview = $('.avatar-preview');$('#myCropper-workspace').empty().html(_this.imgObj);_this.imgObj.cropper({aspectRatio: _this.proportionX / _this.proportionY,preview: $avatarPreview,crop: function(e) {}});}
确认裁剪并获取base64格式的图片信息
let $imgData = _this.imgObj.cropper('getCroppedCanvas')
imgBase64Data = $imgData.toDataURL('image/png');
构造用于上传的数据
// 构造上传图片的数据
let formData = new FormData();
// 截取字符串
let photoType = imgBase64Data.substring(imgBase64Data.indexOf(",") + 1);
//进制转换
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {const byteCharacters = atob(b64Data);const byteArrays = [];for(let offset = 0; offset < byteCharacters.length; offset += sliceSize) {const slice = byteCharacters.slice(offset, offset + sliceSize);const byteNumbers = new Array(slice.length);for(let i = 0; i < slice.length; i++) {byteNumbers[i] = slice.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);byteArrays.push(byteArray);}const blob = new Blob(byteArrays, {type: contentType});return blob;
}
const contentType = 'image/jepg';
const b64Data2 = photoType;
const blob = b64toBlob(b64Data2, contentType);
formData.append("file", blob, "client-camera-photo.png")
formData.append("type", _this.imgType)
非父子组件之间的通信问题
在之前的项目中,常用到父子组件之间的通信传参,一般用两种方法
- 在router里面放置参数,然后通过调用route.params.xxx或者route.params.xxx或者route.query.xxx进行获取
通过props进行通信
这里我们使用eventBus进行组件之间的通信
步骤
1.声明一个bus组件用于B组件把参数传递给A组件
//bus.js
import Vue from 'vue';
export default new Vue();
2.在A组件中引用bus组件,并实时监听其参数变化
// A.vue
import Bus from '../../components/bus/bus.js'export default {components: { Bus },data () {},created: function () {Bus.$on('getTarget', imgToken => { var _this = this;console.log(imgToken);... }); }
}
3.B组件中同样引用bus组件,来把参数传给A组件
// B.vue
// 传参
Bus.$emit('getTarget', imgToken);
参考:
- vue-$on
- vue-$emit
- vue.js之路(4)——vue2.0s中eventBus实现兄弟组件通信
vue选图截图插件完整代码
<template><div class="myCropper-container"><div id="myCropper-workspace"><div class="myCropper-words" v-show="!imgCropperData.imgSrc">请点击按钮选择图片进行裁剪</div></div><div class="myCropper-preview" :class="isShort ? 'myCropper-preview-short' : 'myCropper-preview-long'"><div class="myCropper-preview-1 avatar-preview">![](!imgCropperData.imgUploadSrc ? '/images/thumbnail/thumbnail-img.jpg' : imgCropperData.imgUploadSrc)</div><div class="myCropper-preview-2 avatar-preview">![](!imgCropperData.imgUploadSrc ? '/images/thumbnail/thumbnail-img.jpg' : imgCropperData.imgUploadSrc)</div><div class="myCropper-preview-3 avatar-preview">![](!imgCropperData.imgUploadSrc ? '/images/thumbnail/thumbnail-img.jpg' : imgCropperData.imgUploadSrc)</div><input id="myCropper-input" type="file" :accept="imgCropperData.accept" ref="inputer" @change="handleFile"><Button type="ghost" class="myCropper-btn" @click="btnClick">选择图片</Button><Button type="primary" class="myCropper-btn" :loading="cropperLoading" @click="crop_ok">确认</Button></div></div>
</template><script>var ezjsUtil = Vue.ezjsUtil;import Bus from './bus/bus.js' export default {components: { Bus },props: {imgType: {type: String},proportionX: {type: Number},proportionY: {type: Number}},data () {return {imgCropperData: {accept: 'image/gif, image/jpeg, image/png, image/bmp',maxSize: 5242880,file: null, //上传的文件imgSrc: '', //读取的img文件base64数据流imgUploadSrc: '', //裁剪之后的img文件base64数据流},imgObj: null,hasSelectImg: false,cropperLoading: false,isShort: false,}},created: function () {let _this = this;},mounted: function () {let _this = this;// 初始化预览区域let maxWidthNum = Math.floor(300 / _this.proportionX);let previewWidth = maxWidthNum * _this.proportionX;let previewHeight = maxWidthNum * _this.proportionY;if (previewWidth / previewHeight <= 1.7) {previewWidth = previewWidth / 2;previewHeight = previewHeight / 2;_this.isShort = true;}// 设置最大预览容器的宽高$('.myCropper-preview-1').css('width', previewWidth + 'px');$('.myCropper-preview-1').css('height', previewHeight + 'px');// 设置中等预览容器的宽高$('.myCropper-container .myCropper-preview .myCropper-preview-2').css('width',( previewWidth / 2) + 'px');$('.myCropper-container .myCropper-preview .myCropper-preview-2').css('height', (previewHeight / 2) + 'px');// 设置最小预览容器的宽高$('.myCropper-container .myCropper-preview .myCropper-preview-3').css('width',( previewWidth / 4) + 'px');$('.myCropper-container .myCropper-preview .myCropper-preview-3').css('height', (previewHeight / 4) + 'px');},methods: {// 点击选择图片btnClick () {let _this = this;// 模拟input点击选择文件document.getElementById('myCropper-input').click();},// 选择之后的回调handleFile (e) {let _this = this;let inputDOM = this.$refs.inputer;// 通过DOM取文件数据_this.file = inputDOM.files[0];// 判断文件格式if (_this.imgCropperData.accept.indexOf(_this.file.type) == -1) {_this.$Modal.error({title: '格式错误',content: '您选择的图片格式不正确!'});return;}// 判断文件大小限制if (_this.file.size > 5242880) {_this.$Modal.error({title: '超出限制',content: '您选择的图片过大,请选择5MB以内的图片!'});return;}var reader = new FileReader();// 将图片将转成 base64 格式reader.readAsDataURL(_this.file);reader.onload = function () {_this.imgCropperData.imgSrc = this.result;_this.initCropper();}},// 初始化剪切initCropper () {let _this = this;// 初始化裁剪区域_this.imgObj = $('![](' + _this.imgCropperData.imgSrc + ')');let $avatarPreview = $('.avatar-preview');$('#myCropper-workspace').empty().html(_this.imgObj);_this.imgObj.cropper({aspectRatio: _this.proportionX / _this.proportionY,preview: $avatarPreview,crop: function(e) {}});_this.hasSelectImg = true;},// 确认crop_ok () {let _this = this, imgToken = null, imgBase64Data = null;// 判断是否选择图片if (_this.hasSelectImg == false) {_this.$Modal.error({title: '裁剪失败',content: '请选择图片,然后进行裁剪操作!'});return false;}// 确认按钮不可用_this.cropperLoading = true;let $imgData = _this.imgObj.cropper('getCroppedCanvas')imgBase64Data = $imgData.toDataURL('image/png'); // 构造上传图片的数据let formData = new FormData();// 截取字符串let photoType = imgBase64Data.substring(imgBase64Data.indexOf(",") + 1);//进制转换const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {const byteCharacters = atob(b64Data);const byteArrays = [];for(let offset = 0; offset < byteCharacters.length; offset += sliceSize) {const slice = byteCharacters.slice(offset, offset + sliceSize);const byteNumbers = new Array(slice.length);for(let i = 0; i < slice.length; i++) {byteNumbers[i] = slice.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);byteArrays.push(byteArray);}const blob = new Blob(byteArrays, {type: contentType});return blob;}const contentType = 'image/jepg';const b64Data2 = photoType;const blob = b64toBlob(b64Data2, contentType);formData.append("file", blob, "client-camera-photo.png")formData.append("type", _this.imgType)// ajax上传$.ajax({url: _this.$nfs.uploadUrl,method: 'POST',data: formData,// 默认为true,设为false后直到ajax请求结束(调完回掉函数)后才会执行$.ajax(...)后面的代码async: false,// 下面三个,因为直接使用FormData作为数据,contentType会自动设置,也不需要jquery做进一步的数据处理(序列化)。cache: false,contentType: false,processData: false,type: _this.imgType,success: function(res) {let imgToken = res.data.token;_this.cropperLoading = false;// 传参Bus.$emit('getTarget', imgToken); },error: function(error) {_this.cropperLoading = false;_this.$Modal.error({title: '系统错误',content: '请重新裁剪图片进行上传!'});}});},}}
</script><style lang="less" scoped>.myCropper-container {height: 400px;}.myCropper-container #myCropper-input {width: 0px;height: 0px;}.myCropper-container #myCropper-workspace {width: 500px;height: 400px;border: 1px solid #dddee1;float: left;}// 裁剪图片未选择图片的提示文字.myCropper-container #myCropper-workspace .myCropper-words{text-align: center;font-size: 18px;padding-top: 180px;}// 裁剪图片的预览区域.myCropper-container .myCropper-preview-long {width: 300px;}.myCropper-container .myCropper-preview-short {width: 200px;}.myCropper-container .myCropper-preview {float: left;height: 400px;margin-left: 10px;}.myCropper-container .myCropper-preview .myCropper-preview-1 {border-radius: 5px;overflow: hidden;border: 1px solid #dddee1;box-shadow: 3px 3px 3px #dddee1;img {width: 100%;height: 100%;}}.myCropper-container .myCropper-preview .myCropper-preview-2 {margin-top: 20px;border-radius: 5px;overflow: hidden;border: 1px solid #dddee1;box-shadow: 3px 3px 3px #dddee1;img {width: 100%;height: 100%;}}.myCropper-container .myCropper-preview .myCropper-preview-3 {margin-top: 20px;border-radius: 5px;overflow: hidden;border: 1px solid #dddee1;box-shadow: 3px 3px 3px #dddee1;img {width: 100%;height: 100%;}}// 按钮.myCropper-btn {float: left;margin-top: 20px;margin-right: 10px;}
</style>
BY-LucaLJX
github: lucaljx
基于cropper.js封装vue在线图片裁剪组件相关推荐
- 基于clamp.js封装vue指令,处理多行文本的溢出
最近做项目时,遇到了一个需求:要求div里文本在两行显示,div的宽度是固定的,如果溢出的话就显示省略号.单行文本的溢出问题,我们都很熟悉,只要添加以下css属性就ok: overflow: hidd ...
- 史上最全基于vue的图片裁剪vue-cropper使用
史上最全基于vue的图片裁剪vue-cropper使用 基于vue的图片裁剪vue-cropper 新的需求 vue-cropper官网 代码拷贝 最后 基于vue的图片裁剪vue-cropper 最 ...
- VUE图片裁剪组件,基于VueCropper
VUE图片裁剪组件,基于VueCropper 搬砖的同志麻烦点个赞支持下 效果图 第一步安装vue-cropper插件 npm install vue-cropper 第二步创建组件,把下面的代码复制 ...
- vue图片裁剪组件_使用Vue-Rx的Vue.js图像裁剪组件
vue图片裁剪组件 Vuejs夹 (vuejs-clipper) Vue.js image clipping components using Vue-Rx. 使用Vue-Rx的Vue.js图像裁剪组 ...
- vue图片裁剪组件_Vue.js图像裁剪组件
vue图片裁剪组件 Vuejs夹 (vuejs-clipper) Vue.js image clipping components using Vue-Rx. 使用Vue-Rx的Vue.js图像裁剪组 ...
- 原生html中modal,基于原生JS封装的Modal对话框插件
基于原生JS封装Modal对话框插件 原生JS封装Modal对话框插件,个人用来学习原理与思想,只有简单的基本框架的实现,可在此基础上添加更多配置项 API配置 //基本语法 let modal = ...
- 基于turn.js库电子书在线阅读器源码
在我们进行实际项目开发过程中经常需要在项目中嵌入比较好的文档阅读器,直接在系统中就可以对自己感兴趣的系统文档进行在线阅读,而不必从系统中将文档下载到本地,再打开pdf阅读器或者其他工具软件进行阅读. ...
- 基于node.js和vue的前后端分离影院售票系统电影院影片管理
1,项目介绍 基于 node.js和vue 的影院售票系统拥有两种角色,分别为管理员和用户. 用户:热映.即将上映.电影和影院全局搜索.评论.选座.购票.点赞.收藏.订单等一系列购票功能 管理员:用户 ...
- 【Vue】移动端图片裁剪组件--vue-imgcut
移动端图片裁剪组件–vue-imgcut 安装 npm install vue-imgcut --save GitHub地址 使用说明 template模板中 callback :回调函数 img 是 ...
- HarmonyOS第三方组件——鸿蒙图片裁剪组件ohos-Image-Cropper
前言 基于安卓的平台的图片裁剪组件(https://github.com/ArthurHub/Android-Image-Cropper),实现了鸿蒙平台的迁移和重构,代码已经开源到(https:// ...
最新文章
- 一文读懂QEMU虚拟机
- Windows x64内核学习笔记(一)—— 环境与配置
- 2017年4月19日
- 代码与html混合,自定义的标签与html的标签混合应用_css
- IoT边缘,你究竟是何方神圣?
- node.js 框架基本功能
- HTML5游戏开发(三):使用webpack构建TypeScript应用
- 虚拟机安装VMware ESXi 6.7安装过程介绍
- AB=0与伴随矩阵相互作用型题
- java常用类的特征_Java常用类-包装类
- 安装TeamViewer的监视器驱动后屏幕亮度无法调节
- [转载]关于太阳(卫星)天顶角,太阳高度角,太阳方位角的整理
- MyBatisPlus 又搞事情,发布权限神器!
- Qt Style Sheet实践(二):组合框QComboBox的定制
- app上架因为副标题被App Store残忍拒绝!
- 并发,同步,异步以及事件驱动编程的相关技术
- 苏州优步高级车资费标准和奖励政策
- macOS_Monterey_12.6.1_21G217可引导可虚拟机安装的纯净版苹果OS系统ISO镜像安装包免费下载
- 【面经】Linux零碎系列
- 王树尧老师运筹学课程笔记 06 线性规划与单纯形法(几何意义)