前言

如今触屏设备越来越流行,并且大多数已经支持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" onchange=""><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.onload=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>


在触屏设备上面利用html5裁剪图片的更多相关文章

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

    前言 现在触屏设备越来越流行,而且大多数已经支持html5了.针对此,对触屏设备开发图片裁剪功能, 让其可以直接处理图片,减轻服务端压力. 技术点 浏览器必须支持html5,包括fileReader, ...

  2. jquery -- 触屏设备touch事件

    几种普及得比较好的触摸事件,你可以在绝大多数现代浏览器中来测试这一事件(必须是触屏设备哦): touchstart:触摸开始的时候触发 touchmove:手指在屏幕上滑动的时候触发 touchend ...

  3. 在触屏设备中拖动 overflow 元素

    在 Android 和 iOS 等触屏设备中,如果网页中某元素设置 overflow: auto 或者 overflow:scroll,那么问题就来了.在 Android 3.0 之前以及 iPhon ...

  4. HTML5裁剪图片并上传至服务器实现原理讲解

    HTML5裁剪图片并上传至服务器实现原理讲解   经常做项目需要本地上传图片裁剪并上传服务器,比如会议头像等功能,但以前实现这类需求都很复杂,往往需要先把图片上传到服务器,然后返回给用户,让用户确定裁 ...

  5. [Winform]关于cefsharp触屏设备长按文本内容,崩溃问题的修复

    摘要 在之前遇到cefsharp,在触屏电脑上,长按文本内容,会崩溃的问题. 相关文章 当时遇到这样的问题,在cefsharp项目下提交了bug.已经修复,可以参考当时我提的bug,以及解决过程,可参 ...

  6. 触屏设备上的多点触碰检测C++代码实现

    转自:http://aigo.iteye.com/blog/2272698 代码还是参考自Epic官方的塔防项目:StrategyGame 看了下C++的API,现成的API中貌似只支持单点触碰检测, ...

  7. 让BOOTSTRAP默认SLIDER支持触屏设备

    var isTouch=('ontouchstart' in window); if(isTouch){ $(".carousel").on('touchstart', funct ...

  8. iOS 利用Context裁剪图片

    下面的代码可以裁剪出圆形的图片, 1,先把不规则图片转成正方形图片 UIGraphicsBeginImageContext(newSize); [image drawInRect:CGRectMake ...

  9. 利用html5压缩图片,产出base64图片

    /* 将页面选择的图片等比压缩成指定大小(长边固定) file:图片文件 callBack:回调函数 maxLen:长边的长度*/function makePic(file,callBack,maxL ...

随机推荐

  1. 以python代码解释fork系统调用

    import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork ...

  2. CodeForces Contest #1114: Round #538 (Div. 2)

    比赛传送门:CF #1114. 比赛记录:点我. 又 FST 了. [A]Got Any Grapes? 题意简述: 有三个人,第一个人需要吃绿色葡萄至少 \(a\) 个,第二个人需要吃绿色和紫色葡萄 ...

  3. 在SharePoint 2013里配置Excel Services

    配置步骤,请参看下面两篇文章 http://www.cnblogs.com/jianyus/p/3326304.html https://technet.microsoft.com/zh-cn/lib ...

  4. 013_Mac OS X下应该如何卸载软件和安装应用软件

    一.Mac OS X下应该如何卸载软件 Mac OS X的软件安装方式有很多种,而软件卸载的情况也很不同.在Mac OS X拆除软件往往不是把软件拉到废止篓里那么简单.通常情况下要具体问题具体分析.无 ...

  5. 08 Go 1.8 Release Notes

    Go 1.8 Release Notes Introduction to Go 1.8 Changes to the language Ports Known Issues Tools Assembl ...

  6. [转]solver优化方法

    原文地址:http://www.cnblogs.com/denny402/p/5074212.html 到目前为止,caffe总共提供了六种优化方法: Stochastic Gradient Desc ...

  7. github git 无法读取远程仓库或无权限

    解决方法:重新设置ssh密钥 ssh-keygen -t rsa -C "http://github.com"//输入命令后按提示输入id_rsa.pub的存储地址 和密钥密码 地 ...

  8. opencv的级联分类器(mac)

    级联分类器的介绍:级联分类器训练 因为要训练负样本,windows电脑有些问题,所以就只能有mac进行训练. 在windows中训练,准备了负样本之后,进行三步. 1.opencv_createsam ...

  9. Python 协程检测Kubernetes服务端口

    一.需求分析 在上一篇文章,链接如下: https://www.cnblogs.com/xiao987334176/p/10237551.html 已经得到了需要的数据,现在需要对这些端口做检测,判断 ...

  10. AJAX请求时status返回状态明细表(转)

    转自:http://www.cnblogs.com/wangking/p/6530904.html AJAX请求时status返回状态明细表 readyState的五种状态2010-03-04 18: ...