web网站中常常有的功能:上传头像、上传封面等;一般图片都有一定的比例限制,所以需要前端在上传图片时,进行裁剪,并把裁剪后的图片进行上传。
本例采用Jcrop插件实现裁剪效果,canvas裁剪图片,并把base64位的toDataURL图片转换成blob(二进制数据),最后使用XMLHttpRequest上传到服务器。
Jcrop演示及下载地址:http://code.ciaoca.com/jquery/jcrop/demo/
Jcrop的使用
本例做Jcrop的简单预览功能(同理可以实现网页的放大镜功能)
- 载入 CSS 文件
1 <link rel="stylesheet" href="jquery.Jcrop.css">
- 载入 JavaScript 文件
1 <script src="jquery.js"></script> 2 <script src="jquery.Jcrop.js"></script>
- 给 IMG 标签加上 ID
1 <img id="element_id" src="pic.jpg">
- 调用 Jcrop
1 $('#element_id').Jcrop();
实例代码
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>图像裁剪-Jcrop</title> 6 <link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" /> 7 <style> 8 img { 9 border: 0px; 10 } 11 * { 12 margin: 0; 13 padding: 0; 14 } 15 .head { 16 width: 600px; 17 height: 600px; 18 background-color: gray; 19 } 20 #target{ 21 max-width: 600px; 22 max-height: 600px; 23 } 24 25 #preview-pane { 26 position: fixed; 27 top: 0; 28 right: 0; 29 width: 300px; 30 height: 300px; 31 overflow: hidden; 32 border: 1px solid purple; 33 } 34 #preview-pane .preview-container { 35 width: 100%; 36 height: 100%; 37 } 38 #preview-pane .preview-container img{ 39 max-width: 100%; 40 max-height: 100%; 41 42 } 43 </style> 44 </head> 45 <body> 46 47 <!-- 头像 --> 48 <div class="head" > 49 <img src="images/IMG_0109.JPG" id="target" alt="[Jcrop Example]" /> 50 </div> 51 52 <!-- 预览盒子 --> 53 <div id="preview-pane"> 54 <div class="preview-container"> 55 <img src="images/IMG_0109.JPG" class="jcrop-preview" alt="Preview" id="Preview"/> 56 </div> 57 </div> 58 59 <script src="js/jquery.min.js"></script> 60 <script src="js/jquery.Jcrop.js"></script> 61 <script type="text/javascript"> 62 63 // 定义一些使用的变量 64 var jcrop_api,//jcrop对象 65 boundx,//图片实际显示宽度 66 boundy,//图片实际显示高度 67 realWidth,// 真实图片宽度 68 realHeight, //真实图片高度 69 70 // 使用的jquery对象 71 $target = $('#target'), 72 $preview = $('#preview-pane'), 73 $pcnt = $('#preview-pane .preview-container'), 74 $pimg = $('#preview-pane .preview-container img'), 75 76 xsize = $pcnt.width(), 77 ysize = $pcnt.height(); 78 79 //初始化Jcrop插件 80 function initJcrop(){ 81 82 console.log('init',[xsize,ysize]); 83 $target.removeAttr("style");//清空上一次初始化设置的样式 84 $target.Jcrop({ 85 onChange: updatePreview, 86 onSelect: updatePreview, 87 aspectRatio: xsize / ysize 88 },function(){ 89 //初始化后回调函数 90 // 获取图片实际显示的大小 91 var bounds = this.getBounds(); 92 boundx = bounds[0];//图片实际显示宽度 93 boundy = bounds[1];//图片实际显示高度 94 95 // 保存jcrop_api变量 96 jcrop_api = this; 97 98 }); 99 } 100 101 //更新显示预览内容 102 function updatePreview(c){ 103 if (parseInt(c.w) > 0) 104 { 105 var rx = xsize / c.w; 106 var ry = ysize / c.h; 107 108 $pimg.css({ 109 maxWidth: Math.round(rx * boundx) + 'px', 110 maxHeight: Math.round(ry * boundy) + 'px', 111 width: Math.round(rx * boundx) + 'px', 112 height: Math.round(ry * boundy) + 'px', 113 marginLeft: '-' + Math.round(rx * c.x) + 'px', 114 marginTop: '-' + Math.round(ry * c.y) + 'px' 115 }); 116 } 117 } 118 119 window.onload = function () { 120 initJcrop(); 121 }; 122 123 </script> 124 </body> 125 </html>
预览效果
Canvas的使用
定义:<canvas> 标签定义图形,比如图表和其他图像。
注意:canvas标签的宽高与标签样式的宽高问题,把Canvas 比作是一个画板和一张画纸,标签宽高相当于画板,样式宽高相当于画纸。
canvas裁剪图片,准备上传
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>图像裁剪-Jcrop</title> 6 <link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" /> 7 <style> 8 img { 9 border: 0px; 10 } 11 * { 12 margin: 0; 13 padding: 0; 14 } 15 .head { 16 width: 600px; 17 height: 600px; 18 background-color: gray; 19 } 20 #target{ 21 max-width: 600px; 22 max-height: 600px; 23 } 24 canvas { 25 position: fixed; 26 top: 0; 27 right: 0; 28 border: 1px solid red; 29 width: 200px; 30 height: 200px; 31 } 32 </style> 33 34 35 </head> 36 <body> 37 38 <!-- 头像 --> 39 <div class="head" > 40 <img src="images/IMG_0109.JPG" id="target" alt="[Jcrop Example]" /> 41 </div> 42 43 44 <!-- 画板 --> 45 <canvas id="myCan" width="200" height="200"></canvas> 46 47 <script src="js/jquery.min.js"></script> 48 <script type="text/javascript"> 49 50 51 initCanvas(); 52 53 //初始化canvas画板内容 54 function initCanvas(){ 55 //更新canvas画板内容 56 var img= document.getElementById("target"); 57 var ct= document.getElementById("myCan"); 58 var ctx = ct.getContext("2d"); 59 60 //清空画板 61 ctx.clearRect(0,0, ct.width, ct.height); 62 //.drawImage(图像对象,原图像截取的起始X坐标,原图像截取的起始Y坐标,原图像截取的宽度,原图像截取的高度,绘制图像的起始X坐标,绘制图像的起始Y坐标,绘制图像所需要的宽度,绘制图像所需要的高度); 63 //矩形框[150,150,200,200]--原图像截取的起始X坐标,原图像截取的起始Y坐标,原图像截取的宽度,原图像截取的高度 64 ctx.drawImage(img, 150, 150, 200, 200, 0,0, ct.width , ct.height); 65 } 66 67 </script> 68 </body> 69 </html>
预览
完整代码展示
html代码
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>图像裁剪-Jcrop</title> 6 <link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" /> 7 <style> 8 img { 9 border: 0px; 10 } 11 * { 12 margin: 0; 13 padding: 0; 14 } 15 .head { 16 width: 600px; 17 height: 600px; 18 background-color: gray; 19 } 20 #target{ 21 max-width: 600px; 22 max-height: 600px; 23 } 24 25 #preview-pane { 26 position: fixed; 27 top: 0; 28 right: 0; 29 width: 300px; 30 height: 300px; 31 overflow: hidden; 32 border: 1px solid purple; 33 } 34 #preview-pane .preview-container { 35 width: 100%; 36 height: 100%; 37 38 } 39 40 41 canvas { 42 position: fixed; 43 top: 400px; 44 right: 0; 45 border: 1px solid red; 46 width: 200px; 47 height: 200px; 48 } 49 </style> 50 51 52 </head> 53 <body> 54 55 <!-- 头像 --> 56 <div class="head" > 57 <img src="" id="target" alt="[Jcrop Example]" /> 58 <input type="file" id="file" onchange="changeFile()" style="display: none;"/> 59 </div> 60 <button onClick="openBrowse()">上传图片</button> 61 <button onClick="uploadFile()">确认</button> 62 63 <!-- 预览盒子 --> 64 <div id="preview-pane"> 65 <div class="preview-container"> 66 <img src="" class="jcrop-preview" alt="Preview" id="Preview"/> 67 </div> 68 </div> 69 70 <!-- 画板 --> 71 <canvas id="myCan" width="200" height="200"></canvas> 72 73 <script src="js/jquery.min.js"></script> 74 <script src="js/jquery.Jcrop.js"></script> 75 <script type="text/javascript"> 76 77 // 定义一些使用的变量 78 var jcrop_api,//jcrop对象 79 boundx,//图片实际显示宽度 80 boundy,//图片实际显示高度 81 realWidth,// 真实图片宽度 82 realHeight, //真实图片高度 83 84 // 使用的jquery对象 85 $target = $('#target'), 86 $preview = $('#preview-pane'), 87 $pcnt = $('#preview-pane .preview-container'), 88 $pimg = $('#preview-pane .preview-container img'), 89 90 xsize = $pcnt.width(), 91 ysize = $pcnt.height(); 92 93 94 95 //1、打开浏览器 96 function openBrowse(){ 97 var ie=navigator.appName=="Microsoft Internet Explorer" ? true : false; 98 if(ie){ 99 document.getElementById("file").click(); 100 }else{ 101 var a=document.createEvent("MouseEvents"); 102 a.initEvent("click", true, true); 103 document.getElementById("file").dispatchEvent(a); 104 } 105 } 106 107 //2、从 file 域获取 本地图片 url 108 function getFileUrl(sourceId) { 109 var url; 110 if (navigator.userAgent.indexOf("MSIE")>=1) { // IE 111 url = document.getElementById(sourceId).value; 112 } else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox 113 url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0)); 114 } else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome 115 url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0)); 116 } else if(navigator.userAgent.indexOf("Safari")>0) { // Chrome 117 url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0)); 118 } 119 return url; 120 } 121 //选择文件事件 122 function changeFile() { 123 var url = getFileUrl("file");//根据id获取文件路径 124 preImg(url); 125 return false; 126 } 127 128 //3、将本地图片 显示到浏览器上 129 function preImg(url) { 130 131 console.log('url===' + url); 132 //图片裁剪逻辑 133 if(jcrop_api)//判断jcrop_api是否被初始化过 134 { 135 jcrop_api.destroy(); 136 } 137 138 //初始化预览div内容 139 initPreview(); 140 var p = document.getElementById('Preview'); 141 p.src = url; 142 143 //初始化图片 144 initTarget(); 145 var image = document.getElementById('target'); 146 image.onload=function(){//图片加载是一个异步的过程 147 //获取图片文件真实宽度和大小 148 var img = new Image(); 149 img.onload=function(){ 150 realWidth = img.width; 151 realHeight = img.height; 152 153 //获取图片真实高度之后 154 initJcrop();//初始化Jcrop插件 155 initCanvas();//初始化Canvas内容 156 }; 157 img.src = url; 158 }; 159 image.src = url; 160 } 161 162 //初始化Jcrop插件 163 function initJcrop(){ 164 165 console.log('init',[xsize,ysize]); 166 $target.removeAttr("style");//清空上一次初始化设置的样式 167 $target.Jcrop({ 168 onChange: updatePreview, 169 onSelect: updatePreview, 170 aspectRatio: xsize / ysize 171 },function(){ 172 //初始化后回调函数 173 // 获取图片实际显示的大小 174 var bounds = this.getBounds(); 175 boundx = bounds[0];//图片实际显示宽度 176 boundy = bounds[1];//图片实际显示高度 177 178 // 保存jcrop_api变量 179 jcrop_api = this; 180 181 }); 182 } 183 184 185 //更新显示预览内容 186 function updatePreview(c){ 187 if (parseInt(c.w) > 0) 188 { 189 var rx = xsize / c.w; 190 var ry = ysize / c.h; 191 192 $pimg.css({ 193 maxWidth: Math.round(rx * boundx) + 'px', 194 maxHeight: Math.round(ry * boundy) + 'px', 195 width: Math.round(rx * boundx) + 'px', 196 height: Math.round(ry * boundy) + 'px', 197 marginLeft: '-' + Math.round(rx * c.x) + 'px', 198 marginTop: '-' + Math.round(ry * c.y) + 'px' 199 }); 200 201 //更新canvas画板内容 202 var img=document.getElementById("target"); 203 var ct=document.getElementById("myCan"); 204 var ctx=ct.getContext("2d"); 205 //清空画板 206 ctx.clearRect(0,0, ct.width, ct.height); 207 //.drawImage(图像对象,原图像截取的起始X坐标,原图像截取的起始Y坐标,原图像截取的宽度,原图像截取的高度,绘制图像的起始X坐标,绘制图像的起始Y坐标,绘制图像所需要的宽度,绘制图像所需要的高度); 208 ctx.drawImage(img, c.x/boundx * realWidth,c.y/boundy * realHeight, c.w/boundx * realWidth, c.h/boundy * realHeight,0,0, ct.width, ct.height); 209 } 210 } 211 212 //初始化预览div内容 213 function initTarget(){ 214 $target.removeAttr("style");//清空上一次初始化设置的样式 215 $target.css({ 216 maxWidth: '100%', 217 maxHeight: '100%' 218 }); 219 } 220 //初始化预览div内容 221 function initPreview(){ 222 $pimg.removeAttr("style");//清空上一次初始化设置的样式 223 $pimg.css({ 224 maxWidth: xsize + 'px', 225 maxHeight: ysize + 'px' 226 }); 227 } 228 229 //初始化canvas画板内容 230 function initCanvas(){ 231 //更新canvas画板内容 232 var img= document.getElementById("target"); 233 var ct= document.getElementById("myCan"); 234 var ctx = ct.getContext("2d"); 235 236 var myCanWidth = $('#myCan').width(); 237 var myCanHeight = $('#myCan').height(); 238 239 //清空画板 240 ctx.clearRect(0,0, ct.width, ct.height); 241 242 //.drawImage(图像对象,原图像截取的起始X坐标,原图像截取的起始Y坐标,原图像截取的宽度,原图像截取的高度,绘制图像的起始X坐标,绘制图像的起始Y坐标,绘制图像所需要的宽度,绘制图像所需要的高度); 243 var dWidth = realWidth;//绘制实际宽度 244 var dHeight = realHeight;//绘制实际高度 245 if(dWidth > myCanWidth) 246 { 247 dHeight = myCanWidth / dWidth * dHeight; 248 dWidth = myCanWidth; 249 } 250 if(dHeight > myCanHeight) 251 { 252 dWidth = myCanHeight / dHeight * dWidth ; 253 dHeight = myCanHeight; 254 } 255 ctx.drawImage(img,0,0, realWidth, realHeight, 0,0, dWidth, dHeight); 256 } 257 258 //文件上传 259 function uploadFile(){ 260 //获取裁剪完后的base64图片url,转换为blob 261 var data=document.getElementById("myCan").toDataURL(); 262 var formData=new FormData(); 263 formData.append("imageName",dataURLtoBlob(data)); 264 var httprequest= null; 265 if (window.XMLHttpRequest) { 266 httprequest = new XMLHttpRequest(); 267 } else { 268 httprequest = new ActiveXObject('MicroSoft.XMLHTTP'); 269 } 270 var apiurl= ""; //上传图片的api接口,自行填写 271 httprequest.open('POST',apiurl,true); 272 httprequest.send(formData); 273 httprequest.onreadystatechange= function () { 274 275 if(httprequest.readyState == 4 ){ 276 277 if(httprequest.status == 200) 278 { 279 var json=JSON.parse(httprequest.responseText); 280 console.log(json); 281 282 }else 283 { 284 alert('获取数据错误,错误代码:' + httprequest.status + '错误信息:' + httprequest.statusText); 285 } 286 } 287 }; 288 } 289 290 //把base64位的toDataURL图片转换成blob 291 function dataURLtoBlob(dataurl) { 292 var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], 293 bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); 294 while (n--) { 295 u8arr[n] = bstr.charCodeAt(n); 296 } 297 return new Blob([u8arr], { type: mime }); 298 } 299 300 window.onload = function () { 301 //初始化图片 302 preImg('images/IMG_0109.JPG'); 303 }; 304 305 </script> 306 </body> 307 </html>
图片上传接口可以参照:【Java】JavaWeb文件上传和下载
注意:canvas在裁剪图片的时候有跨域的问题,如果裁剪网络图片,会报异常:Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
本例服务端采用的方法是:服务器转发网络图片,进行图片访问。
页面上访问:<img src="img/getImg?imgUrl=http://test.example.net/a/b/c/123456.jpg"/>
服务端JAVA代码:
1 @RequestMapping(value = "/getImg") 2 public void getImg(HttpServletRequest request, HttpServletResponse response, String imgUrl) throws Exception 3 { 4 // 统一资源 5 URL url = new URL(imgUrl); 6 // 连接类的父类,抽象类 7 URLConnection urlConnection = url.openConnection(); 8 // http的连接类 9 HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; 10 // 设定请求的方法,默认是GET 11 httpURLConnection.setRequestMethod("POST"); 12 // 设置字符编码 13 httpURLConnection.setRequestProperty("Charset", "UTF-8"); 14 // 打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。 15 httpURLConnection.connect(); 16 17 BufferedInputStream bin = new BufferedInputStream(httpURLConnection.getInputStream()); 18 ServletOutputStream outputStream = response.getOutputStream(); 19 20 int size = 0; 21 byte[] buf = new byte[1024*10]; 22 while ((size = bin.read(buf)) != -1) { 23 outputStream.write(buf, 0, size); 24 } 25 bin.close(); 26 outputStream.close(); 27 }
预览效果