【Vue】插件:九、vue-cropper 实现图片裁剪功能

1. 安装

官方参考文档

npm i vue-cropper

2. 使用

2.1 单独封装插件

|-src
|-|-plugins
|-|-|-vue-croppper.js

import Vue from 'vue';
import VueCropper from 'vue-cropper';

Vue.use(VueCropper);

2.2 main.js中

import './plugins/vue-cropper';// 图片裁剪插件

3 封装组件为by-cropper

3.1 template

/* 图片裁剪 */
<template>
    <div class="vue-cropper-box">
        <div class="vue-cropper-header">
            <p class="title">图片裁剪</p>
            <i type="primary" class="el-icon-close" @click="close()"></i>
        </div>
        <div class="vue-cropper-operate">
            <button class="basicButton" @click="cropperChangeScale(1)">放大</button>
            <button class="basicButton" @click="cropperChangeScale(-1)">缩小</button>
            <button class="basicButton" @click="cropperRotateLeft">左旋转</button>
            <button class="basicButton" @click="cropperRotateRight">右旋转</button>
            <button class="basicButton" @click="cropperDown('blob')">下载</button>
            <button class="basicButton" @click="cropperFinish">确定裁剪</button>
        </div>
        <div class="vue-cropper-content">
            <vueCropper
                ref="cropper"
                :img="cropperOption.img"
                :outputSize="cropperOption.outputSize"
                :outputType="cropperOption.outputType"
                :info="cropperOption.info"
                :full="cropperOption.full"
                :canMove="cropperOption.canMoveBox"
                :canMoveBox="cropperOption.canMoveBox"
                :original="cropperOption.original"
                :canScale="cropperOption.canScale"
                :autoCrop="cropperOption.autoCrop"
                :autoCropWidth="cropperOption.autoCropWidth"
                :autoCropHeight="cropperOption.autoCropHeight"
                :fixed="cropperOption.fixed"
                :fixedNumber="cropperOption.fixedNumber"
                @realTime="realTime"
                @imgLoad="imgLoad"
            ></vueCropper>
        </div>
    </div>
</template>

3.2 script

<script>
export default {
    name: 'by-cropper',
    components: {},
    inject: ['reload'],
    filters: {},
    props: {
        file: {
            default: () => {},
            required: true
        },
        cropperOption: {
            default: () => ({
                img: '', // 裁剪图片的地址
                info: true, // 裁剪框的大小信息
                outputSize: 1, // 裁剪生成图片的质量
                full: false, // 输出原图比例截图 props名full
                outputType: 'jpeg', // 裁剪生成图片的格式
                canMove: true, // 能否拖动图片
                original: false, // 上传图片是否显示原始宽高
                canMoveBox: true, // 能否拖动截图框
                canScale: false, // 图片是否允许滚轮缩放
                autoCrop: true, // 是否默认生成截图框
                autoCropWidth: 400, // 默认生成截图框宽度
                autoCropHeight: 300, // 默认生成截图框高度
                fixedBox: false, // 截图框固定大小
                fixed: true, // 是否开启截图框宽高固定比例
                fixedNumber: [4, 3] // 截图框的宽高比例
            })
        }
    },
    data() {
        return {
        };
    },
    computed: {
        // computeFunction() {
        //     return value;
        // }
    },
    watch: {
    },
    created() {
        // this.init();
    },
    mounted() {
        // this.init();
    },
    methods: {

        // ---------------图片裁剪功能-开始---------------
        cropperChangeScale(num) {
            num = num || 1;
            this.$refs.cropper.changeScale(num);
        },

        // 左旋转
        cropperRotateLeft() {
            this.$refs.cropper.rotateLeft();
        },

        // 右旋转
        cropperRotateRight() {
            this.$refs.cropper.rotateRight();
        },

        // 下载图片
        cropperDown(type) {
            console.log('down');
            let aLink = document.createElement('a');
            aLink.download = 'author-img';
            if (type === 'blob') {
                this.$refs.cropper.getCropBlob(data => {
                    this.downImg = window.URL.createObjectURL(data);
                    aLink.href = window.URL.createObjectURL(data);
                    aLink.click();
                });
            } else {
                this.$refs.cropper.getCropData(data => {
                    this.downImg = data;
                    aLink.href = data;
                    aLink.click();
                });
            }
        },

        // 确定裁剪
        cropperFinish(type) {
            if (type === 'Blob') {
                this.$refs.cropper.getCropBlob((data) => {
                    let file = data;
                    file.name = this.file.name;
                    this.$emit('cropperFinish', file, data);
                });

            } else {
                this.$refs.cropper.getCropData(data => {

                    // 将剪裁后base64的图片转化为file格式
                    let file = this.convertBase64UrlToBlob(data);
                    file.name = this.file.name;

                    this.$emit('cropperFinish', file, data);
                });
            }

        },

        // 将base64的图片转换为file文件
        convertBase64UrlToBlob(urlData) {
            let bytes = window.atob(urlData.split(',')[1]); // 去掉url的头,并转换为byte
            // 转化为base64
            // reader.readAsDataURL(file)
            // 转化为blob
            // 处理异常,将ascii码小于0的转换为大于0
            let ab = new ArrayBuffer(bytes.length);
            let ia = new Uint8Array(ab);
            for (let i = 0; i < bytes.length; i++) {
                ia[i] = bytes.charCodeAt(i);
            }
            return new Blob([ab], { type: 'image/jpeg' });
        },

        // 实时预览函数
        realTime(data) {
            // console.log('realTime');
        },

        // 图片已加载
        imgLoad(msg) {
            // console.log('imgLoad');
            // console.log(msg);
        },

        close() {
            this.$emit('close');
        }

    }
};
</script>

