【vue】cropperjs图片裁剪

11 篇文章 0 订阅

    在一些需求中,需要上传图片,而且有时上传的图片是要裁剪成一定的比例才能上传,这时我们就可以用cropperjs实现这个功能。

    cropperjs可以进行非常灵活的配置来实现图片的裁剪,可以生成一个裁剪之后的canvas对象,还可以利用toDataURL方法生成Base64格式的图片。如果不使用canvas的方式,利用该工具丰富的api可以拿到裁剪区域相对于原图的各项数据,使用这些数据进行css绝对定位即可展示裁剪后的图,该方式可以保证图片不失真和完整。支持在移动端或者pc端(IE9+)使用。

官方:cropperjs的github文档地址

废话不多说,现在开始演示在vue中如何使用:

一、引入:
npm install cropperjs
二、使用:

主要和vant结合使用
创建一个组件cutImg.vue

cutImg.vue

template部分:

<template>
    <div class="cut_img">
        <div class="top_panel">
            <span @click="$emit('cancel')">取消</span>
            <span @click="save">完成</span>
        </div>
         <!-- 裁剪的主体部分 -->
        <div class="container">
            <div class="img-container">
                <img :src="img" ref="image" />
            </div>
        </div>
        <div class="bottom_panel">
            <div @click="rotate">
                <VantIcon :name="require('@imgs/icon/rotate.png')" />
                <span>旋转</span>
            </div>
            <div @click="resetRotate">
                <VantIcon :name="require('@imgs/icon/reset.png')" />
                <span>还原</span>
            </div>
        </div>
    </div>
</template>

主要的效果是:

script部分:

<script>
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
import { base64ToFile } from "@tools/handle.js";
import { Icon as VantIcon } from "vant";

export default {
    name: "CutImg",
    props: {
        width: {
            type: Number,
            default: window.innerWidth * 0.8,
        },
        height: {
            type: Number,
            default: window.innerWidth * 0.8 * 0.5625,
        },
        img: String, // 从本地上传的图片
        ratio: { // 裁剪的比例
            type: Number, 
            default: 1,
        },
    },
    components: { VantIcon },
    data() {
        return {
            myCropper: null,
            cropperInstance: null,
        };
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            let vm = this;
            // 实例化cropper
            this.myCropper = new Cropper(this.$refs.image, {
                aspectRatio: this.ratio, // 裁剪的比例
                dragMode: "move", // 模式,可移动
                background: false,
                ready() {
                    vm.cropperInstance = this.cropper; // 裁剪下来的图片
                },
            });
        },
        // 以90度进行旋转
        rotate() {
            this.cropperInstance.rotate(-90);
        },
        // 还原
        resetRotate() {
            this.cropperInstance.reset();
        },
        save() {
        	// 获取裁剪下来图片的base64,其质量为medium
            let base64 = this.myCropper.getCroppedCanvas({
                    imageSmoothingQuality: "medium",
                }).toDataURL("image/jpeg");
            let file = base64ToFile(base64,`${new Date().getTime().toString()}.jpg`);
            this.$emit("ok", { base64, file });
        },
    },
};

到此为止,裁剪的组件已经搭建完成,现在就是在我们需要的地方进行使用

home.vue

<template>
    <div class="home">
        <Uploader
            type="file"
            v-model="img"
            :max-count="1"
            accept="image/*"
            :after-read="afterRead"
            :before-read="beforeRead"
            :preview-options="preOptions"
        >
            <img class="photo" :src="require('@imgs/upload.png')" alt="" />
        </Uploader>
        <Popup
            v-model="showCutImg"
            position="bottom"
            :style="{ height: '100%', background: '#000' }"
            get-container="#cutImg_box"
        >
            <CutImg
                v-if="showCutImg"
                :ratio="1 / 1"
                :img="imgWillCut"
                @cancel="onCutCancel"
                @ok="onCutOk"
            />
        </Popup>
    </div>
</template>

<script>
import { compressImg } from "@tools/handle.js";
import { Uploader, Popup, Toast } from "vant";
import CutImg from "@comp/CutImg";

