Vue学习笔记:vue-cropper实现封装截图组件

  • vue-cropper一款好用的前端截图插件

项目介绍

使用vue+elementUI写的项目,需要用到图片截图功能视频截图视频直播等功能,关于截图不失真,发现vue-cropper这款插件能满足需求,可以让用户自由调整截图框的宽高、位置,可以根据用户的需求选择截图的格式(png、jpg),也可以选择图片的编码格式(base64、blob)等。

案例展示:

http://zhb_nyx.gitee.io/cropperimg/#/cropperImg

官方网站:

https://github.com/xyxiao001/vue-cropper

组件文档:

图片裁剪 | zhb-ui

第一步:项目中引入vue-cropper插件

npm install vue-cropper --save 或者 npm install vue-cropper -S

第二步:引入依赖,

在main.js中引入

import VueCropper from 'vue-cropper'

Vue.use(VueCropper)

按需引入

import VueCropper from 'vue-cropper'

第三步:封装弹框内截图组件

如上图样式布局,

<template>
    <div class="imgCrop-body">
        <!--图片显示区域-->
        <div class="imgCrop-box">
            <!--原图-->
            <div class="imgCrop-con">
                <vue-cropper
                        ref="cropper"
                        :img="option.img"
                        :output-size="option.size"
                        :output-type="option.outputType"
                        :info="true"
                        :full="option.full"
                        :fixed="fixed"
                        :fixed-number="fixedNumber"
                        :can-move="option.canMove"
                        :can-move-box="option.canMoveBox"
                        :fixed-box="option.fixedBox"
                        :original="option.original"
                        :auto-crop="option.autoCrop"
                        :auto-crop-width="option.autoCropWidth"
                        :auto-crop-height="option.autoCropHeight"
                        :center-box="option.centerBox"
                        @real-time="realTime"
                        :high="option.high"
                        @img-load="imgLoad"
                        mode="cover">
                </vue-cropper>
            </div>
            <!--截图-->
            <div class="imgCrop-con imgCrop-con-preview" ref="imgCropPreviews">
                <div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px',  'overflow': 'hidden', 'margin': '5px'}">
                    <div :style="previews.div">
                        <img :src="previews.url" :style="previews.img">
                    </div>
                </div>
            </div>
        </div>
        <!--操作区域-->
        <div class="imgCrop-btn">
            <div class="ict-query-button">
                <label for="uploads" style="width:100%;height: 100%; ">上传</label>
                <input
                        type="file"
                        id="uploads"
                        style="position:absolute; clip:rect(0 0 0 0);"
                        ref="uploadImg"
                        accept="image/png, image/jpeg, image/gif, image/jpg"
                        @change="uploadImg($event, 1)"
                >
            </div>
            <div class="ict-query-button"  v-if="!crap"  @click="startCrop">截取</div>
            <div class="ict-query-button"  v-else  @click="stopCropAndSave">保存</div>
            <div class="ict-query-button" @click="changeScale(100)">放大</div>
            <div class="ict-query-button" @click="changeScale(-100)">缩小</div>
            <div class="ict-query-button" @click="clearCrop">清空</div>
            <div class="ict-query-button" @click="refreshCrop">刷新</div>
            <div class="ict-query-button" @click="rotateLeft">向右旋转90度</div>
            <div class="ict-query-button" @click="rotateRight">向左旋转90度</div>

        </div>
    </div>
    <!--到底啦!=======================================================-->