3.3 style 样式仅供参考

<style lang="less" scoped>
// 基础按钮
.basicButton {
  font-size: @font-size-m;
  padding: 0 0.1rem;
  line-height: 0.4rem;
  border: none;
  border-radius: @border-radius-middle;
  color: #fff;
  background: @base-color-blue;

  &:not(:last-child) {
    margin-right: 0.2rem;
  }

  &.disabled {
    opacity: 0.5;
  }

  &.cancel{
    background: @base-color-grey;
  }

  &.warning {
    background: @base-color-red;
  }

  &.black {
    background-color: @base-color-black;
  }
}
/* 图片裁剪工具 */
.vue-cropper-box{
  z-index: 3001;
  position: absolute;
  top: 0;
  width: 100%;
  height: 95%;
  background: #fff;  

  .vue-cropper-operate{
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.2rem;
  }

  .vue-cropper-content{
    width: 100%;
    height:calc(100% - 1rem);
    position: relative;
    overflow-y: auto;

    .vue-cropper{
      position: absolute;
    }
  }
}
</style>

4. 使用组件

4.1 template片段

<div v-if="isShowCropper">
    <by-cropper
        :cropperOption="cropperOption"
        :file="cropperFile"
        @cropperFinish="cropperFinish"
        @close="()=>{isShowCropper=false}"
        ></by-cropper>
</div>

4.2 script片段

vuex 中的上传接口,接口如何写可以参考文章【Vue教程】Vue Cli3项目结构优化【Vue教程】vuex、axios请求接口的几种方式

import ByCropper from '@/components/common/ByCropper';

export default {
    data() {
        return {
            /* vue-cropper 配置信息 */
            isShowCropper: false, // 是否显示-图片裁剪-弹窗
            cropperFile: {}, // 当前待裁剪图片的文件信息
            cropperOption: {
                img: '', // 裁剪图片的地址
                info: true, // 裁剪框的大小信息
                outputSize: 1, // 裁剪生成图片的质量
                full: false, // 输出原图比例截图 props名full
                outputType: 'jpeg', // 裁剪生成图片的格式
                canMove: true, // 能否拖动图片
                original: false, // 上传图片是否显示原始宽高
                canMoveBox: true, // 能否拖动截图框
                canScale: false, // 图片是否允许滚轮缩放
                autoCrop: true, // 是否默认生成截图框
                autoCropWidth: 400, // 默认生成截图框宽度
                autoCropHeight: 300, // 默认生成截图框高度
                fixedBox: false, // 截图框固定大小
                fixed: true, // 是否开启截图框宽高固定比例
                fixedNumber: [4, 3] // 截图框的宽高比例
            },
            methods:{
                // 以下方法可以根据 封装组件 this.$emit('cropperFinish', file, data); 中获取的 file data 自定义进行处理
                // 获取到文件时,进行如下处理
                handleFile(file){
                    this.cropperFile = file;
                    this.cropperOption.img = file.src;
                    this.isShowCropper = true;
                },
                // 确定裁剪
                cropperFinish(file) {
                    // 请求接口-上传文件
                    this.postUploadFile(file);
                },
                // 文件上传-接口-上传文件
                postUploadFile(file) {
                    console.log('PostUploadFile>', file);
                    let formData = new FormData();
                    formData.append('file', file, file.name);
                    // vuex 中的上传接口,接口如何写可以参考文章[杀杀杀]()
                    this.uploadFileCase(formData).then(res => {
                    console.log('uploadFileCase>', file);
                    if (res.code === 1) {
                    // 自定义操作
                    // this.fileList.push({
                        // name: file.name,
                        // src: file.src,
                       //  filePath: res.data,
                       //  file: file
                    // });
                    // this.ruleForm.imgPath = res.data;
                    // this.ruleForm.file = file;

                    this.isShowCropper = false;
                    this.cropperOption.img = null;
                    this.cropperFile = {};
                    }
                });
              },
            },
        }
    }
}

参考文档网址:
一个优雅的图片裁剪插件:vue-cropper
Vue项目图片剪切上传——vue-cropper的使用
vue使用vue-cropper实现裁剪、上传、下载功能

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342