前言

如今触屏设备越来越流行,并且大多数已经支持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. linux笔记_day09

    1.运算器.控制器.存储器.输入输出(IO) 地址总线:内存寻址 数据总线:传输数据 控制总线:控制指令 寄存器:cpu暂时存储器 2.系统设定 默认输出设备:标准输出,STDOUT,1(描述符)(显 ...

  2. 利用Python生成随机密码

    #coding:utf-8 #利用python生成一个随机10位的字符串 import string import random import re list = list(string.lowerc ...

  3. RabbitMQ消费端消息的获取方式(.Net Core)

    1[短链接]:BasicGet(String queue, Boolean autoAck) 通过request的方式独自去获取消息,断开式,一次次获取,如果返回null,则说明队列中没有消息. 隐患 ...

  4. linux内核环形缓冲区【转】

    转自:https://blog.csdn.net/eydwyz/article/details/56671023 循环缓冲区在一些竞争问题上提供了一种免锁的机制,免锁的前提是,生产者和消费 都只有一个 ...

  5. jmeter --使用put方法上传文件

    今天来记录下put上传文件遇到的坑吧!折腾死我了, 刚开始的时候用的jmeter3.0,各种尝试,发现始终告诉我文件内容为空<actual file content,not shown here ...

  6. java Comparator和Comparable(比较器)

    Comparable: 一个类实现了Camparable接口则表明这个类的对象之间是可以相互比较的,这个类对象组成的集合就可以直接使用sort方法排序,sort方法调用compareTo()方法里定义 ...

  7. SqlServer自定义函数Function中调用with as

    SET QUOTED_IDENTIFIER ON 标识符可以由双引号分隔,而文字必须由单引号分隔 SET QUOTED_IDENTIFIER OFF 标识符不可加引号,且必须遵守所有 Transact ...

  8. 详解使用 Tarjan 求 LCA 问题(图解)

    LCA问题有多种求法,例如倍增,Tarjan. 本篇博文讲解如何使用Tarjan求LCA. 如果你还不知道什么是LCA,没关系,本文会详细解释. 在本文中,因为我懒为方便理解,使用二叉树进行示范. L ...

  9. 深度剖析:PHP中json_encode与json_decode

    一.json_encode() 对变量进行JSON编码, 语法: json_encode ( $value [, $options = 0 ] ) 注意:1.$value为要编码的值,且该函数只对UT ...

  10. weex官方demo weex-hackernews代码解读(上)

    一.介绍 weex 是阿里出品的一个类似RN的框架,可以使用前端技术来开发移动应用,实现一份代码支持H5,IOS和Android.最新版本的weex已默认将vue.js作为前端框架,而weex-hac ...