在触屏设备上面利用html5裁剪图片

推荐:

这一篇文章是早年为了解决图片裁剪的探索性文章,现在已经开放出了falsh版及html5版本的图片裁剪插件,各位有时间可以看看:

浮士德html5图片裁剪器2016开源版

浮士德头像裁剪flash版2016福利版

上面两个解决方案已经经过几个项目,可以应用于低级浏览器及现代浏览器,ipad,移动设备等。


前言

现在触屏设备越来越流行,而且大多数已经支持html5了。针对此,对触屏设备开发图片裁剪功能,

让其可以直接处理图片,减轻服务端压力。

技术点

浏览器必须支持html5,包括fileReader,canvas等api,并且该设备至少支持单点触事件(touchstart,touchmove,touchend),可惜的是

很多浏览器只能识别一只手指(不支持多点触摸事件,假如支持的话,请告知我)。

思路

利用filereader直接读取本地图片,然后赋予一个图片,该图片及裁剪框的位置计算跟pc端一样,但是触发的事件不一样,触屏版是根据触屏事件触发的。裁剪时,利用cavas的api直接画出相关图像,然后得到数据,再利用xmlhttprequest发送请求。

非html5无法完成这个过程。

运行结果

这只是一个demo,也是最初的雏形,当然不会太好看了,但是基本实现功能即可。



部分代码


<!doctype html>
<html>

<head>
    <meta name="Author" content="flashlizi - www.riaidea.com">
    <meta name="Description" content="HTML5 experiment">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>头像上传组件 - HTML5版</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        body
        {
            padding: 0;
            margin: 0;
            height: 100%;
            background-color: #eee;
            font-size: 12px;
            color: #666;
        }

        a
        {
            text-decoration: none;
            color: #333;
        }

        a:hover
        {
            text-decoration: none;
            color: #f00;
        }


    </style>
    <script>

        if(window.FileReader==undefined){
            alert("该手机不支持html5");
        }

    </script>

    <script type="text/javascript" src="/static/mobile/lib/zepto.min.js"></script>
</head>

<body >
<h1>选择图片:<input type="file" id="browseFile" οnchange=""><input type="button" id="saveimg" value="保存图片"/></h1>
<div id="wrapper" style="border: 1px gray dotted; padding: 25px;">

    <div id="component_box" style="position: relative; border: 1px green solid; width: 300px; height: 300px;">
        <div id="tipBox" style="display: none;">
            <img src=""/>
        </div>
        <div id="mainCutter" style="overflow: hidden; display: none; position: relative;">
            <img id="imgPreview" />
            <div id="cutBox" style=" position:absolute; width: 150px; height: 200px; opacity: 0.5; background: gray;"></div>
        </div>
    </div>

    <!--画布-->

    <canvas id="cropper" style=" display:none;border:1px solid red; width: 300px; height: 300px;" ></canvas>
</div>
<div><span style="color: green;">调整裁剪区域大小:</span>
    <!--调整用slider-->
    <div><div id="processBar" style=" margin: 0 auto; position: relative; width: 220px; height: 20px; background: green;"><div id="processPoint" style="background: url(images/horizSlider.png); width: 18px; height: 20px; position: absolute;left: 0;top: 0;"></div></div></div>
</div>
<div id="the_show" style="display: none;">
    <h2>提示:</h2>
    <div id="theTips"></div>

    <h2>后台获得的图像</h2>
    <img src="" id="showImg"/>
</div>
<div style="color: green;">友情提醒:拖动裁剪框裁剪框将随之移动,上划放大裁剪框,下滑缩小裁剪框。</div>
<div id="tips2" style="color: green; position: absolute;left: 0px; bottom: 0px; border: 1px solid green;"></div>


