在Vue3項(xiàng)目中使用VueCropper裁剪組件實(shí)現(xiàn)裁剪及預(yù)覽效果
前言
某次看到后臺(tái)系統(tǒng)中使用到了裁剪組件,感覺(jué)挺好玩的并且最近也在學(xué)Vue3和Ts,所以就研究了VueCropper組件,封裝了裁剪組件,效果如下圖。
一、使用步驟
1.安裝庫(kù)
npm i vue-cropper --save
2.引入庫(kù)
代碼如下(示例):
import ‘vue-cropper/dist/index.css' import { VueCropper } from ‘vue-cropper'
3.在component文件夾中新建一個(gè)裁剪Vue文件
TipsDialog是我自己封裝的dialog組件,可以替換成el-dialog
<template> <div> <input ref="reuploadInput" type="file" accept="image/*" @change="onChange" id="fileBtn" style="display: none" > <TipsDialog :visible="dialogVisible" :title="'圖片裁剪'" :width="'40%'" :custom-class="'upload_dialog'" @close="dialogVisible = false" > // 核心內(nèi)容 <template #default> <div class="cropper"> // 裁剪左側(cè)內(nèi)容 <div class="cropper_left"> <vueCropper :tyle="{ width: '400px'}" ref="cropperRef" :img="options.img" :info="true" :info-true="options.infoTrue" :auto-crop="options.autoCrop" :fixed-box="options.fixedBox" :can-move="options.canMoveBox" :can-scale="options.canScale" :fixed-number="fixedNumber" :fixed="options.fixed" :full="options.full" :center-box="options.centerBox" @real-time="previewHandle" /> <div class="reupload_box"> <div class="reupload_text" @click="uploadFile('reload')" > 重新上傳 </div> <div> <el-icon class="rotate_right" @click="changeScale(1)" > <CirclePlus /> </el-icon> <el-icon class="rotate_right" @click="changeScale(-1)" > <Remove /> </el-icon> <el-icon class="rotate_right" @click="rotateRight" > <RefreshRight /> </el-icon> </div> </div> </div> <div class="cropper_right"> <div class="preview_text"> 預(yù)覽 </div> <div :style="getStyle" class="previewImg" > <div :style="previewFileStyle"> <img :style="previews.img" :src="previews.url" alt="" > </div> </div> </div> </div> </template> <template #footer> <span class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="" @click="refreshCrop" >重置</el-button> <el-button type="primary" @click="onConfirm" > 確認(rèn) </el-button> </span> </template> </TipsDialog> </div> </template> <script lang="ts" setup> // 需要引入的庫(kù) import 'vue-cropper/dist/index.css' import { VueCropper } from 'vue-cropper' import { ref, watch, reactive } from 'vue' import TipsDialog from '~/components/TipsDialog/TipsDialog.vue' // 封裝的dialog組件 import { ElMessage } from 'element-plus' import { commonApi } from '../../api' // 封裝的api const dialogVisible = ref<boolean>(false) // dialog的顯示與隱藏 const emits = defineEmits(['confirm']) // 自定義事件 // 裁剪組件需要使用到的參數(shù) interface Options { img: string | ArrayBuffer | null // 裁剪圖片的地址 info: true // 裁剪框的大小信息 outputSize: number // 裁剪生成圖片的質(zhì)量 [1至0.1] outputType: string // 裁剪生成圖片的格式 canScale: boolean // 圖片是否允許滾輪縮放 autoCrop: boolean // 是否默認(rèn)生成截圖框 autoCropWidth: number // 默認(rèn)生成截圖框?qū)挾? autoCropHeight: number // 默認(rèn)生成截圖框高度 fixedBox: boolean // 固定截圖框大小 不允許改變 fixed: boolean // 是否開(kāi)啟截圖框?qū)捀吖潭ū壤? fixedNumber: Array<number> // 截圖框的寬高比例 需要配合centerBox一起使用才能生效 full: boolean // 是否輸出原圖比例的截圖 canMoveBox: boolean // 截圖框能否拖動(dòng) original: boolean // 上傳圖片按照原始比例渲染 centerBox: boolean // 截圖框是否被限制在圖片里面 infoTrue: boolean // true 為展示真實(shí)輸出圖片寬高 false 展示看到的截圖框?qū)捀? accept: string // 上傳允許的格式 } // 父組件傳參props interface IProps { type: string // 上傳類型, 企業(yè)logo / 瀏覽器logo allowTypeList: string[] // 接收允許上傳的圖片類型 limitSize: number // 限制大小 fixedNumber: number[] // 截圖框的寬高比例 fixedNumberAider?: number[] // 側(cè)邊欄收起截圖框的寬高比例 previewWidth: number // 預(yù)覽寬度 title?: string // 裁剪標(biāo)題 } // 預(yù)覽樣式 interface IStyle { width: number | string, height: number | string } /* 父組件傳參 */ const props = withDefaults(defineProps<IProps>(), { type: 'systemLogo', allowTypeList: () => ['jpg', 'png', 'jpeg'], limitSize: 1, fixedNumber: () => [1, 1], fixedNumberAider: () => [1, 1], previewWidth: 228, title: 'LOGO裁剪' }) // 裁剪組件需要使用到的參數(shù) const options = reactive<Options>({ img: '', // 需要剪裁的圖片 autoCrop: true, // 是否默認(rèn)生成截圖框 autoCropWidth: 150, // 默認(rèn)生成截圖框的寬度 autoCropHeight: 150, // 默認(rèn)生成截圖框的長(zhǎng)度 fixedBox: false, // 是否固定截圖框的大小 不允許改變 info: true, // 裁剪框的大小信息 outputSize: 1, // 裁剪生成圖片的質(zhì)量 [1至0.1] outputType: 'png', // 裁剪生成圖片的格式 canScale: true, // 圖片是否允許滾輪縮放 fixed: true, // 是否開(kāi)啟截圖框?qū)捀吖潭ū壤? fixedNumber: [1, 1], // 截圖框的寬高比例 需要配合centerBox一起使用才能生效 1比1 full: true, // 是否輸出原圖比例的截圖 canMoveBox: false, // 截圖框能否拖動(dòng) original: false, // 上傳圖片按照原始比例渲染 centerBox: true, // 截圖框是否被限制在圖片里面 infoTrue: true, // true 為展示真實(shí)輸出圖片寬高 false 展示看到的截圖框?qū)捀? accept: 'image/jpeg,image/jpg,image/png,image/gif,image/x-icon' }) const getStyle = ref<IStyle>({ width: '', height: '' }) /* 允許上傳的類型 */ const acceptType = ref<string[]>([]) // 裁剪后的預(yù)覽樣式信息 const previews: any = ref({}) const previewFileStyle = ref({}) // 裁剪組件Ref const cropperRef: any = ref({}) // input組件Ref const reuploadInput = ref<HTMLElement | null | undefined>() // 回顯圖片使用的方法 const onChange = (e: any) => { const file = e.target.files[0] const URL = window.URL || window.webkitURL // 上傳圖片前置鉤子,用于判斷限制類型用 if (beforeUploadEvent(file)) { options.img = URL.createObjectURL(file) dialogVisible.value = true } } /* 上傳圖片前置攔截函數(shù) */ const beforeUploadEvent = (file: File) => { const type = file.name.substring(file.name.lastIndexOf('.') + 1) // 獲得圖片上傳后綴 // 判斷是否符合上傳類型 const isAllowTye = props.allowTypeList.some(item => { return item === type }) if (!isAllowTye) { ElMessage.error(`僅支持${acceptType.value.join('、')}格式的圖片`) return false } return true } /* 重置裁剪組件 */ const refreshCrop = () => { // cropperRef裁剪組件自帶很多方法,可以打印看看 cropperRef.value.refresh() } /* 右旋轉(zhuǎn)圖片 */ const rotateRight = () => { cropperRef.value.rotateRight() } /* 放大縮小圖片比例 */ const changeScale = (num: number) => { const scale = num || 1 cropperRef.value.changeScale(scale) } // 縮放的格式 const tempScale = ref<number>(0) // 點(diǎn)擊上傳 const uploadFile = (type: string): void => { /* 打開(kāi)新的上傳文件無(wú)需生成新的input元素 */ if (type === 'reupload') { reuploadInput.value?.click() return } let input: HTMLInputElement | null = document.createElement('input') input.type = 'file' input.accept = options.accept input.onchange = onChange input.click() input = null } /* 上傳成功方法 */ const cropperSuccess = async (dataFile: File) => { const fileFormData = new FormData() fileFormData.append('file', dataFile) // 在接口請(qǐng)求中需要上傳file文件格式, 并且該接口需要改header頭部為form-data格式 const { code, data } = await commonApi.uploadFile(fileFormData) if (code.value === 200 && data.value) { return data.value } // 之前調(diào)用接口的方式 // axios('http://localhost:3001/adminSystem/common/api/upload', { // data: fileFormData, // method: 'POST', // headers: { // 'Content-Type': 'multipart/form-data' // } // }).then(async (result: any) => { // const res = await result // console.log(res, 'res') // }).catch((err: any) => { // console.log(err, 'err') // }) } // base64轉(zhuǎn)圖片文件 const dataURLtoFile = (dataUrl: string, filename: string) => { const arr = dataUrl.split(',') const mime = arr[0].match(/:(.*?);/)[1] const bstr = atob(arr[1]) let len = bstr.length const u8arr = new Uint8Array(len) while (len--) { u8arr[len] = bstr.charCodeAt(len) } return new File([u8arr], filename, { type: mime }) } // 上傳圖片(點(diǎn)擊保存按鈕) const onConfirm = () => { cropperRef.value.getCropData(async (data: string) => { const dataFile: File = dataURLtoFile(data, 'images.png') const res = await cropperSuccess(dataFile) // 觸發(fā)自定義事件 emits('confirm', res) return res }) dialogVisible.value = false } // 裁剪之后的數(shù)據(jù) const previewHandle = (data: any) => { previews.value = data // 預(yù)覽img圖片 tempScale.value = props.previewWidth / data.w previewFileStyle.value = { width: data.w + 'px', height: data.h + 'px', margin: 0, overflow: 'hidden', zoom: tempScale.value, position: 'relative', border: '1px solid #e8e8e8', 'border-radius': '2px' } } watch( () => props, () => { /* 預(yù)覽樣式 */ getStyle.value = { width: props.previewWidth + 'px', // 預(yù)覽寬度 height: props.previewWidth / props.fixedNumber[0] + 'px' // 預(yù)覽高度 } // 上傳格式tips信息 acceptType.value = [] for (let i = 0; i < props.allowTypeList.length; i++) { acceptType.value.push(props.allowTypeList[i].toUpperCase()) } }, { deep: true } ) /* 向子組件拋出上傳事件 */ defineExpose({ uploadFile }) </script> <style lang="scss" scoped> .cropper { width: 100%; height: 50vh; display: flex; overflow: hidden; .cropper_left { display: flex; flex-direction: column; .reupload_box { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; .reupload_text { color: var(--primary-color); cursor: pointer; } .rotate_right { margin-left: 16px; cursor: pointer; } } } .cropper_right { flex: 1; margin-left: 44px; .preview_text { margin-bottom: 12px; } } } </style>
4.在父組件中使用(HTML)
// 引入裁剪組件 import clipperDialog from '~/components/clipperDialog/clipperDialog.vue' HTML <clipperDialog ref="clipperRef" :type="clipperData.type" :allow-type-list="clipperData.allowTypeList" :limit-size="clipperData.limitSize" :fixed-number="clipperData.fixedNumber" :fixed-number-aider="clipperData.fixedNumberAider" :preview-width="clipperData.previewWidth" @confirm="onConfirm" />
5.定義props傳參(TS)
JS // 定義interface類型 interface IClipper { type: string // 上傳類型 allowTypeList: string[] // 接收允許上傳的圖片類型 limitSize: number // 限制大小 fixedNumber: number[] // 截圖框的寬高比例 fixedNumberAider?: number[] // 側(cè)邊欄收起截圖框的寬高比例 previewWidth: number // 預(yù)覽寬度 previewWidthAider?: number // 側(cè)邊欄收起預(yù)覽寬度 } const clipperData = ref<IClipper>({ type: '', allowTypeList: [], limitSize: 1, fixedNumber: [], previewWidth: 0 })
6.核心方法(TS)
/* 瀏覽器logo上傳 */ const browserUpload = (): void => { clipperData.value = { type: 'browserLogo', // 該參數(shù)可根據(jù)實(shí)際要求修改類型 allowTypeList: ['png', 'jpg', 'jpeg', 'peeee'], // 允許上傳的圖片格式 limitSize: 1, // 限制的大小 fixedNumber: [1, 1], // 截圖比例,可根據(jù)實(shí)際情況進(jìn)行修改 previewWidth: 100 // 預(yù)覽寬度 } // 打開(kāi)裁剪組件 clipperRef.value.uploadFile() } /* 保存logo自定義事件, 實(shí)際業(yè)務(wù)在此編寫(xiě) */ const onConfirm = (val: any): void => { console.log(val, '點(diǎn)擊保存按鈕后的圖片信息') }
總結(jié)
以上就是我封裝的裁剪組件,或許存在一些不足之處,還請(qǐng)大佬們多多指教!
到此這篇關(guān)于在Vue3項(xiàng)目中使用VueCropper裁剪組件(裁剪及預(yù)覽效果)的文章就介紹到這了,更多相關(guān)Vue3使用VueCropper裁剪組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 前端vue-cropperjs實(shí)現(xiàn)圖片裁剪方案
- Vue使用Vue-cropper實(shí)現(xiàn)圖片裁剪
- vue圖片裁剪插件vue-cropper使用方法詳解
- Vue-cropper 圖片裁剪的基本原理及思路講解
- 基于cropper.js封裝vue實(shí)現(xiàn)在線圖片裁剪組件功能
- vue-cli結(jié)合Element-ui基于cropper.js封裝vue實(shí)現(xiàn)圖片裁剪組件功能
- cropper js基于vue的圖片裁剪上傳功能的實(shí)現(xiàn)代碼
- vue移動(dòng)端裁剪圖片結(jié)合插件Cropper的使用實(shí)例代碼
相關(guān)文章
Vue服務(wù)端渲染和Vue瀏覽器端渲染的性能對(duì)比(實(shí)例PK )
這篇文章主要介紹了Vue服務(wù)端渲染和Vue瀏覽器端渲染的性能對(duì)比(實(shí)例PK ),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03vue中解決拖拽改變存在iframe的div大小時(shí)卡頓問(wèn)題
這篇文章主要介紹了vue中解決拖拽改變存在iframe的div大小時(shí)卡頓問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07vue項(xiàng)目中銷毀window.addEventListener事件監(jiān)聽(tīng)解析
這篇文章主要介紹了vue項(xiàng)目中銷毀window.addEventListener事件監(jiān)聽(tīng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07Vue3?使用v-model實(shí)現(xiàn)父子組件通信的方法(常用在組件封裝規(guī)范中)
這篇文章主要介紹了Vue3?使用v-model實(shí)現(xiàn)父子組件通信(常用在組件封裝規(guī)范中)的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06vue DatePicker日期選擇器時(shí)差8小時(shí)問(wèn)題
這篇文章主要介紹了vue DatePicker日期選擇器時(shí)差8小時(shí)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05vue?elementui?大文件進(jìn)度條下載功能實(shí)現(xiàn)
本文介紹了如何使用下載進(jìn)度條來(lái)展示下載進(jìn)度的方法,并展示了相關(guān)的效果圖,結(jié)合實(shí)例代碼介紹了vue?elementui?大文件進(jìn)度條下載的方法,感興趣的朋友一起看看吧2025-01-01vue項(xiàng)目使用uniapp生成app的全過(guò)程
這篇文章主要介紹了vue項(xiàng)目使用uniapp生成app的全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10