使用pinchzoom实现头像剪裁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用pinchzoom实现头像剪裁相关的知识,希望对你有一定的参考价值。

pinchzoom 官网 http://manuelstofer.github.io/pinchzoom/


PortraitClip.css

.pinch-page{
    position:fixed;
    top:0;
    bottom:0;
    left:0;
    right:0;
    display:none;
    width:100%;
    height:100%;
    background-color:#333;
    z-index:9;

}
.pinch-page .top-mask,
.pinch-page .bottom-mask{
    position:absolute;
    left:0;
    top:0;
    right:0;
    display:block;
    background-color:rgba(0,0,0,0.6);
    z-index: 99;
}

.pinch-page .bottom-mask{
    top:auto;
    bottom:0;
}

.pinch-page .pinch-zoom-container{
    position:absolute!important;
    left:0;
    top:0;
    bottom:0;
    right:0;
    margin:auto;
    overflow: visible!important;
    z-index: 9;
    box-sizing: border-box;
}

.pinch-page .pinch-zoom-container:before{
    content:"";
    position:absolute;
    top:0;
    bottom:0;
    left:0;
    right:0;
    display:block;
    width:100%;
    height:100%;
    z-index:999;
    border:1px solid #fff;
    box-sizing: border-box;
}

.pinch-page .pinch-zoom{
}

.pinch-page .pinch-zoom-image{
    display:block;
    width:100%;
    height:320px;
    background: no-repeat center center;
    background-size:contain;
}

.pinch-page .operation-bar{
    position:absolute;
    left:0;
    right:0;
    bottom:0;
    display:block;
    height:60px;
    z-index: 999;
    background-color:rgba(51,51,51,0.6);
}
.pinch-page .operation-bar .cancel-btn{
    position:absolute;
    top:0;
    bottom:0;
    left:0;
    display:block;
    height:60px;
    line-height: 60px;
    font-size:16px;
    color:#fff;
    padding:0 20px;
}
.pinch-page .operation-bar .save-btn{
    position:absolute;
    top:0;
    bottom:0;
    right:0;
    display:block;
    height:60px;
    line-height: 60px;
    font-size:16px;
    color:#fff;
    padding:0 20px;
}

PortraitClip.js