<script type="text/javascript">
//--逻辑,点击图片上传选择后将加载预览图片
var Options={
    width:300,
    height:300,
    cutWidth:150,
    cutHeight:200,
    cutMinSize:50,//裁剪框最小尺寸,即最小可以缩放到这个size,width及height任意一个都无法小于这个值。

    //--系统自带,运行时自动运算,请不要修改。
    cropViewWidth:0,//在画布里面显示的最大宽度
    cropViewHeight:0,//在画布里面显示的最大高度
    cropLeft:0,
    cropTop:0,
    //--裁剪框
    cutViewWidth:0,   //当前宽度,
    cutViewHeight:0,//当前高度
    cutMaxWidth:0,   //裁剪框最大宽度。
    cutMaxHeight:0,//裁剪框最大高度。
    //--四象限。用于判断距离。
    cutBoxLimitX1:0,
    cutBoxLimitX2:0,
    cutBoxLimitY1:0,
    cutBoxLimitY2:0,
    cutLeft:0,//裁剪框绝对定位,左侧距离。
    cutTop:0,//裁剪框绝对定位,离顶部距离。
    initStatus:false//当前组件是否已经初始化了。
};
var Options_image={
    width:0,
    height:0,
    imgData:""
}

    var input_browseFile = document.getElementById("browseFile");
    var img_preview = document.getElementById("imgPreview");
    var cutBox=document.getElementById("cutBox");
    var tipBox=document.getElementById("tipBox");
    var _cropper=document.getElementById("cropper");
    var mainCutter=document.getElementById("mainCutter");
    var tips2=$("#tips2");
    var wrapper=document.getElementById("wrapper");
    var component_box=document.getElementById("component_box");

    var ctx = _cropper.getContext('2d');//ctx.drawImage(myImage, 50, 50);
function previewInImage (file) {
        //通过file.size可以取得图片大小
        var reader = new FileReader();
        LoadingImage();

        reader.onload = function( evt ){
            img_preview.src = evt.target.result;
        }
       Options_image.imgData= reader.readAsDataURL(file);
    }
img_preview.οnlοad=function(){
    Options_image.width=img_preview.width;
    Options_image.height=img_preview.height;
    _initCropAndCut();
}
function LoadingImage(){
    $(img_preview).css({"width":"","height":""});
}
function _initCropAndCut(){
    //--计算比例,将其放到canvas里面。

    var scale = Math.max(Options_image.width/Options.width,Options_image.height/Options.height);
    if(scale>1){
       Options.cropViewWidth=parseInt(Math.floor(Options_image.width/scale));
       Options.cropViewHeight=parseInt(Math.floor(Options_image.height/scale));
    }
    else{
       Options.cropViewWidth=Options_image.width;
       Options.cropViewHeight=Options_image.height;
    }
    //--计算画布里面的图像的位置。
    Options.cropLeft=parseInt((Options.width-Options.cropViewWidth)/2);
    Options.cropTop=parseInt((Options.height-Options.cropViewHeight)/2);
    //--计算裁剪框实际大小及实际位置。
    //计算裁剪框的位置。

    var scale_2=Math.max(Options.cutWidth/Options.cropViewWidth,Options.cutHeight/Options.cropViewHeight);
    if(scale_2>1){
        Options.cutViewWidth=parseInt(Math.floor(Options.cutWidth/scale_2));
        Options.cutViewHeight=parseInt(Math.floor(Options.cutHeight/scale_2));
    }
    else{
        Options.cutViewHeight=Options.cutHeight;
        Options.cutViewWidth=Options.cutWidth;
    }
    Options.cutMaxWidth=Options.cutViewWidth;
    Options.cutMaxHeight=Options.cutViewHeight;

    Options.cutLeft=parseInt(Math.floor((Options.cropViewWidth-Options.cutViewWidth))/2);
    Options.cutTop=parseInt(Math.floor((Options.cropViewHeight-Options.cutViewHeight))/2);
    //-四象限。
    Options.cutBoxLimitX1=0;
    Options.cutBoxLimitX2=Options.cropViewWidth;
    Options.cutBoxLimitY1=0;
    Options.cutBoxLimitY2=Options.cropViewHeight;

    $(cutBox).css({"display":"block","width":Options.cutViewWidth+"px","height":Options.cutViewHeight+"px","left":Options.cutLeft+"px","top":Options.cutTop+"px"});
    $(img_preview).css({"width":Options.cropViewWidth+"px","height":Options.cropViewHeight+"px"});
    $(mainCutter).css({"display":"block","width":Options.cropViewWidth+"px","height":Options.cropViewHeight+"px","left":Options.cropLeft+"px","top":Options.cropTop+"px"});
    //ctx.drawImage(img_preview,Options.cropLeft,Options.cropTop,Options.cropViewWidth,Options.cropViewHeight);
    //ctx.drawImage(img_preview, 0, 0, Options_image.width,Options_image.height, Options.cropLeft, Options.cropTop, Options.cropViewWidth, Options.cropViewHeight );

    Options.initStatus=true;
    Options_process.initStatus=true;
    Options_process.percent=100;
    Options_process.pointX=Options_process.barWidth;
    _resizeProcessBar();
}

    input_browseFile.addEventListener("change", function () {
        //通过 this.files 取到 FileList ,这里只有一个
        previewInImage(this.files[0]);

    }, false);
    //--添加缩放功能。
    Options_zoom={
    beginX1:0,
    beginY1:0,
    beginX2:0,
    beginY2:0,
    endX1:0,
    endY1:0,
    endX2:0,
    endY2:0
    };