export default {
    name: "Home",
    components: {
        Uploader,
        Popup,
        CutImg,
    },
    data() {
        return {
            showCutImg: false,
            imgWillCut: "",
            img: []
        };
    },
    computed: {
        preOptions() {
            return {
                showIndex: false,
                closeable: true,
            };
        },
    },
    methods: {
        // 读取图片数据
        afterRead(ctn) {
            this.showCutImg = true;
            console.log("读取图片数据", ctn);
            this.imgWillCut = ctn.content;
        },
        // 读取图片数据之前,检测图片大小
        beforeRead(file) {
            // 大于 2m 小于 10M 则压缩, 大于 10M 则提示图片过大
            return new Promise((resolve, reject) => {
                if (file.size > 10485760) {
                    Toast(`图片大小不得超过10M,请重新选择图片`);
                    return reject(false);
                }
                // 小于5M直接生成
                if (file.size < 2097152) return resolve(file);
                // 大于5M则压缩
                if (file.size >= 2097152) {
                    Toast.loading({
                        message: "图片加载中...",
                        forbidClick: true,
                    });
                    // 对图片进行压缩
                    compressImg(file)
                        .then((fs) => {
                            resolve(fs);
                        })
                        .catch((err) => {
                            console.log("压缩图片报错", err);
                        })
                        .finally(() => {
                            Toast.clear();
                        });
                }
            });
        },
        // 取消裁剪
        onCutCancel() {
            this.showCutImg = false;
        },
        // 裁剪成功
        onCutOk(result) {
            this.showCutImg = false;
            this.img[0].file = result.file;
            this.img[0].content = result.base64;
        },
    },
};
</script>

<style lang="scss">
.home {
    min-height: 100vh;
    font-size: 20px * $scale;
    background-color: rgba(138, 0, 11, 0.7);
    .van-uploader {
        display: block;
        width: 500px * $scale;
        height: 500px * $scale;
        margin: 0 auto;
    }
    .van-uploader__input-wrapper {
        margin: 0 auto;
    }
    .van-uploader__preview {
        margin: 0;
        width: 100%;
    }
    .van-uploader__preview-image {
        width: 100%;
        height: 100%;
    }
}
</style>

其中handle.js

const base64ToFile = (dataurl, fileName) => {
    // global atob Uint8Array File
    let arr = dataurl.split(',')
    let imgType = arr[0].match(/:(.*?);/)[1]
    let bstr = atob(arr[1])
    let n = bstr.length
    let u8arr = new Uint8Array(n)
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], fileName, { type: imgType })
}

function fileToBase64(file) {
    // 创建 FileReader 解析为 base64 格式
    let fileReader = new FileReader()
    fileReader.readAsDataURL(file)
    return new Promise((resolve, reject) => {
        // 解析成功
        fileReader.onload = function () {
            resolve(fileReader.result)
        }
        // 发生错误
        fileReader.onerror = function (err) {
            reject('error: 文件讀取發生錯誤', JSON.stringify(err))
        }
    })
}

// 压缩图片
const compressImg = (file) => {
    return new Promise((resolve, reject) => {
        fileToBase64(file)
            .then(src => {
                // 根据 src 渲染图像
                let img = new Image()
                img.src = src
                // 等待图片加载完毕
                img.onload = () => {
                    // 根据图片, 绘制 canvas
                    let canvas = document.createElement('canvas')
                    let ctx = canvas.getContext('2d')
                    let maxWidth = 1024;
                    let scale = Math.min(1, maxWidth / Math.max(img.width, img.height));
                    canvas.width = img.width * scale
                    canvas.height = img.height * scale
                    ctx.fillStyle = '#fff'
                    ctx.fillRect(0, 0, canvas.width, canvas.height)
                    ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
                    // 进行压缩, 获取 base64
                    let base64 = canvas.toDataURL('image/jpeg', 0.5)
                    // 响应结果
                    let nfile = base64ToFile(base64, file.name)
                    resolve(nfile)
                }
                img.onerror = (err) => {
                    reject(err)
                }
            }).catch(err => {
                reject(err)
            })
    })
}

export {
    base64ToFile,
    compressImg
};

最后的效果流程:
在这里插入图片描述

相应代码:代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值