前言:
手机越来越普遍,利用手机即时拍照裁剪图片的功能需求是存在的。不才目前供职于一家网站,里面也有触屏版需要裁剪图片。
技术难点:
一般难点:
html5对图片的读取处理。
意外难点:
难点1、ios6,7下面对超过2M的图片有一个限制,该限制使得假如你直接将2m的本地图片或者相片绘制到canvas画布上面会出现压扁现象。
难点2、ios拍照会有额外的exif信息(图片信息),该信息包含了拍摄日期,作者,还有一个很关键的orientation(角度),ios拍摄有四个角度,分别为1,3,6,8,里面的代表含义记不清楚,不过就是表示,正拍,左旋90度,右旋90度,旋转180度。由于ios有这个orientation,所以假如用户使用ios的其他角度拍摄,直接绘制到画布上面会出现图片颠倒,并非用户希望的情形。这时候你就要根据角度进行调整了。
三、部分代码,为何这是部分代码?因为涉及到前端后端及样式,我没法提供完整代码,而且,涉及到公司机密,我只能提供一些我在解决bug途中所写的测试代码。
譬如,将图片颠倒缩小在放大,用于用户拖动裁剪框以后,js获得用户拖动的坐标,高度宽度,再根据相片的orientation逆推原本的坐标及正确的高度宽度,然后裁剪出那一部分图像,再根据orientation调整角度,必要时需要颠倒,缩放图像。
<html> <head> <title>画布的旋转、缩放等功能。</title> <script type="text/javascript" src="/static/mobile/lib/zepto.min.js"></script> </head> <body> <h1>选择:<a href="javascript:changeImg(6);">右旋90度图片</a> <a href="javascript:changeImg(3);">旋转180度图片</a> <a href="javascript:changeImg(8);">左旋90度图片</a> <a href="javascript:changeImg(1);">正常图片</a></h1> <img id="sourceImg" src="images/exif_info_6.jpg" style="max-width: 400px;max-height: 400px;"/> <fieldset> <legend>设置需要裁剪的x,y,及裁剪高度及宽度。</legend> <div> <table> <tr><td>x:</td><td><input type="text" id="txt_x" value="600"/></td></tr> <tr><td>y:</td><td><input type="text" id="txt_y" value="800"/></td></tr> <tr><td>width:</td><td><input type="text" id="txt_width" value="1480"/></td></tr> <tr><td>height:</td><td><input type="text" id="txt_height" value="850"/></td></tr> <tr><td>Orientation:</td><td><input type="text" id="txt_Orientation" value="6"/></td></tr> <tr><td>画布缩小倍数:</td><td><input type="text" id="txt_scale" value="5"/></td></tr> <tr><td> </td><td><input type="button" value="裁剪到临时画布" id="btn_saveToTmp"></td></tr> </table> </div> <div>临时画布:</div> <div style="border: 1px green solid;"> <canvas id="canvas_tmp"></canvas> </div> <div>最终画布</div> <div style="border: 1px red solid;"> <canvas id="canvas_final"></canvas> <div id="results"></div> </div> </fieldset> </body> </html> <script type="text/javascript"> function changeImg(_Orientation){ switch (_Orientation){ case 8: setX(750);setY(220);setW(1800);setH(1200);setScale(5); setOrientation(8); _sourceImg.src="images/exif_info_8.jpg"; break; case 6: setX(600);setY(800);setW(1480);setH(850);setScale(5); setOrientation(6); _sourceImg.src="images/exif_info_6.jpg"; break; case 3: setX(600);setY(450);setW(2300);setH(1450);setScale(5);setOrientation(3); _sourceImg.src="images/exif_info_3.jpg"; break; case 1: setX(600);setY(450);setW(2300);setH(1450);setScale(5);setOrientation(1); _sourceImg.src="images/exif_info_1.jpg"; break; } } var _sourceImg=document.getElementById("sourceImg"); var tmpCanvas=document.getElementById("canvas_tmp"); var tmpContext=tmpCanvas.getContext("2d"); var finalCanvas=document.getElementById("canvas_final"); var finalContext=finalCanvas.getContext("2d"); function getX(){ return parseInt($("#txt_x").val()); } function getY(){ return parseInt($("#txt_y").val()); } function getW(){ return parseInt($("#txt_width").val()); } function getH(){ return parseInt($("#txt_height").val()); } function getScale(){ return parseFloat($("#txt_scale").val()); } function getOrientatin(){ return parseFloat($("#txt_Orientation").val()); } function setX(x){ $("#txt_x").val(x); } function setY(x){ $("#txt_y").val(x); } function setW(x){ $("#txt_width").val(x); } function setH(x){ $("#txt_height").val(x); } function setScale(x){ $("#txt_scale").val(x); } function setOrientation(x){ $("#txt_Orientation").val(x); } function drawToTmpCanvas(x,y,w,h){ tmpContext.clearRect(0,0,tmpCanvas.width,tmpCanvas.height) ; var _cW=parseFloat(w/getScale()); var _cH=parseFloat(h/getScale()); tmpCanvas.width=_cW; tmpCanvas.height=_cH; tmpContext.drawImage(_sourceImg,x,y,w,h,0,0,_cW,_cH); } $("#btn_saveToTmp").click(function(){ drawToTmpCanvas(getX(),getY(),getW(),getH()); var _cW=parseFloat(getW()/getScale()); var _cH=parseFloat(getH()/getScale()); $("#results").html(""); var newCanvas=document.createElement("canvas"); var newContext=newCanvas.getContext("2d"); $("#results").append(newCanvas); drawToFinal(tmpCanvas,newCanvas,newContext,_cW,_cH,getOrientatin()); }); function drawToFinal(_sourceCanvas,_drawCanvas,_drawContext,_sw,_sh,_Orientation){ switch(_Orientation){ case 8: // 90 rotate left --需要90度向左旋转。。那么,这个 PixelYDimension就是宽度了,PixelXDimension就是高度了。 _drawCanvas.width=_sh; _drawCanvas.height=_sw; var tSetting={ dx:0, dy:0, dw:0, dh:0, transX:0, transY:0 }; tSetting.dw=_sh; var scale2=_sw/_sh; tSetting.dh=parseFloat(tSetting.dw/scale2); tSetting.dy=(_sw-tSetting.dh)/2; tSetting.transY=_sw/2; tSetting.transX=_sh/2; _drawContext.translate(tSetting.transX,tSetting.transY); _drawContext.rotate(-0.5*Math.PI); _drawContext.scale(scale2,scale2); _drawContext.drawImage(_sourceCanvas,0,0,_sw,_sh,tSetting.dx-tSetting.transX,tSetting.dy-tSetting.transY,tSetting.dw,tSetting.dh); break; case 3: //180向左旋转 (右旋转也可以。) _drawCanvas.width=_sw; _drawCanvas.height=_sh; var tSetting={ dx:0, dy:0, dw:0, dh:0, transX:0, transY:0 }; tSetting.dw=_sw; tSetting.dh=_sh; tSetting.transY=_sh/2; tSetting.transX=_sw/2; _drawContext.translate(tSetting.transX,tSetting.transY); _drawContext.rotate(1*Math.PI); //_drawContext.scale(scale2,scale2); _drawContext.drawImage(_sourceCanvas,0,0,_sw,_sh,tSetting.dx-tSetting.transX,tSetting.dy-tSetting.transY,tSetting.dw,tSetting.dh); break; case 6: //90 rotate right 需要向右旋转90度,PixelYDimension就是宽度了,PixelXDimension就是高度了。 _drawCanvas.width=_sh; _drawCanvas.height=_sw; var tSetting={ dx:0, dy:0, dw:0, dh:0, transX:0, transY:0 }; tSetting.dw=_sh; var scale2=_sw/_sh; tSetting.dh=parseFloat(tSetting.dw/scale2); tSetting.dy=(_sw-tSetting.dh)/2; tSetting.transY=_sw/2; tSetting.transX=_sh/2; _drawContext.translate(tSetting.transX,tSetting.transY); _drawContext.rotate(0.5*Math.PI); _drawContext.scale(scale2,scale2); _drawContext.drawImage(_sourceCanvas,0,0,_sw,_sh,tSetting.dx-tSetting.transX,tSetting.dy-tSetting.transY,tSetting.dw,tSetting.dh); break; case 1: //正常状态。 _drawCanvas.width=_sw; _drawCanvas.height=_sh; var tSetting={ dx:0, dy:0, dw:0, dh:0, transX:0, transY:0 }; tSetting.dw=_sw; tSetting.dh=_sh; tSetting.transY=_sh/2; tSetting.transX=_sw/2; _drawContext.translate(tSetting.transX,tSetting.transY); //_drawContext.rotate(1*Math.PI); //_drawContext.scale(scale2,scale2); _drawContext.drawImage(_sourceCanvas,0,0,_sw,_sh,tSetting.dx-tSetting.transX,tSetting.dy-tSetting.transY,tSetting.dw,tSetting.dh); break; break; } } </script>
测试用结果如下:
而实际上线的效果如下:
打开图片裁剪页面:
点击头像,然后读取本地图片:
拖动裁剪框及下面的拖动条以调整裁剪框大小及位置,最后得到:
保存裁剪结果:
done。