//--添加裁剪框移动功能
Options_move={
    beginX1:0,
    beginY1:0,
    endX1:0,
    endY1:0
};

/**
 * 拖动裁剪框的逻辑处理。
 * */
cutBox.addEventListener("touchstart",function(event){
  event.preventDefault();
  event.stopPropagation();
    Options_move={
        beginX1:0,
        beginY1:0,
        endX1:0,
        endY1:0
    };
    var beginX=event.changedTouches[0].pageX;
    var beginY=event.changedTouches[0].pageY;
    Options_move.beginX1=beginX;
    Options_move.beginY1=beginY;

},false);
cutBox.addEventListener("touchmove",function(event){
    event.preventDefault();
    event.stopPropagation();
    //--
    var beginX=event.changedTouches[0].pageX;
    var beginY=event.changedTouches[0].pageY;
    Options_move.endX1=beginX;
    Options_move.endY1=beginY;
    //--计算是否发生位移,根据位移来定位裁剪框位置。
    //位移量。
    var _d_x=Options_move.endX1-Options_move.beginX1;
    var _d_y=Options_move.endY1-Options_move.beginY1;
    //--当前裁剪框原始位置。
    var _new_x=Options.cutLeft;
    var _new_y=Options.cutTop;
    _new_x+=_d_x;
    _new_y+=_d_y;
    //--判断是否在矩形边框,假如超出去,那么就取最终点。
    //--注意,判断相关点的范围。

    if(_new_x<Options.cutBoxLimitX1){
        _new_x=Options.cutBoxLimitX1;
    }
    else if(_new_x>Options.cutBoxLimitX2){
        _new_x=Options.cutBoxLimitX2;
    }
    //--顺便判断,加上宽度后,是否超过了范围。
    if((_new_x+Options.cutViewWidth)>Options.cutBoxLimitX2){
        _new_x=Options.cutBoxLimitX2-Options.cutViewWidth;
    }
    if(_new_y<Options.cutBoxLimitY1){
        _new_y=Options.cutBoxLimitY1;
    }
    else if(_new_y>Options.cutBoxLimitY2){
        _new_y=Options.cutBoxLimitY2;
    }
    //--顺便判断,加上裁剪框高度后,是否超过下限。
    if((_new_y+Options.cutViewHeight)>Options.cutBoxLimitY2){
        _new_y=Options.cutBoxLimitY2-Options.cutViewHeight;
    }


    Options.cutLeft=_new_x;
    Options.cutTop=_new_y;
    _resizeCutBox();
    //---将这一点的放回前一点。
    Options_move.beginX1=Options_move.endX1;
    Options_move.beginY1=Options_move.endY1;

},false);
cutBox.addEventListener("touchend",function(event){
    event.preventDefault();
    event.stopPropagation();
    return;

},false);
 /**
 * 根据相关参数重新resize裁剪框
 * */
function _resizeCutBox(){
    $(cutBox).css({"width":Options.cutViewWidth+"px","height":Options.cutViewHeight+"px","left":Options.cutLeft+"px","top":Options.cutTop+"px"});
}
function _getCutImageData(){
    var output = document.createElement("canvas");
    //--坐标换算。
    var scale_x=Options_image.width/Options.cropViewWidth;
    var scale_y=Options_image.height/Options.cropViewHeight;
    var _o_x=parseInt( (scale_x)*Options.cutLeft);
    var _o_y=parseInt( (scale_y)*Options.cutTop);
    //--长度换算
    var _o_width=parseInt(scale_x*Options.cutViewWidth);
    var _o_height=parseInt(scale_y*Options.cutViewHeight);

    output.width = Options.cutWidth;
    output.height = Options.cutHeight;
    output.getContext("2d").drawImage(img_preview, _o_x,_o_y, _o_width, _o_height, 0, 0, output.width, output.height);
    return output.toDataURL("image/jpeg");
}
function saveImage()
{
    var imgData = _getCutImageData();
    /*

    $("#the_show").css("display","block");

    document.getElementById("showImg").src=imgData;
    return;
    */
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(e)
    {
        if(xhr.readyState == 4)
        {
            if(xhr.status == 200)
            {
                //--获取返回的数据。
                var _res=xhr.responseText;
                _res= $.trim(_res);
                var json= $.parseJSON(_res);
                if(json.status==true){
                    $("#the_show").css("display","block");
                    var surl=json.url+"?t="+Math.random();
                    $("#showImg").attr("src",surl);
                }
                else{
                    alert(json.message);
                }
                //document.getElementById("status").innerHTML = "<font color='#f00'>上传成功!</font>";
            }
            else{
                alert("服务端无法响应,错误编号:"+xhr.status);

            }
        }
    };

    xhr.open("post", "/quickTest/html5CropperHandler.jsp", true);
    var data = new FormData();
    data.append("username", "flashlizi");
    data.append("size", 180);
    data.append("file", imgData);
    xhr.send(data);
}
/**
 * processBar 进度条相关操作。
 * */

