基于cropper.js封裝vue實(shí)現(xiàn)在線圖片裁剪組件功能
效果圖如下所示,
cropper.js
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兩個(gè)文件
<script src="/path/to/jquery.js"></script><!-- jQuery is required --> <link href="/path/to/cropper.css" rel="external nofollow" rel="stylesheet"> <script src="/path/to/cropper.js"></script>
注意:必須先引入jquery文件,才能使用cropper.js插件
簡(jiǎn)單使用
構(gòu)建截圖所要用到的div容器
<!-- Wrap the image or canvas element with a block element (container) --> <div>  </div>
添加容器的樣式,讓img填充滿整個(gè)容器(很重要)
/* Limit image width to avoid overflow the container */ img { max-width: 100%; /* This rule is very important, please do not ignore this! */ }
調(diào)用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); } });
其他詳細(xì)api請(qǐng)參考:github:cropper.js
封裝成vue組件
封裝成vue組件中需解決的問題
- cropper.js相關(guān)
模擬input框點(diǎn)擊選擇圖片并對(duì)選擇的圖片進(jìn)行格式、大小限制
重新選擇圖片裁剪
確認(rèn)裁剪并獲取base64格式的圖片信息
- vue相關(guān)
非父子組件之間的通信問題
模擬input框點(diǎn)擊選擇圖片并對(duì)選擇的圖片進(jìn)行格式、大小限制
構(gòu)建一個(gè)隱藏的input標(biāo)簽,然后模擬點(diǎn)擊此input,從而達(dá)到能選擇圖片的功能
<!-- input框 --> <input id="myCropper-input" type="file" :accept="imgCropperData.accept" ref="inputer" @change="handleFile"> //模擬點(diǎn)擊 document.getElementById('myCropper-input').click();
給input綁定一個(gè)監(jiān)聽內(nèi)容變化的方法,拿到上傳的文件,并進(jìn)行格式、大小校驗(yàn)
// imgCropperData: { // accept: 'image/gif, image/jpeg, image/png, image/bmp', // } handleFile (e) { let _this = this; let inputDOM = this.$refs.inputer; // 通過DOM取文件數(shù)據(jù) _this.file = inputDOM.files[0]; // 判斷文件格式 if (_this.imgCropperData.accept.indexOf(_this.file.type) == -1) { _this.$Modal.error({ title: '格式錯(cuò)誤', content: '您選擇的圖片格式不正確!' }); return; } // 判斷文件大小限制 if (_this.file.size > 5242880) { _this.$Modal.error({ title: '超出限制', content: '您選擇的圖片過大,請(qǐng)選擇5MB以內(nèi)的圖片!' }); return; } var reader = new FileReader(); // 將圖片將轉(zhuǎn)成 base64 格式 reader.readAsDataURL(_this.file); reader.onload = function () { _this.imgCropperData.imgSrc = this.result; _this.initCropper(); } }
重新選擇圖片裁剪
當(dāng)?shù)谝淮芜x擇圖片之后,肯定會(huì)面臨需要重選圖片的問題,那么就會(huì)面臨如何替換掉裁剪框中的圖片,上面的步驟選擇了圖片后通過FileRender()方法拿到了圖片的主要信息,現(xiàn)在就需要重新構(gòu)建裁剪框就可以解決問題了,查看cropper.js給出的官方demo,發(fā)現(xiàn)官方是使用動(dòng)態(tài)添加裁剪容器的方法,進(jìn)行操作的,這里我們仿照官方進(jìn)行實(shí)現(xiàn)。
// 初始化剪切 initCropper () { let _this = this; // 初始化裁剪區(qū)域 _this.imgObj = $(''); let $avatarPreview = $('.avatar-preview'); $('#myCropper-workspace').empty().html(_this.imgObj); _this.imgObj.cropper({ aspectRatio: _this.proportionX / _this.proportionY, preview: $avatarPreview, crop: function(e) { } }); }
確認(rèn)裁剪并獲取base64格式的圖片信息
let $imgData = _this.imgObj.cropper('getCroppedCanvas') imgBase64Data = $imgData.toDataURL('image/png');
構(gòu)造用于上傳的數(shù)據(jù)
// 構(gòu)造上傳圖片的數(shù)據(jù) let formData = new FormData(); // 截取字符串 let photoType = imgBase64Data.substring(imgBase64Data.indexOf(",") + 1); //進(jìn)制轉(zhuǎn)換 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)
非父子組件之間的通信問題
在之前的項(xiàng)目中,常用到父子組件之間的通信傳參,一般用兩種方法
在router里面放置參數(shù),然后通過調(diào)用route.params.xxx或者route.query.xxx進(jìn)行獲取
通過props進(jìn)行通信
這里我們使用eventBus進(jìn)行組件之間的通信
步驟
1.聲明一個(gè)bus組件用于B組件把參數(shù)傳遞給A組件
//bus.js import Vue from 'vue'; export default new Vue();
2.在A組件中引用bus組件,并實(shí)時(shí)監(jiān)聽其參數(shù)變化
// 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組件,來把參數(shù)傳給A組件
// B.vue // 傳參 Bus.$emit('getTarget', imgToken);
參考:
vue-$on
vue-$emit
vue.js之路(4)——vue2.0s中eventBus實(shí)現(xiàn)兄弟組件通信
vue選圖截圖插件完整代碼
<template> <div class="myCropper-container"> <div id="myCropper-workspace"> <div class="myCropper-words" v-show="!imgCropperData.imgSrc">請(qǐng)點(diǎn)擊按鈕選擇圖片進(jìn)行裁剪</div> </div> <div class="myCropper-preview" :class="isShort ? 'myCropper-preview-short' : 'myCropper-preview-long'"> <div class="myCropper-preview-1 avatar-preview">  </div> <div class="myCropper-preview-2 avatar-preview">  </div> <div class="myCropper-preview-3 avatar-preview">  </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">確認(rèn)</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數(shù)據(jù)流 imgUploadSrc: '', //裁剪之后的img文件base64數(shù)據(jù)流 }, imgObj: null, hasSelectImg: false, cropperLoading: false, isShort: false, } }, created: function () { let _this = this; }, mounted: function () { let _this = this; // 初始化預(yù)覽區(qū)域 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; } // 設(shè)置最大預(yù)覽容器的寬高 $('.myCropper-preview-1').css('width', previewWidth + 'px'); $('.myCropper-preview-1').css('height', previewHeight + 'px'); // 設(shè)置中等預(yù)覽容器的寬高 $('.myCropper-container .myCropper-preview .myCropper-preview-2').css('width',( previewWidth / 2) + 'px'); $('.myCropper-container .myCropper-preview .myCropper-preview-2').css('height', (previewHeight / 2) + 'px'); // 設(shè)置最小預(yù)覽容器的寬高 $('.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: { // 點(diǎn)擊選擇圖片 btnClick () { let _this = this; // 模擬input點(diǎn)擊選擇文件 document.getElementById('myCropper-input').click(); }, // 選擇之后的回調(diào) handleFile (e) { let _this = this; let inputDOM = this.$refs.inputer; // 通過DOM取文件數(shù)據(jù) _this.file = inputDOM.files[0]; // 判斷文件格式 if (_this.imgCropperData.accept.indexOf(_this.file.type) == -1) { _this.$Modal.error({ title: '格式錯(cuò)誤', content: '您選擇的圖片格式不正確!' }); return; } // 判斷文件大小限制 if (_this.file.size > 5242880) { _this.$Modal.error({ title: '超出限制', content: '您選擇的圖片過大,請(qǐng)選擇5MB以內(nèi)的圖片!' }); return; } var reader = new FileReader(); // 將圖片將轉(zhuǎn)成 base64 格式 reader.readAsDataURL(_this.file); reader.onload = function () { _this.imgCropperData.imgSrc = this.result; _this.initCropper(); } }, // 初始化剪切 initCropper () { let _this = this; // 初始化裁剪區(qū)域 _this.imgObj = $(''); 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; }, // 確認(rèn) crop_ok () { let _this = this, imgToken = null, imgBase64Data = null; // 判斷是否選擇圖片 if (_this.hasSelectImg == false) { _this.$Modal.error({ title: '裁剪失敗', content: '請(qǐng)選擇圖片,然后進(jìn)行裁剪操作!' }); return false; } // 確認(rèn)按鈕不可用 _this.cropperLoading = true; let $imgData = _this.imgObj.cropper('getCroppedCanvas') imgBase64Data = $imgData.toDataURL('image/png'); // 構(gòu)造上傳圖片的數(shù)據(jù) let formData = new FormData(); // 截取字符串 let photoType = imgBase64Data.substring(imgBase64Data.indexOf(",") + 1); //進(jìn)制轉(zhuǎn)換 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, // 默認(rèn)為true,設(shè)為false后直到ajax請(qǐng)求結(jié)束(調(diào)完回掉函數(shù))后才會(huì)執(zhí)行$.ajax(...)后面的代碼 async: false, // 下面三個(gè),因?yàn)橹苯邮褂肍ormData作為數(shù)據(jù),contentType會(huì)自動(dòng)設(shè)置,也不需要jquery做進(jìn)一步的數(shù)據(jù)處理(序列化)。 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: '系統(tǒng)錯(cuò)誤', content: '請(qǐng)重新裁剪圖片進(jìn)行上傳!' }); } }); }, } } </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; } // 裁剪圖片的預(yù)覽區(qū)域 .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
總結(jié)
以上所述是小編給大家介紹的基于cropper.js封裝vue實(shí)現(xiàn)在線圖片裁剪組件功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- 前端vue-cropperjs實(shí)現(xiàn)圖片裁剪方案
- Vue使用Vue-cropper實(shí)現(xiàn)圖片裁剪
- vue圖片裁剪插件vue-cropper使用方法詳解
- Vue-cropper 圖片裁剪的基本原理及思路講解
- vue-cli結(jié)合Element-ui基于cropper.js封裝vue實(shí)現(xiàn)圖片裁剪組件功能
- cropper js基于vue的圖片裁剪上傳功能的實(shí)現(xiàn)代碼
- vue移動(dòng)端裁剪圖片結(jié)合插件Cropper的使用實(shí)例代碼
- 在Vue3項(xiàng)目中使用VueCropper裁剪組件實(shí)現(xiàn)裁剪及預(yù)覽效果
相關(guān)文章
vue?ElementUI級(jí)聯(lián)選擇器回顯問題解決
這篇文章主要介紹了vue?ElementUI級(jí)聯(lián)選擇器回顯問題解決,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09vueScroll實(shí)現(xiàn)移動(dòng)端下拉刷新、上拉加載
這篇文章主要介紹了vueScroll實(shí)現(xiàn)移動(dòng)端下拉刷新、上拉加載,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03vue3?reactive響應(yīng)式依賴收集派發(fā)更新原理解析
這篇文章主要為大家介紹了vue3響應(yīng)式reactive依賴收集派發(fā)更新原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03VUEX 數(shù)據(jù)持久化,刷新后重新獲取的例子
今天小編就為大家分享一篇VUEX 數(shù)據(jù)持久化,刷新后重新獲取的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11使用elementUI表單校驗(yàn)函數(shù)validate需要注意的坑及解決
這篇文章主要介紹了使用elementUI表單校驗(yàn)函數(shù)validate需要注意的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06vue+antDesign實(shí)現(xiàn)樹形數(shù)據(jù)展示及勾選聯(lián)動(dòng)
本文主要介紹了vue+antDesign實(shí)現(xiàn)樹形數(shù)據(jù)展示及勾選聯(lián)動(dòng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02