• 在触屏设备上面利用html5裁剪图片(转)


    前言

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

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

    技术点

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

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

    思路

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

    非html5无法完成这个过程。

    运行结果

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

    部分代码

      1 <!doctype html>
      2 <html>
      3 
      4 <head>
      5     <meta name="Author" content="flashlizi - www.riaidea.com">
      6     <meta name="Description" content="HTML5 experiment">
      7     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      8     <title>头像上传组件 - HTML5版</title>
      9     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
     10 
     11     <style>
     12         body
     13         {
     14             padding: 0;
     15             margin: 0;
     16             height: 100%;
     17             background-color: #eee;
     18             font-size: 12px;
     19             color: #666;
     20         }
     21 
     22         a
     23         {
     24             text-decoration: none;
     25             color: #333;
     26         }
     27 
     28         a:hover
     29         {
     30             text-decoration: none;
     31             color: #f00;
     32         }
     33 
     34 
     35     </style>
     36     <script>
     37 
     38         if(window.FileReader==undefined){
     39             alert("该手机不支持html5");
     40         }
     41 
     42     </script>
     43 
     44     <script type="text/javascript" src="/static/mobile/lib/zepto.min.js"></script>
     45 </head>
     46 
     47 <body >
     48 <h1>选择图片:<input type="file" id="browseFile" onchange=""><input type="button" id="saveimg" value="保存图片"/></h1>
     49 <div id="wrapper" style="border: 1px gray dotted; padding: 25px;">
     50 
     51     <div id="component_box" style="position: relative; border: 1px green solid;  300px; height: 300px;">
     52         <div id="tipBox" style="display: none;">
     53             <img src=""/>
     54         </div>
     55         <div id="mainCutter" style="overflow: hidden; display: none; position: relative;">
     56             <img id="imgPreview" />
     57             <div id="cutBox" style=" position:absolute;  150px; height: 200px; opacity: 0.5; background: gray;"></div>
     58         </div>
     59     </div>
     60 
     61     <!--画布-->
     62 
     63     <canvas id="cropper" style=" display:none;border:1px solid red;  300px; height: 300px;" ></canvas>
     64 </div>
     65 <div><span style="color: green;">调整裁剪区域大小:</span>
     66     <!--调整用slider-->
     67     <div><div id="processBar" style=" margin: 0 auto; position: relative;  220px; height: 20px; background: green;"><div id="processPoint" style="background: url(images/horizSlider.png);  18px; height: 20px; position: absolute;left: 0;top: 0;"></div></div></div>
     68 </div>
     69 <div id="the_show" style="display: none;">
     70     <h2>提示:</h2>
     71     <div id="theTips"></div>
     72 
     73     <h2>后台获得的图像</h2>
     74     <img src="" id="showImg"/>
     75 </div>
     76 <div style="color: green;">友情提醒:拖动裁剪框裁剪框将随之移动,上划放大裁剪框,下滑缩小裁剪框。</div>
     77 <div id="tips2" style="color: green; position: absolute;left: 0px; bottom: 0px; border: 1px solid green;"></div>
     78 
     79 
     80 <script type="text/javascript">
     81 //--逻辑,点击图片上传选择后将加载预览图片
     82 var Options={
     83     300,
     84     height:300,
     85     cutWidth:150,
     86     cutHeight:200,
     87     cutMinSize:50,//裁剪框最小尺寸,即最小可以缩放到这个size,width及height任意一个都无法小于这个值。
     88 
     89     //--系统自带,运行时自动运算,请不要修改。
     90     cropViewWidth:0,//在画布里面显示的最大宽度
     91     cropViewHeight:0,//在画布里面显示的最大高度
     92     cropLeft:0,
     93     cropTop:0,
     94     //--裁剪框
     95     cutViewWidth:0,   //当前宽度,
     96     cutViewHeight:0,//当前高度
     97     cutMaxWidth:0,   //裁剪框最大宽度。
     98     cutMaxHeight:0,//裁剪框最大高度。
     99     //--四象限。用于判断距离。
    100     cutBoxLimitX1:0,
    101     cutBoxLimitX2:0,
    102     cutBoxLimitY1:0,
    103     cutBoxLimitY2:0,
    104     cutLeft:0,//裁剪框绝对定位,左侧距离。
    105     cutTop:0,//裁剪框绝对定位,离顶部距离。
    106     initStatus:false//当前组件是否已经初始化了。
    107 };
    108 var Options_image={
    109     0,
    110     height:0,
    111     imgData:""
    112 }
    113 
    114     var input_browseFile = document.getElementById("browseFile");
    115     var img_preview = document.getElementById("imgPreview");
    116     var cutBox=document.getElementById("cutBox");
    117     var tipBox=document.getElementById("tipBox");
    118     var _cropper=document.getElementById("cropper");
    119     var mainCutter=document.getElementById("mainCutter");
    120     var tips2=$("#tips2");
    121     var wrapper=document.getElementById("wrapper");
    122     var component_box=document.getElementById("component_box");
    123 
    124     var ctx = _cropper.getContext('2d');//ctx.drawImage(myImage, 50, 50);
    125 function previewInImage (file) {
    126         //通过file.size可以取得图片大小
    127         var reader = new FileReader();
    128         LoadingImage();
    129 
    130         reader.onload = function( evt ){
    131             img_preview.src = evt.target.result;
    132         }
    133        Options_image.imgData= reader.readAsDataURL(file);
    134     }
    135 img_preview.onload=function(){
    136     Options_image.width=img_preview.width;
    137     Options_image.height=img_preview.height;
    138     _initCropAndCut();
    139 }
    140 function LoadingImage(){
    141     $(img_preview).css({"width":"","height":""});
    142 }
    143 function _initCropAndCut(){
    144     //--计算比例,将其放到canvas里面。
    145 
    146     var scale = Math.max(Options_image.width/Options.width,Options_image.height/Options.height);
    147     if(scale>1){
    148        Options.cropViewWidth=parseInt(Math.floor(Options_image.width/scale));
    149        Options.cropViewHeight=parseInt(Math.floor(Options_image.height/scale));
    150     }
    151     else{
    152        Options.cropViewWidth=Options_image.width;
    153        Options.cropViewHeight=Options_image.height;
    154     }
    155     //--计算画布里面的图像的位置。
    156     Options.cropLeft=parseInt((Options.width-Options.cropViewWidth)/2);
    157     Options.cropTop=parseInt((Options.height-Options.cropViewHeight)/2);
    158     //--计算裁剪框实际大小及实际位置。
    159     //计算裁剪框的位置。
    160 
    161     var scale_2=Math.max(Options.cutWidth/Options.cropViewWidth,Options.cutHeight/Options.cropViewHeight);
    162     if(scale_2>1){
    163         Options.cutViewWidth=parseInt(Math.floor(Options.cutWidth/scale_2));
    164         Options.cutViewHeight=parseInt(Math.floor(Options.cutHeight/scale_2));
    165     }
    166     else{
    167         Options.cutViewHeight=Options.cutHeight;
    168         Options.cutViewWidth=Options.cutWidth;
    169     }
    170     Options.cutMaxWidth=Options.cutViewWidth;
    171     Options.cutMaxHeight=Options.cutViewHeight;
    172 
    173     Options.cutLeft=parseInt(Math.floor((Options.cropViewWidth-Options.cutViewWidth))/2);
    174     Options.cutTop=parseInt(Math.floor((Options.cropViewHeight-Options.cutViewHeight))/2);
    175     //-四象限。
    176     Options.cutBoxLimitX1=0;
    177     Options.cutBoxLimitX2=Options.cropViewWidth;
    178     Options.cutBoxLimitY1=0;
    179     Options.cutBoxLimitY2=Options.cropViewHeight;
    180 
    181     $(cutBox).css({"display":"block","width":Options.cutViewWidth+"px","height":Options.cutViewHeight+"px","left":Options.cutLeft+"px","top":Options.cutTop+"px"});
    182     $(img_preview).css({"width":Options.cropViewWidth+"px","height":Options.cropViewHeight+"px"});
    183     $(mainCutter).css({"display":"block","width":Options.cropViewWidth+"px","height":Options.cropViewHeight+"px","left":Options.cropLeft+"px","top":Options.cropTop+"px"});
    184     //ctx.drawImage(img_preview,Options.cropLeft,Options.cropTop,Options.cropViewWidth,Options.cropViewHeight);
    185     //ctx.drawImage(img_preview, 0, 0, Options_image.width,Options_image.height, Options.cropLeft, Options.cropTop, Options.cropViewWidth, Options.cropViewHeight );
    186 
    187     Options.initStatus=true;
    188     Options_process.initStatus=true;
    189     Options_process.percent=100;
    190     Options_process.pointX=Options_process.barWidth;
    191     _resizeProcessBar();
    192 }
    193 
    194     input_browseFile.addEventListener("change", function () {
    195         //通过 this.files 取到 FileList ,这里只有一个
    196         previewInImage(this.files[0]);
    197 
    198     }, false);
    199     //--添加缩放功能。
    200     Options_zoom={
    201     beginX1:0,
    202     beginY1:0,
    203     beginX2:0,
    204     beginY2:0,
    205     endX1:0,
    206     endY1:0,
    207     endX2:0,
    208     endY2:0
    209     };
    210 //--添加裁剪框移动功能
    211 Options_move={
    212     beginX1:0,
    213     beginY1:0,
    214     endX1:0,
    215     endY1:0
    216 };
    217 
    218 /**
    219  * 拖动裁剪框的逻辑处理。
    220  * */
    221 cutBox.addEventListener("touchstart",function(event){
    222   event.preventDefault();
    223   event.stopPropagation();
    224     Options_move={
    225         beginX1:0,
    226         beginY1:0,
    227         endX1:0,
    228         endY1:0
    229     };
    230     var beginX=event.changedTouches[0].pageX;
    231     var beginY=event.changedTouches[0].pageY;
    232     Options_move.beginX1=beginX;
    233     Options_move.beginY1=beginY;
    234 
    235 },false);
    236 cutBox.addEventListener("touchmove",function(event){
    237     event.preventDefault();
    238     event.stopPropagation();
    239     //--
    240     var beginX=event.changedTouches[0].pageX;
    241     var beginY=event.changedTouches[0].pageY;
    242     Options_move.endX1=beginX;
    243     Options_move.endY1=beginY;
    244     //--计算是否发生位移,根据位移来定位裁剪框位置。
    245     //位移量。
    246     var _d_x=Options_move.endX1-Options_move.beginX1;
    247     var _d_y=Options_move.endY1-Options_move.beginY1;
    248     //--当前裁剪框原始位置。
    249     var _new_x=Options.cutLeft;
    250     var _new_y=Options.cutTop;
    251     _new_x+=_d_x;
    252     _new_y+=_d_y;
    253     //--判断是否在矩形边框,假如超出去,那么就取最终点。
    254     //--注意,判断相关点的范围。
    255 
    256     if(_new_x<Options.cutBoxLimitX1){
    257         _new_x=Options.cutBoxLimitX1;
    258     }
    259     else if(_new_x>Options.cutBoxLimitX2){
    260         _new_x=Options.cutBoxLimitX2;
    261     }
    262     //--顺便判断,加上宽度后,是否超过了范围。
    263     if((_new_x+Options.cutViewWidth)>Options.cutBoxLimitX2){
    264         _new_x=Options.cutBoxLimitX2-Options.cutViewWidth;
    265     }
    266     if(_new_y<Options.cutBoxLimitY1){
    267         _new_y=Options.cutBoxLimitY1;
    268     }
    269     else if(_new_y>Options.cutBoxLimitY2){
    270         _new_y=Options.cutBoxLimitY2;
    271     }
    272     //--顺便判断,加上裁剪框高度后,是否超过下限。
    273     if((_new_y+Options.cutViewHeight)>Options.cutBoxLimitY2){
    274         _new_y=Options.cutBoxLimitY2-Options.cutViewHeight;
    275     }
    276 
    277 
    278     Options.cutLeft=_new_x;
    279     Options.cutTop=_new_y;
    280     _resizeCutBox();
    281     //---将这一点的放回前一点。
    282     Options_move.beginX1=Options_move.endX1;
    283     Options_move.beginY1=Options_move.endY1;
    284 
    285 },false);
    286 cutBox.addEventListener("touchend",function(event){
    287     event.preventDefault();
    288     event.stopPropagation();
    289     return;
    290 
    291 },false);
    292  /**
    293  * 根据相关参数重新resize裁剪框
    294  * */
    295 function _resizeCutBox(){
    296     $(cutBox).css({"width":Options.cutViewWidth+"px","height":Options.cutViewHeight+"px","left":Options.cutLeft+"px","top":Options.cutTop+"px"});
    297 }
    298 function _getCutImageData(){
    299     var output = document.createElement("canvas");
    300     //--坐标换算。
    301     var scale_x=Options_image.width/Options.cropViewWidth;
    302     var scale_y=Options_image.height/Options.cropViewHeight;
    303     var _o_x=parseInt( (scale_x)*Options.cutLeft);
    304     var _o_y=parseInt( (scale_y)*Options.cutTop);
    305     //--长度换算
    306     var _o_width=parseInt(scale_x*Options.cutViewWidth);
    307     var _o_height=parseInt(scale_y*Options.cutViewHeight);
    308 
    309     output.width = Options.cutWidth;
    310     output.height = Options.cutHeight;
    311     output.getContext("2d").drawImage(img_preview, _o_x,_o_y, _o_width, _o_height, 0, 0, output.width, output.height);
    312     return output.toDataURL("image/jpeg");
    313 }
    314 function saveImage()
    315 {
    316     var imgData = _getCutImageData();
    317     /*
    318 
    319     $("#the_show").css("display","block");
    320 
    321     document.getElementById("showImg").src=imgData;
    322     return;
    323     */
    324     var xhr = new XMLHttpRequest();
    325     xhr.onreadystatechange = function(e)
    326     {
    327         if(xhr.readyState == 4)
    328         {
    329             if(xhr.status == 200)
    330             {
    331                 //--获取返回的数据。
    332                 var _res=xhr.responseText;
    333                 _res= $.trim(_res);
    334                 var json= $.parseJSON(_res);
    335                 if(json.status==true){
    336                     $("#the_show").css("display","block");
    337                     var surl=json.url+"?t="+Math.random();
    338                     $("#showImg").attr("src",surl);
    339                 }
    340                 else{
    341                     alert(json.message);
    342                 }
    343                 //document.getElementById("status").innerHTML = "<font color='#f00'>上传成功!</font>";
    344             }
    345             else{
    346                 alert("服务端无法响应,错误编号:"+xhr.status);
    347 
    348             }
    349         }
    350     };
    351 
    352     xhr.open("post", "/quickTest/html5CropperHandler.jsp", true);
    353     var data = new FormData();
    354     data.append("username", "flashlizi");
    355     data.append("size", 180);
    356     data.append("file", imgData);
    357     xhr.send(data);
    358 }
    359 /**
    360  * processBar 进度条相关操作。
    361  * */
    362 
    363 Options_process={
    364     beginX:0,//触摸时候起始点
    365     beginY:0,//触摸时候起始点
    366     endX:0,//触摸时候终点
    367     endY:0,//触摸时候终点
    368     barWidth:200,//进度条长度
    369     pointX:0,//当前指示点位置
    370     pointY:0,
    371     percent:0,//百分比值。
    372     initStatus:false
    373 };
    374 var processBar=document.getElementById("processBar");
    375 var processPoint=document.getElementById("processPoint");
    376 
    377 //--添加触屏事件,监控相关动作。
    378 //开始触摸
    379 processBar.addEventListener("touchstart",function(event){
    380     event.preventDefault();
    381     event.stopPropagation();
    382 
    383     if(!Options_process.initStatus){
    384         return;
    385     }
    386     var beginX=event.changedTouches[0].pageX;
    387     var beginY=event.changedTouches[0].pageY;
    388     Options_process.beginX=beginX;
    389     Options_process.beginY=beginY;
    390 },false) ;
    391 //--移动中
    392 processBar.addEventListener("touchmove",function(event){
    393     event.preventDefault();
    394     event.stopPropagation();
    395 
    396     if(!Options_process.initStatus){
    397         return;
    398     }
    399     var beginX=event.changedTouches[0].pageX;
    400     var beginY=event.changedTouches[0].pageY;
    401     Options_process.endX=beginX;
    402     Options_process.endY=beginY;
    403     //--计算比分比。
    404     var _d_x=Options_process.endX-Options_process.beginX;
    405     Options_process.percent+=parseInt(_d_x*100/Options_process.barWidth);
    406     if(Options_process.percent<0){
    407         Options_process.percent=0;
    408     }
    409     else if(Options_process.percent>100){
    410         Options_process.percent=100;
    411     }
    412     //--计算那个指示点位置。
    413     Options_process.pointX=parseInt(Options_process.barWidth*(Options_process.percent/100));
    414     _resizeProcessBar();
    415     //--根据百分比,设置裁剪框大小。
    416     var _o_cut_x=Options.cutLeft;
    417     var _o_cut_y=Options.cutTop;
    418     var _o_cut_width=Options.cutViewWidth;
    419     var _new_cut_width= parseInt(Options.cutMaxWidth*(Options_process.percent/100));
    420     var _new_cut_height= parseInt(Options.cutMaxHeight*(Options_process.percent/100));
    421     if(_new_cut_width>_o_cut_width){
    422         //--扩大了。
    423         //--计算当前坐标
    424         var _d_x_2=_new_cut_width-Options.cutViewWidth;
    425         var _d_y_2=_new_cut_height-Options.cutViewHeight;
    426 
    427         Options.cutLeft=Options.cutLeft-parseInt(_d_x_2/2);
    428         Options.cutTop=Options.cutTop-parseInt(_d_y_2/2);
    429         Options.cutViewWidth=_new_cut_width;
    430         Options.cutViewHeight=_new_cut_height;
    431         _resizeCutBox();
    432 
    433     }
    434     else if(_new_cut_width<_o_cut_width){
    435         //--缩小了。
    436         var _d_x_2=Options.cutViewWidth-_new_cut_width;
    437         var _d_y_2=Options.cutViewHeight-_new_cut_height;
    438         Options.cutLeft=Options.cutLeft+parseInt(_d_x_2/2);
    439         Options.cutTop=Options.cutTop+parseInt(_d_y_2/2);
    440         Options.cutViewWidth=_new_cut_width;
    441         Options.cutViewHeight=_new_cut_height;
    442         _resizeCutBox();
    443 
    444     }
    445 
    446     //--后续处理。
    447     Options_process.beginX=Options_process.endX;
    448     Options_process.endY=Options_process.endY;
    449 
    450 },false) ;
    451 //--结束
    452 processBar.addEventListener("touchend",function(event){
    453     event.preventDefault();
    454     event.stopPropagation();
    455 
    456     if(!Options_process.initStatus){
    457         return;
    458     }
    459 },false) ;
    460 /**
    461  * 根据相关属性,重设slider位置。
    462  * */
    463 function _resizeProcessBar(){
    464     $(processPoint).css("left",Options_process.pointX+"px");
    465 }
    466 
    467 
    468  $("#saveimg").click(function(){
    469     if(Options.initStatus==false){
    470         alert("请先选择图片!");
    471         return;
    472     }
    473     saveImage();
    474 });
    475 
    476 </script>
    477 
    478 </body>
    479 </html>
  • 相关阅读:
    批处理 星号的替换
    1.1.1 Windows系统内置工具——ipconfig
    2.1 以太网回顾
    书面实验1.3 识别冲突域和广播域
    书面实验1.1:OSI问题
    1.3 OSI模型
    1.2 网络互联模型
    1.1
    如何开启系统服务
    如何查看或启用打开windows功能
  • 原文地址:https://www.cnblogs.com/xingmeng/p/4346381.html
Copyright © 2020-2023  润新知