(function($){
    /* ======================================== 页面渲染 ======================================== */
    /* ======================================== 不可变字段 ======================================== */
    var initialized = false; // 是否初始化
    var container = null;
    var clientWidth = $(window).width();
    var clientHeight = $(window).height();
    var pinchZoomImageWidth = clientWidth;
    var pinchZoomImageHeight = clientWidth;
    var maskHeight = (clientHeight-clientWidth)/2;
    var pinchZoom = null; // pinchZoom 对象

    /* ======================================== 可变字段 ======================================== */
    var imageWidth = 0;
    var imageHeight = 0;
    var xOffset = 0;
    var yOffset = 0;
    var originScale = 0;
    var image = null;
    var callback = null; // 加载完之后的回调



    // 构建DOM
    function render(){
        var html =  "<div class=‘pinch-page‘>                                                           "+
                    "  <em class=‘top-mask‘></em>                                                      "+
                    "  <div class=‘pinch-zoom‘>                                                        "+
                    "     <div class=‘pinch-zoom-image‘></div>                                        "+
                    "  </div>                                                                          "+
                    "  <em class=‘bottom-mask‘></em>                                                   "+
                    "  <div class=‘operation-bar‘>                                                     "+
                    "     <a class=‘cancel-btn‘>取消</a>                                              "+
                    "     <a class=‘save-btn‘>选取</a>                                                "+
                    "  </div>                                                                          "+
                    "</div>                                                                             ";
        $(html).appendTo("body");

        // 初始化 pinchZoom
        pinchZoom = new RTP.PinchZoom($(".pinch-zoom"));

        // 添加遮罩
        $(".pinch-page .top-mask").css("height",maskHeight+"px");
        $(".pinch-page .bottom-mask").css("height",maskHeight+"px");
        $(".pinch-zoom-container,.pinch-zoom-image").css({
            "width":pinchZoomImageWidth+"px",
            "height":pinchZoomImageHeight+"px"
        });

        container = $(".pinch-page");
    }

    // 绑定事件
    function bindPageEvents(){
        container.on("touchend",".save-btn",function(e){
            e.stopPropagation();
            e.preventDefault();

            var transformCss = $(".pinch-zoom").css("-webkit-transform");
            var positionInfo = retrievePositionInfo(transformCss);
            clipImage(positionInfo.scale,positionInfo.x,positionInfo.y,callback);

            $(container).hide();
        });


        container.on("touchend",".cancel-btn",function(e){
            e.stopPropagation();
            e.preventDefault();
            $(container).hide();
        });

        container.on("touchstart touchmove touchend",function(e){
            e.stopPropagation();
            e.preventDefault();
        });
    }

    // 初始化方法
    function init(){
        if(initialized){
            return;
        }
        initialized = true;
        render();
        bindPageEvents();
    }

    /* ======================================== common-method ======================================== */
    // 刷新 头像剪裁容器
    function loadImage(originImage,loadCallback){
        if(!initialized){init();} // 如果未初始化则初始化

        callback = loadCallback;
        $(container).find(".pinch-zoom-image").css("background-image","url("+originImage+")");

        image = new Image();
        image.onload = function(){
            imageWidth = image.width;
            imageHeight = image.height;
            loadImageCallback();

            $(container).show();
        };
        image.src=originImage;
    }



    // 图像加载回调
    function loadImageCallback(){

        if(imageWidth>imageHeight){
            yOffset = (imageWidth-imageHeight)*pinchZoomImageWidth/imageWidth/2;
            originScale = pinchZoomImageWidth/imageWidth;
        }else{
            xOffset = (imageHeight-imageWidth)*pinchZoomImageWidth/imageHeight/2;
            originScale = pinchZoomImageHeight/imageHeight;
        }

        var minZoom = 1;
        if(xOffset!=0){
            minZoom = clientWidth/(clientWidth-2*xOffset);
        }
        if(yOffset!=0){
            minZoom = clientWidth/(clientWidth-2*yOffset);
        }

        // 初始化 pinchZoom 对戏那个
        pinchZoom.options.minZoom = minZoom;
        pinchZoom.options.tapZoomFactor = minZoom;
        pinchZoom.options.xOffset = xOffset;
        pinchZoom.options.yOffset = yOffset;



        var event = {touches:[{pageX:pinchZoomImageWidth/2,pageY:clientHeight/2}]};
        setTimeout(function(){pinchZoom.handleDoubleTap(event);},300);

    }

    // 剪裁图像
    function clipImage(scale,x,y,callback){
        // 创建 canvas
        var canvasId ="canvas_"+new Date().getTime()+""+parseInt(Math.random()*10000);
        $("<canvas></canvas>").hide().attr("id",canvasId).appendTo("body");

        var canvas=$("#"+canvasId)[0];
        var ctx=canvas.getContext("2d");

        // 释放canvas
        function releaseCanvas(){
            $("#"+canvasId).remove();
        }
        // canvas 宽度 高度
        canvas.width = pinchZoomImageWidth;
        canvas.height= pinchZoomImageHeight;

        if(image.width>image.height){
            y=y-yOffset;
        }else{
            x=x-xOffset;
        }
        ctx.drawImage(image,x/originScale,y/originScale,pinchZoomImageWidth/scale/originScale,pinchZoomImageHeight/scale/originScale,0,0,pinchZoomImageWidth,pinchZoomImageHeight);

        var dataURL = canvas.toDataURL();
        if(callback!=null){
            callback(dataURL);
        }

        releaseCanvas();
    }

    // 获取 pinch-zoom 缩放 偏移信息
    function retrievePositionInfo(transformCss){
        var infoStr = transformCss.replace("matrix(","");
        infoStr = infoStr.replace(")","");
        var arr = infoStr.split(",");
        var scale = arr[0];
        var x = arr[4];
        var y = arr[5];
        if(x<0){
            x = -1*x;
        }
        if(y<0){
            y=-1*y;
        }

        var result = {scale:scale,x:x/scale,y:y/scale};
        return result;
    }



    var PortraitClipUtil = {};

    PortraitClipUtil.loadImage=loadImage;

    window.PortraitClipUtil = PortraitClipUtil;
})(jQuery);


测试页面

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Pinchzoom.js Demo</title>

    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes" />


    <link rel="stylesheet" href="css/common/PortraitClip.css" />
    <style>
        body{margin:0;}
    </style>
</head>
<body style="height:900px;">

    <button id="upload-image">上传图片</button>

    <image id="image" style="position: absolute;display:block;top:0;z-index:9999;"/>


    <script type="text/javascript" src="dist/js/jquery-1.11.3.min.js"></script>
    <script type="text/javascript" src="dist/js/pinchzoom-xiaomin.min.js"></script>
    <script type="text/javascript" src="js/common/PortraitClip.js"></script>
    <script>
        $("#upload-image").click(function(e){
            PortraitClipUtil.loadImage("frog.jpg",function(dataURL){
                $("#image").attr("src",dataURL);
            });
        });
    </script>
</body>
</html>


技术分享


注:这边引入的是pinchzoom-xiaomin.min.js。对pinchzoom.min.js源代码有所变更。pinchzoom.js,pinchzoom.min.js,pinchzoom-xiaomin.js,pinchzoom-xiaomin.min.js在相应的附件中。


以上是关于使用pinchzoom实现头像剪裁的主要内容,如果未能解决你的问题,请参考以下文章

cropper.js头像剪裁

cropper+pillow处理上传图片剪裁

java web 网站头像上传处理 (springmvc +bootstrap+cropper)

java web 网站头像上传处理 (springmvc +bootstrap+cropper)

java web 网站头像上传处理 (springmvc +bootstrap+cropper)

Android7.0调用系统相机拍照读取系统相册照片+CropImageView剪裁照片