</template>
<script>
    export default {
        name: "cropperImgComponents",
        data(){
            return {
                crap: true,
                previews: {},
                option: {
                    // 裁剪图片的地址
                    img: '',
                    // 裁剪生成图片的质量
                    size: 1,
                    // 输出原图比例截图 props名full
                    full: false,
                    // 裁剪生成图片的格式
                    outputType: 'png',
                    // 上传图片是否可以移动
                    canMove: true,
                    // 固定截图框大小 不允许改变
                    fixedBox: false,
                    // 上传图片按照原始比例渲染
                    original: false,
                    // 截图框能否拖动
                    canMoveBox: true,
                    // 是否默认生成截图框
                    autoCrop: true,
                    // 只有自动截图开启 宽度高度才生效
                    // 默认生成截图框宽度
                    autoCropWidth: 400,
                    // 默认生成截图框高度
                    autoCropHeight: 350,
                    // 截图框是否被限制在图片里面
                    centerBox: false,
                    // 是否按照设备的dpr 输出等比例图片
                    high: true
                },
                show: true,
                // 是否开启截图框宽高固定比例
                fixed: false,
                // 截图框的宽高比例
                fixedNumber: [1, 2],
                // 裁剪框的大小信息
                info: true,
                // canScale 图片是否允许滚轮缩放
                canScale: true,
                // infoTrue  true 为展示真实输出图片宽高 false 展示看到的截图框宽高:
                infoTrue: true,
                // maxImgSize  限制图片最大宽度和高度
                maxImgSize: 2000,
                // enlarge 图片根据截图框输出比例倍数
                enlarge: 1,
                // mode  图片默认渲染方式
                mode: 'contain'
            }
        },
        mounted() {

        },
        methods:{
            setCropImg(){
                this.$refs.uploadImg.click()
                this.crap = true
            },
            //上传图片
            uploadImg(e, num) {

                var file = e.target.files[0]


                this.file = file
                console.log(file);
                if (file.size / 1024 / 1024 > 5) {
                    this.$message.info("上传文件不超过5M");
                    return;
                }
                if (!/\.(jpg|png|JPG|PNG)$/.test(file.name)) {
                    this.$message.info('图片类型必须是jpg,png中的一种')
                    return false
                }
                var reader = new FileReader()
                reader.onload = (e) => {
                    let data
                    if (typeof e.target.result === 'object') {
                        // 把Array Buffer转化为blob 如果是base64不需要
                        data = window.URL.createObjectURL(new Blob([e.target.result]))
                    } else {
                        data = e.target.result
                    }
                    if (num === 1) {
                        this.option.img = data
                    } else if (num === 2) {
                        this.example2.img = data
                    }
                }
                // 转化为base64
                // reader.readAsDataURL(file)
                // 转化为blob
                reader.readAsArrayBuffer(file)
            },
            imgLoad(msg) {
                console.log(msg)
                this.previews.url = msg.url
            },
            // 实时预览函数
            realTime(data) {
                this.previews = data
                this.$emit('', this.previews)
                console.log(data)
            },
            //开始截图
            startCrop() {
                this.crap = true
                this.$refs.cropper.startCrop()
            },
            //停止截图&保存
            stopCropAndSave() {
                this.crap = false
                this.$refs.cropper.stopCrop()
                this.$refs.cropper.getCropData((data) => {
                    this.base64ToFile(data,'cropFile')
                })
            },
            //清空截图
            clearCrop() {
                this.$refs.cropper.clearCrop()
                this.crap = false
            },
            //放大缩小
            changeScale(num) {
                num = num || 1
                this.$refs.cropper.changeScale(num)
            },
            //刷新
            refreshCrop() {
                this.$refs.cropper.refresh()
                this.crap = false
            },
            //向左旋转
            rotateLeft() {
                this.$refs.cropper.rotateLeft()
            },
            //向右旋转
            rotateRight() {
                this.$refs.cropper.rotateRight()
            },
            //base64ToFile
            base64ToFile(urlData, fileName) {
                var arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
                while(n--){
                    u8arr[n] = bstr.charCodeAt(n);
                }
                let imgFile = new File([u8arr], fileName, {type:mime});

                this.$parent.getCropApi(imgFile,urlData)


            },
        }
    }
</script>
<style lang="scss" scoped>
    .imgCrop-body{
        width: 100%;
        height: 100%;
        overflow: hidden;
        //图片显示区域
        .imgCrop-box{
            width: 100%;
            height: calc(100% - 100px);
            display: flex;
            justify-content: center;
            align-items: center;
            .imgCrop-con{
                width: calc(50% - 20px);
                height:  calc(100% - 20px);
                margin: 10px;
                &.imgCrop-con-preview{
                    background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");

                }
            }
        }
        //操作区域
        .imgCrop-btn{
            width: 100%;
            height: 50px;
            margin-top: 20px;
            display: flex;
            justify-content: center;
            >div{
                margin: 0 10px;
            }
        }


    }

</style>

