zoukankan      html  css  js  c++  java
  • vue 等比例截图组件,支持缩放和旋转

    <template>
        <div class="crop-image" :style="wrapStyle">
             <img :src="url" alt="" crossOrigin="anonymous" :style="imgStyle" v-if="url">
            <canvas ref="canvas"  @mousedown="onmousedown" @mousemove="onmousemove" @mousewheel="onmousewheel" ></canvas>
        </div>
    </template>
    <script>
    export default {
        name:"crop-image",
        props:{
            opt:{
                type:Object,
                default(){
                    return {
                        400,
                        height:400,
                        addScale:0.06  // 缩放的速度
                    };
                }
            }
        },
        data(){
            return {
                img:null,
                url:null,
                imgInfo: null, // 压缩前的信息
                imgCInfo:null, // 压缩后的信息
                clipInfo:null, // 压缩前的信息
                clipCinfo:null, // 压缩后的信息
                ctx:null, // 画板
                pos:{x:0,y:0},
                deg:0
            }
        },
        computed:{
            wrapStyle(){
                return {
                    `${this.opt.width}px`,
                    height:`${this.opt.height}px`
                }
            },
            imgStyle(){
                let imgCInfo = this.imgCInfo;
                if(imgCInfo){
                    return {
                         `${imgCInfo.w}px`,
                        height: `${imgCInfo.h}px`,
                        left:`${this.pos.x}px`,
                        top:`${this.pos.y}px`,
                        transform: `rotateZ(${this.deg}deg)`,
                        transformOrigin: "center"
                    }
                }
                return {height:"0px",height:"0px",display:"none"};
            }
        },
        mounted(){
            let canvas = this.$refs.canvas;
            canvas.width = this.opt.width;
            canvas.height = this.opt.height;
            this.ctx = canvas.getContext("2d");
            window.addEventListener("mouseup",this.onmouseup);
        },
        methods:{
            init(src,bound){
                this.setSrc(src)
                this.setClip(bound);
            },
         
            loadImage(url,attrs){
                
                let img = new Image();
                img.src = url;
                attrs = attrs || {};
                for(let i in attrs){
                    img[i] = attrs[i];
                }
                return new Promise((resolve,reject)=>{
                    img.onload = function(){
                        resolve(img);
                    };
                    img.onerror  = reject;
                });
            },
            compress(target){
                // 压缩比例
                let opt = this.opt;
                let wRate = opt.width/opt.height;
                let tRate =  target.width/target.height;
                let w,h,scale;
                if(wRate > tRate){
                    h = opt.height;
                    scale = h/target.height;
                    w = scale * target.width;
                }else{
                    w = opt.width;
                    scale = w / target.width;
                    h = scale * target.height;
                }
                return {
                    w,h,scale
                }
            },
            // 外部调用
            async setSrc(src){
                let img = await this.loadImage(src,{crossOrigin:"anonymous"});
                this.url = src;
                this.img = img;
                this.setImg(img);
            },
            // 外部调用
            setClip(clipInfo){
                if(clipInfo){
                    this.clipInfo = {
                        w:clipInfo.width,
                        h:clipInfo.height
                    };
                    this.clipCinfo =  this.compress(clipInfo);
                    this.clipCinfo.x = (this.opt.width - this.clipCinfo.w)/2;
                    this.clipCinfo.y = (this.opt.height - this.clipCinfo.h)/2;
                    this.fill();
                }
            },
            async exportBase(){
                // 导出图片 base64
                let deg = this.deg;
                let pos = this.pos;
                let clipCinfo = this.clipCinfo;
                
                let imgCInfo = this.imgCInfo; // 压缩后的宽高
                let imgInfo = this.imgInfo;   // 原始的宽高
                
                let scale = imgCInfo.scale;
                let sx =  clipCinfo.x - pos.x;
                let sy =  clipCinfo.y - pos.y;
                let canvas = document.createElement("canvas");
                canvas.width = parseInt(clipCinfo.w / scale);
                canvas.height = parseInt(clipCinfo.h / scale);
                let swidth = imgInfo.w;
                let sheight = imgInfo.h;
                if(swidth < canvas.width){
                    swidth = canvas.width
                }
                if(sheight < canvas.height){
                    sheight = canvas.height
                }
                let ctx = canvas.getContext("2d");
                let img = this.img;
                if(deg != 0){
                    img = await this.loadImage(this.getRotateImg());
                    if(deg == 90 || deg == 270){
                        // 竖着的,需要坐标转换
                        let x = pos.x + (imgCInfo.w -imgCInfo.h)/2;
                        let y = pos.y  + (imgCInfo.h -imgCInfo.w)/2;
                        sx = clipCinfo.x - x;
                        sy = clipCinfo.y - y;
                    }
                }
                sx /= scale;
                sy /= scale;
                ctx.drawImage(img,sx,sy,swidth,sheight,0,0,swidth,sheight);
                return  canvas.toDataURL("image/png");
            },
            rotate(flag){
                // 旋转
                let deg = this.deg;
                if(flag){
                    deg += 90;
                }else{
                    deg -= 90;
                }
                if(deg >= 360){
                    deg -= 360;
                }
                if(deg <= 0){
                    deg += 360;
                }
                this.deg = deg;
            },
            getRotateImg(){
                // 获取旋转后的图片
                let deg = this.deg;
                let imgInfo = this.imgInfo;
                let canvas = document.createElement("canvas");
                let w,h;
                if(deg == 90 || deg == 270){
                    // 垂直
                    w = imgInfo.h;
                    h = imgInfo.w;
                }else{
                    // 横着的
                    w = imgInfo.w;
                    h = imgInfo.h;
                }
                canvas.width = w;
                canvas.height = h;
                let ctx = canvas.getContext("2d");
                ctx.translate(w/2,h/2);
                ctx.rotate(deg * Math.PI/180 );
                ctx.drawImage(this.img,0,0,imgInfo.w,imgInfo.h,-imgInfo.w/2,-imgInfo.h/2,imgInfo.w,imgInfo.h);
                return  canvas.toDataURL("image/png");
            },
            setImg(img){
                this.imgInfo = {
                    w:img.width,
                    h:img.height
                };
                this.imgCInfo = this.compress(img);
                let x =  (this.opt.width -  this.imgCInfo.w)/2;
                let y = (this.opt.height -  this.imgCInfo.h)/2;
                this.pos = {
                    x,y
                };
            },
            
            onmouseup(){
                this.mouse = null;
            },
            onmousedown(e){
                this.mouse = {
                    x:e.offsetX,
                    y:e.offsetY
                }
            },
            onmousemove(e){
                if(this.mouse){
                    this.pos.x += e.offsetX - this.mouse.x ;
                    this.pos.y += e.offsetY - this.mouse.y;
                    this.mouse = {
                        x:e.offsetX,
                        y:e.offsetY
                    };
                }
            },
            onmousewheel(event){
                event.preventDefault();
                const direction = (event.wheelDelta || -event.detail) > 0 ? 1 : 0;
                let addScale = this.opt.addScale || 0.06;
                let w,h,scale;
                if(direction){
                    // 放大
                    w = this.imgCInfo.w * (1 + addScale);
                }else{
                    // 缩小
                    w = this.imgCInfo.w * (1 - addScale);
                }
                if(w < 10){
                    w = 10;
                }
                scale = w/this.imgInfo.w;
                h = this.imgInfo.h * scale;
                let x  = this.pos.x -  (w - this.imgCInfo.w)/2;
                let y = this.pos.y -  (h - this.imgCInfo.h)/2;
                this.pos = {
                    x,y
                };
                this.imgCInfo = {
                    scale,
                    w,
                    h
                }
            },
            fill(){
                let {w,h,x,y} =  this.clipCinfo;
                let clipctx = this.ctx;
                let opt = this.opt;
                clipctx.clearRect(0, 0, opt.width, opt.height);
                clipctx.beginPath();
                clipctx.fillStyle = 'rgba(0,0,0,0.6)';
                clipctx.strokeStyle = "green";
                //遮罩层
                clipctx.globalCompositeOperation = "source-over";
                clipctx.fillRect(0, 0, opt.width, opt.height);
                //画框
                clipctx.globalCompositeOperation = 'destination-out';
                clipctx.fillStyle = 'rgba(0,0,0,1)';
                clipctx.fillRect(x, y, w, h);
                //描边
                clipctx.globalCompositeOperation = "source-over";
                clipctx.moveTo(x, y);
                clipctx.lineTo(x + w, y);
                clipctx.lineTo(x + w, y + h);
                clipctx.lineTo(x, y + h);
                clipctx.lineTo(x, y);
                clipctx.stroke();
                clipctx.closePath();
            }
        }
    }
    </script>
    <style lang="scss" scoped>
    .crop-image{
        border: 1px solid rgb(35, 184, 255);
        margin:  20px auto;
        position: relative;
        overflow: hidden;
        background: url(data:image/png;base64,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) center repeat;
        canvas{
             100%;
            height: 100%;
            position: relative;
            z-index: 2;
            cursor: move;
        }
        img{
            position: absolute;
            z-index: 1;
        }
    }
    </style>
    

      

    初始化:

    this.$refs.cropImage.init("图片路径",{80,height:120});  

    旋转:

    this.$refs.cropImage.rotate()

      

    导出等比例的图片:

    this.src = await this.$refs.cropImage.exportBase();
    

      

  • 相关阅读:
    java实现报数游戏
    java实现取字母组成串
    java实现取字母组成串
    java实现取字母组成串
    java实现取字母组成串
    java实现取字母组成串
    java实现填写算式
    java实现填写算式
    java实现填写算式
    java实现填写算式
  • 原文地址:https://www.cnblogs.com/muamaker/p/12063540.html
Copyright © 2011-2022 走看看