Options_process={
    beginX:0,//触摸时候起始点
    beginY:0,//触摸时候起始点
    endX:0,//触摸时候终点
    endY:0,//触摸时候终点
    barWidth:200,//进度条长度
    pointX:0,//当前指示点位置
    pointY:0,
    percent:0,//百分比值。
    initStatus:false
};
var processBar=document.getElementById("processBar");
var processPoint=document.getElementById("processPoint");

//--添加触屏事件,监控相关动作。
//开始触摸
processBar.addEventListener("touchstart",function(event){
    event.preventDefault();
    event.stopPropagation();

    if(!Options_process.initStatus){
        return;
    }
    var beginX=event.changedTouches[0].pageX;
    var beginY=event.changedTouches[0].pageY;
    Options_process.beginX=beginX;
    Options_process.beginY=beginY;
},false) ;
//--移动中
processBar.addEventListener("touchmove",function(event){
    event.preventDefault();
    event.stopPropagation();

    if(!Options_process.initStatus){
        return;
    }
    var beginX=event.changedTouches[0].pageX;
    var beginY=event.changedTouches[0].pageY;
    Options_process.endX=beginX;
    Options_process.endY=beginY;
    //--计算比分比。
    var _d_x=Options_process.endX-Options_process.beginX;
    Options_process.percent+=parseInt(_d_x*100/Options_process.barWidth);
    if(Options_process.percent<0){
        Options_process.percent=0;
    }
    else if(Options_process.percent>100){
        Options_process.percent=100;
    }
    //--计算那个指示点位置。
    Options_process.pointX=parseInt(Options_process.barWidth*(Options_process.percent/100));
    _resizeProcessBar();
    //--根据百分比,设置裁剪框大小。
    var _o_cut_x=Options.cutLeft;
    var _o_cut_y=Options.cutTop;
    var _o_cut_width=Options.cutViewWidth;
    var _new_cut_width= parseInt(Options.cutMaxWidth*(Options_process.percent/100));
    var _new_cut_height= parseInt(Options.cutMaxHeight*(Options_process.percent/100));
    if(_new_cut_width>_o_cut_width){
        //--扩大了。
        //--计算当前坐标
        var _d_x_2=_new_cut_width-Options.cutViewWidth;
        var _d_y_2=_new_cut_height-Options.cutViewHeight;

        Options.cutLeft=Options.cutLeft-parseInt(_d_x_2/2);
        Options.cutTop=Options.cutTop-parseInt(_d_y_2/2);
        Options.cutViewWidth=_new_cut_width;
        Options.cutViewHeight=_new_cut_height;
        _resizeCutBox();

    }
    else if(_new_cut_width<_o_cut_width){
        //--缩小了。
        var _d_x_2=Options.cutViewWidth-_new_cut_width;
        var _d_y_2=Options.cutViewHeight-_new_cut_height;
        Options.cutLeft=Options.cutLeft+parseInt(_d_x_2/2);
        Options.cutTop=Options.cutTop+parseInt(_d_y_2/2);
        Options.cutViewWidth=_new_cut_width;
        Options.cutViewHeight=_new_cut_height;
        _resizeCutBox();

    }

    //--后续处理。
    Options_process.beginX=Options_process.endX;
    Options_process.endY=Options_process.endY;

},false) ;
//--结束
processBar.addEventListener("touchend",function(event){
    event.preventDefault();
    event.stopPropagation();

    if(!Options_process.initStatus){
        return;
    }
},false) ;
/**
 * 根据相关属性,重设slider位置。
 * */
function _resizeProcessBar(){
    $(processPoint).css("left",Options_process.pointX+"px");
}


 $("#saveimg").click(function(){
    if(Options.initStatus==false){
        alert("请先选择图片!");
        return;
    }
    saveImage();
});

</script>

</body>
</html>




  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值