第四步:封装点击打开文件上传,弹出截取图片

<template>
  <div class="upfile">
    <div class="addFile" v-if="isAddFile" @click="getCropImg">+</div>
    <div v-else class="addFileImg"  @click="getCropImg">
      <img   :src="imgBase64">
    </div>





    <!--截图区域-->
    <div class="CropDialogVisible" v-show="isCropDialogVisible">
      <div class="CropDialog-main" style="height: 80vh;position: relative;">
        <div class="CropDialog-close" @click="isCropDialogVisible = false">×</div>
        <cropper-img ref="cropImg"></cropper-img>
      </div>
    </div>

  </div>
</template>

<script>

  import cropperImg from './cropperImg.vue'
  export default {
    name: "upfile",
    props: ["type","imgUrl"],
    created() {
      console.log(this.imgUrl);
      if(this.imgUrl){
        this.imgBase64 = this.imgUrl
        this.filedisabled = false
      }


    },
    data() {
      return {

        imgBase64:'',
        //
        isCropDialogVisible:false,
        cropFile:'',
        isAddFile:true
      };
    },
    components:{
      cropperImg
    },
    methods: {
      getCropImg(){
        let that = this
        this.$refs.cropImg.setCropImg()
        setTimeout(function () {
          that.isCropDialogVisible = true
        },500)

      },
      getCropApi(file,base64){
        console.log(file, base64);
        this.imgBase64 = base64
        this.isCropDialogVisible = false
        this.isAddFile = false
        this.$emit("getCropFile",{File:file,Base64:base64})
      }



    }
  };
</script>

<style lang="scss" scoped>
  .CropDialogVisible{
    width: 100%;
    height: 100%;
    position: fixed;
    top:0;
    left: 0;
    background: rgba(0,0,0,.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    .CropDialog-main{
      width: 80%;
      height: 80%;
      background: #ffffff;
      .CropDialog-close{
        width: 40px;
        height: 40px;
        background: #666666;
        color: #ffffff;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        position: absolute;
        top: -12px;
        right: -20px;
        font-size: 38px;
        cursor: pointer;
      }

    }
  }
  .upfile{
    width: 200px;
    .addFileImg{
      display: block;
      width: 80px;
      height: 80px;
      background: #eeeeee;
      cursor: pointer;
      img{
        display: block;
        width: 100%;
        height: 100%;
      }
    }
    .addFile{
      width: 80px;
      height: 80px;
      border: 1px dotted #64686B;
      border-radius:4px;
      font-size: 35px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      color:#64686B;

    }
  }
  .avatar-file {
    text-align: center;
  }

  .avatar-file-text {
    width: 240px;
    border: 1px solid #dddddd;
    border-radius:3px;
    line-height: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 500;
    font-size: 14px;
    white-space: nowrap;
    padding: 0 5px;
    cursor: pointer;
    border-radius:0;
  }

  .avatar-file-text img {
    width: 16px;
    height: 16px;
    margin-right: 9px;
  }

  .avatar-file-imgbox {
    height: 65px;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    border: 1px solid#C0C4CC;
  }

  .filename {

    font-size: 14px;
    font-weight: 400;
    text-decoration: underline;
    text-align: left;
    color: #1d73f6;
    line-height: 22px;
    margin-left: 10px;
  }

  .filename span {
    width: 8px;
    height: 8px;
    // color: #ff0000;
    font-size: 36px;
    cursor: pointer;
  }
</style>

第五步:调用截取图片组件

<template>
<div style="width: 100%;display: flex;align-items: center;justify-content: center;">
    <div style="display: flex;align-items: center">
        <span style="margin-right: 20px;">截取图片</span>
        <cropper-file></cropper-file>
    </div>
</div>
</template>

<script>
    import cropperFile from './cropperFile.vue'
    export default {
        name: "cropper",
        data(){

        },
        methods:{

        },
        components:{
            cropperFile
        }
    }
</script>

<style scoped>

</style>

现已将该功能整合进我的UI组件库内,大家可以前往下载使用;

文档如上;

 

最后

热爱分享!乐于助人!

大家好,我是“前端互助会”,欢迎大家关注微信公众号“前端公众号”!

一起学习,共同成长!

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端互助会

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值