vue3.2中好用的圖片裁剪工具
阿新 • • 發佈:2022-03-08
vue3.2中好用的圖片裁剪工具
https://github.com/xyxiao001/vue-cropper
演示地址:按需保留需要的功能
http://github.xyxiao.cn/vue-cropper/example/
# 需要使用外層容器包裹並設定寬高
## 使用 注意: 需要關掉本地的mock服務, 不然圖片轉化會報錯
## vue 3 安裝使用
```
npm install vue-cropper@next
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
<template> <div> <el-row> <el-col :span="12"> <!-- 圖片上傳區域--> <div> <div style="width:100%;height: 60vh;"> <!-- <vue-cropper--> <!-- autoCrop--> <!-- ref="cropperInstance"--> <!-- centerBox/>--> <vue-cropper style="" :canMove="option.canMove" autoCrop :img="option.img" :centerBox="option.centerBox" :original="option.original" @realTime="realTime" ref="cropperInstance" :outputType="option.outputType" centerBox/> </div> </div> </el-col> <div> </div> </div> </div> <!-- <div v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</div>--> </el-card> </div></el-col> </el-row> <!-- <el-button @click="getCropData" block ref="cropper">獲取截圖後的圖片</el-button>--> <el-alert style="width: 50%" title="使用方法" type="success" description="點選圖片,通過滑鼠滑輪控制圖片縮放大小;外部裁剪框可拖動進行拉伸大小;點選圖片和裁剪框非重疊部分,可以移動圖片位置" show-icon > </el-alert> <label class="btn" for="uploads">選擇Logo檔案</label> <input type="file" id="uploads" ref="uploadsFileInstall" style="position: absolute; clip: rect(0 0 0 0)" accept="image/png, image/jpeg, image/gif, image/jpg" @change="selectImg($event)" /> <el-button size="small" type="primary" plain @click="changeScale(1)" > 放大 </el-button > <el-button size="small" type="primary" plain @click="changeScale(-1)" > 縮小</el-button > <!-- <el-button size="mini" type="danger" plain icon="el-icon-delete" @click="clearImg">清空素材</el-button> --> <el-button size="small" type="primary" plain @click="rotateLeft" >↺ 左旋轉</el-button > <el-button size="small" type="primary" plain @click="rotateRight" >↻ 右旋轉</el-button > <el-button size="small" type="success" :loading="uploadLoading" @click="uploadImg('blob')" >上傳logo <el-icon class="el-icon--right"><Upload /></el-icon></el-button> <!-- <!–預覽效果圖–>--> <!-- <div class="show-preview">--> <!-- <div class="show-preview-title">預覽</div>--> <!-- <div :style="previews.div" class="preview">--> <!-- <img v-show="previews.url" :src="previews.url" :style="previews.img" />--> <!-- </div>--> <!-- </div>--> <!-- <div>--> <!-- {{previews}}--> <!-- </div>--> <!-- <div>--> <!-- <img v-show="previews.url" :src="previews.url" alt="">--> <!-- </div>--> <div> <p>預覽截圖框內容:畫素點:width:{{previews.w}}px;height:{{previews.h}}px</p> </div> <section class="pre-item" style="border: 1px solid black;background: rgba(0,0,0,.5);" :style="{'width': previews.w + 'px', 'height': previews.h + 'px'}"> <div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden', 'margin': '0px'}"> <div :style="previews.div"> <img :src="previews.url" :style="previews.img"> </div> </div> </section> <div style="width: 100%;height: 40px;background-color: #ecdcde;display: flex;align-items: center;justify-content: center"> <el-button type="primary"> 醫院詳情 </el-button> </div> <div> <!-- 分為2個區域:圖片上傳處理:醫院資訊編輯=1:1--> <div> <el-row > <el-col :span="12"> <el-card> <template > <div class="clearfix"> <span>基本資料</span> </div> </template> <el-tabs v-model="activeTabsName"> <el-tab-pane label="醫院資料" name="first"> <el-form ref="formEditRef" :model="hospForm.editForm" label-width="80px"> <el-form-item label="醫院id" prop="hospId" > <el-input v-model="hospForm.editForm.hospId" disabled></el-input> </el-form-item> <el-form-item label="醫院名稱" prop="hospName" > <el-input v-model="hospForm.editForm.hospName" ></el-input> </el-form-item> <el-form-item label="省ID" prop="provinceId" > <el-input v-model="hospForm.editForm.provinceId" ></el-input> </el-form-item> <el-form-item label="市ID" prop="cityId" > <el-input v-model="hospForm.editForm.cityId" ></el-input> </el-form-item> <el-form-item label="區ID" prop="areaId" > <el-input v-model="hospForm.editForm.areaId" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="commitEditHospInfo">儲存</el-button> <!-- <el-button type="danger" @click="close">關閉</el-button>--> </el-form-item> </el-form> </el-tab-pane> </el-tabs> </el-card> </el-col> <el-col :span="12"> <!-- <div>--> <!-- <img style="background: white" :src="elImgSrc" alt="">--> <!-- logo預覽:{{elImgSrc}}--> <!-- </div>--> <div class="demo-image__preview"> <el-image v-if="hosp.info.reportLogo" crossorigin='anonymous' style="background: white" :src="elImgSrc" :initial-index="4" fit="cover" > </el-image> {{hosp.info}} </div> </el-col> </el-row> </div> </div> </div> </template> <script setup> import { Edit, Share, Delete, Search, Upload } from '@element-plus/icons-vue' import "vue-cropper/dist/index.css"; import { VueCropper } from "vue-cropper"; import {onMounted, reactive, ref, watch} from "vue"; import axios from "axios"; import {editHospByBtn, getHospByBtn, uploadLogo} from "../../api/HospInfoByBtn/HospInfoByBtn"; import {ElMessage, ElMessageBox} from "element-plus"; import moment from "moment"; import router from "../../router"; const option = reactive({ img: '', //裁剪圖片的地址 outputSize: 1, //裁剪生成圖片的質量(可選0.1 - 1) outputType: 'png', //裁剪生成圖片的格式(jpeg || png || webp) info: true, //圖片大小資訊 canScale: true, //圖片是否允許滾輪縮放 autoCrop: false, //是否預設生成截圖框 autoCropWidth: 230, //預設生成截圖框寬度 autoCropHeight: 35, //預設生成截圖框高度 fixed: false, //是否開啟截圖框寬高固定比例 fixedNumber: [7, 4], //截圖框的寬高比例 full: true, //false按原比例裁切圖片,不失真 fixedBox: true, //固定截圖框大小,不允許改變 canMove: true, //上傳圖片是否可以移動 canMoveBox: true, //截圖框能否拖動 original: true, //上傳圖片按照原始比例渲染 centerBox: false, //截圖框是否被限制在圖片裡面 height: true, //是否按照裝置的dpr 輸出等比例圖片 infoTrue: false, //true為展示真實輸出圖片寬高,false展示看到的截圖框寬高 maxImgSize: 3000, //限制圖片最大寬度和高度 enlarge: 1, //圖片根據截圖框輸出比例倍數 mode: '280px 160px' //圖片預設渲染方式 }) const chooseImageType = ref('jpg') const cropper=ref(null) const cropperInstance = ref(null) // 用於標識cropper元件例項 vue2.x 中的ref const uploadLoading = ref(false) const previews = ref({}) const previewStyle3 = ref({}) const activeTabsName =ref('first') const test=JSON.parse(window.sessionStorage.getItem("arr_start_menuRouter_NoBtn")) const hosp=reactive({ info:{ reportLogo:'' } }) //上傳圖片需要新增的token const SysUserToken= window.sessionStorage.getItem("SysUserToken") const tokenHeader=ref({accessToken:SysUserToken}) // const elImgSrc=ref('') const hospForm=reactive({ editForm:{ hospId:'', hospName:'', provinceId:'', cityId:'', areaId:'', } }) const formEditRef= ref(null) const size = ref('') const editHospDialogVisible=ref(false) //時間處理: const dateFormat=(date)=>{ return moment(date).format('YYYY-MM-DD HH:mm:ss') // 這裡可修改的格式 } //點選編輯醫院資訊 const editHospInfo=()=>{ //開啟對話方塊 hospForm.editForm.hospId=hosp.info.hospId hospForm.editForm.hospName=hosp.info.hospName hospForm.editForm.provinceId=hosp.info.provinceId hospForm.editForm.cityId=hosp.info.cityId hospForm.editForm.areaId=hosp.info.areaId editHospDialogVisible.value=true } //監聽:編輯醫院資訊對話方塊關閉:清空表單,還原警告 const editHospDialogClosed=()=>{ //清空表單 formEditRef.value.resetFields(); } //點選取消 編輯醫院資訊 const cancleEidtHospInfo=()=>{ //清空表單 formEditRef.value.resetFields(); //關閉彈出框 editHospDialogVisible.value=false } //編輯角色:點選儲存按鈕,提交所有的醫院資訊 const commitEditHospInfo=()=>{ //校驗欄位 //提交資料 const param ={ } param.hospId=hospForm.editForm.hospId param.hospName=hospForm.editForm.hospName param.provinceId=hospForm.editForm.provinceId param.cityId=hospForm.editForm.cityId param.areaId=hospForm.editForm.areaId //發起請求 editHospByBtn(param).then(res =>{ if (res.code === 200) { ElMessage({ message: "修改醫院資訊成功", type: 'success', showClose: true, duration: 1000, }) //清空表單 // formEditRef.value.resetFields() //重新整理列表 getHospInfo() // // console.log("重新請求列表成功") // editHospDialogVisible.value =false //關閉對話方塊 }else { ElMessage({ message: "修改醫院資訊失敗"+res.msg, type: 'error', showClose: true, duration: 1000, }) //跳出 } }) } //編輯角色:點選提交按鈕,提交所有的醫院資訊 const commitEditRoleInfo=()=>{ //校驗欄位 //提交資料 const param ={ } param.hospId=hospForm.editForm.hospId param.hospName=hospForm.editForm.hospName param.provinceId=hospForm.editForm.provinceId param.cityId=hospForm.editForm.cityId param.areaId=hospForm.editForm.areaId //發起請求 editHospByBtn(param).then(res =>{ if (res.code === 200) { ElMessage({ message: "修改醫院資訊成功", type: 'success', showClose: true, duration: 1000, }) //清空表單 formEditRef.value.resetFields() //重新整理列表 getHospInfo() // console.log("重新請求列表成功") editHospDialogVisible.value =false //關閉對話方塊 }else { ElMessage({ message: "修改醫院資訊失敗"+res.msg, type: 'error', showClose: true, duration: 1000, }) //跳出 } }) } //獲取醫院資訊 const getHospInfo=()=>{ //校驗欄位 //提交資料 const param ={ } param.hospId=hosp.info.hospId //發起請求 getHospByBtn(param).then(res =>{ if (res.code === 200) { //更新醫院資訊 hosp.info=res.data hospForm.editForm.hospId=hosp.info.hospId hospForm.editForm.hospName=hosp.info.hospName hospForm.editForm.provinceId=hosp.info.provinceId hospForm.editForm.cityId=hosp.info.cityId hospForm.editForm.areaId=hosp.info.areaId //更新圖片資訊 elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo }else { ElMessage({ message: "獲取醫院資訊失敗"+res.msg, type: 'error', showClose: true, duration: 1000, }) //跳出 } }) } const handleClose =(done)=>{ // ElMessageBox.confirm('Are you sure to close this dialog?') ElMessageBox.confirm('您確定要關閉此對話方塊嗎?') .then(() => { done() }) .catch(() => { // catch error }) } // 圖片上傳成功之後的回撥函式 const uploadFileSuccess=(response, file, fileList)=>{ // console.log(response) if(response.code===200){ ElMessage({ message: "上傳圖片成功", type: 'success', showClose: true, duration: 1000, }) getHospInfo() }else { ElMessage({ message: response.msg, type: 'error', showClose: true, duration: 1000, }) } } // const elImgSrc=ref('') const elImgSrc=ref('http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo) watch(hosp.info,()=>{ elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo }) //檔案上傳之前進行校驗 const beforeAvatarUpload=(file, fileList)=>{ //獲取上傳檔案的字尾名 const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1); const whiteList = ["jpeg", "png", "bmp", "jpg"]; if (whiteList.indexOf(fileSuffix) === -1) { ElMessage({ message: "檔案格式錯誤,選上傳:.jpeg,.png,.bmp,.jpg。", type: 'error', showClose: true, duration: 1000, }) file.abort() //取消上傳請求 return; } //獲取上傳檔案大小 let imgSize = Number(file.size / 1024 ); if (imgSize > 200) { ElMessage({ message: "檔案大小不能超過200k,請重新上傳。", type: 'error', showClose: true, duration: 1000, }) file.abort() //取消上傳請求 return; }else { } } //初始化 onMounted(() => { /** * 1、判斷是否是路由跳轉 */ if(router.currentRoute.value.params.Report_ByHospital){ hosp.info=JSON.parse(router.currentRoute.value.params.Report_ByHospital) elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo getHospInfo() } /** * 2、判斷是否有快取:醫院管理 */ else if(JSON.parse(window.sessionStorage.getItem("router_Report_HospInfoByBtn")) ){ hosp.info=JSON.parse(window.sessionStorage.getItem("router_Report_HospInfoByBtn")) elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo getHospInfo() } else { hosp.info.hospId='' hosp.info.reportLogo='' hosp.info.hospName='' hosp.info.provinceId='' hosp.info.cityId='' hosp.info.areaId='' elImgSrc.value='' } //醫院資訊賦值 hospForm.editForm.hospId=hosp.info.hospId hospForm.editForm.hospName=hosp.info.hospName hospForm.editForm.provinceId=hosp.info.provinceId hospForm.editForm.cityId=hosp.info.cityId hospForm.editForm.areaId=hosp.info.areaId }) const realTime = function(data){ previews.value = data //固定高寬 計算 :按照比例計算 let x_scale=308 / previews.value.w let y_scale=44 / previews.value.h // console.log("previews.w",previews.value.w) // console.log("previews.h",previews.value.h) // console.log("x_scale",x_scale) // console.log("y_scale",y_scale) // let x_scale=0.5 // let y_scale=0.5 previewStyle3.value = { width: previews.value.w + "px", height: previews.value.h + "px", overflow: "hidden", margin: "0", transform:"scale("+x_scale+","+y_scale+")", background:"white", // let zoom_value=100 / previews.value.w // let zoom_value1=100 / zoom_value.w }} const getCropData=(data)=>{ console.log("data:",data) console.log(cropper.value) cropper.value.getCropData(data=>{ console.log(data) }) } // 選擇圖片 const selectImg = function(e){ if(!e.target.files.length) return if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) { alert('圖片型別要求:jpeg、jpg、png') return false } let file = e.target.files[0] let strArr = file.name.split('.') chooseImageType.value = strArr[strArr.length-1] let reader = new FileReader() reader.onload = (e) => { let data if (typeof e.target.result === 'object') { data = window.URL.createObjectURL(new Blob([e.target.result])) } else { data = e.target.result } option.img = data } //轉化為base64 reader.readAsDataURL(file) } // 圖片縮放 const changeScale = function(num){ num = num || 1 cropperInstance.value.changeScale(num) } // 向左旋轉 const rotateLeft = function(){ cropperInstance.value.rotateLeft() } // 向右旋轉 const rotateRight = function(){ cropperInstance.value.rotateRight() } // 上傳圖片 const uploadImg = function(type){ if(type !== 'blob') return cropperInstance.value.getCropBlob(async (data)=>{ let formData = new FormData(); formData.append('logoFile',data,`DX.${chooseImageType.value}`) formData.append('hospId',hosp.info.hospId) //呼叫axios上傳 將請求地址改為自己圖片上傳的地址 uploadLoading.value = true uploadLogo(formData).then(res =>{ if (res.code === 200) { ElMessage({ message: "logo上傳成功", type: 'success', showClose: true, duration: 1000, }) getHospInfo() uploadLoading.value = false }else { ElMessage({ message: +res.msg, type: 'error', showClose: true, duration: 1000, }) uploadLoading.value = false } }) }) } </script> <style lang="less" scoped> /deep/ .cropper-move{ background: rgba(0,0,0,.5)!important; } .btn { outline: none; display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; -webkit-appearance: none; text-align: center; -webkit-box-sizing: border-box; box-sizing: border-box; outline: 0; -webkit-transition: .1s; transition: .1s; font-weight: 500; padding: 8px 15px; font-size: 12px; border-radius: 3px; color: #fff; background-color: #409EFF; border-color: #409EFF; margin-right: 10px; } .show-preview{ background-color: #FFFFFF; } </style>