h5 canvas 图片上传操作
最近写的小 demo,使用的是h5的 canvas来对图片进行放大,移动,剪裁等等
这是最原始的代码,比较接近我的思路,后续会再对格式和结构进行优化
html:
1 <pre name="code" class="brush: html;" rows="15" cols="300"> 2 <input type="file" name="" accept="image/gif, image/jpeg" id="upload"> 3 <canvas id="showimg" style="border:1px solid #aaa;"></canvas> 4 <p>移动:</p> 5 <input type="range" min="0" max="2" id="move" step="0.01" value="1" class="range-control" oninput="translateall()"/><br/> 6 <button id="crop">剪裁输出</button> 7 <img id="img" src="" style="border:1px solid #aaa;">
js:初始代码
var img = new Image(); var can = document.getElementById('showimg'); var ctx = can.getContext("2d"); can.width = 500; can.height = 400; var fictitious_imgwidth,fictitious_imgheight,flag; var distance_x = 0; var distance_y = 0; var orign_x,orign_y//鼠标点击时的坐标 var present_x,present_y//记录图片做上角的坐标 var substitute_x,substitute_y//暂时记录图片左上角坐标 ctx.fillStyle = "#aaa"; ctx.fillRect(0,0,500,400); ctx.beginPath(); ctx.moveTo(100,100); ctx.lineTo(400,100); ctx.lineTo(400,300); ctx.lineTo(100,300); ctx.lineTo(100,100); ctx.lineWidth = 3; ctx.strokeStyle = '#333' ctx.stroke(); ctx.clip(); ctx.closePath(); ctx.clearRect(0, 0, can.width, can.height); $('#upload').change(function(){ console.log('this is runing') ctx.clearRect(0, 0, can.width, can.height); img.onload = function(){ fictitious_imgwidth = img.width; fictitious_imgheight = img.height; present_x = can.width*0.5-img.width*0.5; present_y = can.height*0.5-img.height*0.5; ctx.drawImage(img,present_x,present_y,img.width,img.height); } img.src = getFileUrl('upload'); }) function translateall(){ var val = document.getElementById("move").value; reprint(val) } function reprint(scale){ ctx.clearRect(0, 0, can.width, can.height); fictitious_imgwidth = img.width*scale; fictitious_imgheight = img.height*scale; check_present(); ctx.drawImage(img,present_x,present_y,fictitious_imgwidth,fictitious_imgheight) } function getFileUrl(sourceId) { var url; if (navigator.userAgent.indexOf("MSIE")>=1) { // IE url = document.getElementById(sourceId).value; } else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0)); } else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0)); } return url; } $('#showimg').mousedown(function(e){ console.log('mousedown is running') orign_x = e.offsetX; orign_y = e.offsetY; judgment_isinimg(e); }).mousemove(function(e){ if(flag){ distance_x = e.offsetX - orign_x; distance_y = e.offsetY - orign_y; ctx.clearRect(0, 0, can.width, can.height); substitute_x = present_x + distance_x; substitute_y = present_y + distance_y; ctx.drawImage(img,substitute_x,substitute_y,fictitious_imgwidth,fictitious_imgheight); } }).mouseleave(function(){ flag = false present_x = substitute_x; present_y =substitute_y; }).mouseup(function(){ flag = false present_x = substitute_x; present_y =substitute_y; }) function judgment_isinimg(e){ var ll = present_x var lt = present_y var rl = present_x+fictitious_imgwidth var rt = present_y+fictitious_imgheight var x=event.clientX-can.getBoundingClientRect().left; var y=event.clientY-can.getBoundingClientRect().top; if(ll < x && x < rl && lt < y && y < rt){ flag = true; }else{ flag = false; } } function check_present(){ if(typeof present_x == 'undefined' || typeof present_y == 'undefined'){ present_x = can.width*0.5-fictitious_imgwidth*0.5; present_y = can.height*0.5-fictitious_imgheight*0.5; } } $('#crop').click(function(){ crop_canvas = document.createElement('canvas'); crop_canvas.width = 300; crop_canvas.height = 200; crop_ctx =crop_canvas.getContext('2d') crop_ctx.fillStyle = "#fff"; crop_ctx.fillRect(0,0,300,200); check_present(); crop_ctx.drawImage(img,Number(present_x)-100,Number(present_y)-100,fictitious_imgwidth,fictitious_imgheight); var fullQuality = crop_canvas.toDataURL('image/jpeg', 1.0); $('#img').attr('src',fullQuality); })
修改后:
-(function($){ var crop = { init:function(){ this.img = new Image(); this.can = document.getElementById('showimg'); var ctx = this.ctx = this.can.getContext("2d"); this.width = this.can.width = 500; this.height = this.can.height = 400; ctx.fillStyle = "#aaa"; ctx.fillRect(0,0,500,400); ctx.beginPath(); ctx.moveTo(100,100); ctx.lineTo(400,100); ctx.lineTo(400,300); ctx.lineTo(100,300); ctx.lineTo(100,100); ctx.lineWidth = 3; ctx.strokeStyle = '#333' ctx.stroke(); ctx.clip(); ctx.closePath(); this.clear(); this.addListen(); }, change:function(){ this.clear(); this.img.onload = function(){ var $this = crop; $this.img_width = $this.img.width; $this.img_height = $this.img.height; $this.fictitious_imgwidth = $this.img_width; $this.fictitious_imgheight = $this.img_height; $this.present_x = $this.width*0.5-$this.img_width*0.5; $this.present_y = $this.height*0.5-$this.img_height*0.5; $this.ctx.drawImage($this.img,$this.present_x,$this.present_y,$this.img_width,$this.img_height); } this.img.src = this.getFileUrl('upload'); }, translate:function(){ var val = document.getElementById("move").value; this.clear(); this.fictitious_imgwidth = this.img_width*val; this.fictitious_imgheight = this.img_height*val; this.ctx.drawImage(this.img,this.present_x,this.present_y,this.fictitious_imgwidth,this.fictitious_imgheight); }, mouseDown:function(e){ this.orign_x = e.offsetX; this.orign_y = e.offsetY; this.judgmentIsInImg(e); }, mouseMove:function(e){ var e = e || event; if(this.flag){ this.distance_x = e.offsetX - this.orign_x; this.distance_y = e.offsetY - this.orign_y; this.clear(); this.substitute_x = this.present_x + this.distance_x; this.substitute_y = this.present_y + this.distance_y; this.ctx.drawImage(this.img,this.substitute_x,this.substitute_y,this.fictitious_imgwidth,this.fictitious_imgheight); } }, mouseLeave:function(){ if(this.flag){ this.present_x = this.substitute_x; this.present_y = this.substitute_y; this.flag = false; } }, out:function(){ var crop_canvas = document.createElement('canvas'); crop_canvas.width = 300; crop_canvas.height = 200; crop_ctx = crop_canvas.getContext('2d'); crop_ctx.fillStyle = "#fff"; crop_ctx.fillRect(0,0,300,200); crop_ctx.drawImage(this.img,Number(this.present_x)-100,Number(this.present_y)-100,this.fictitious_imgwidth,this.fictitious_imgheight); var fullQuality = crop_canvas.toDataURL('image/jpeg', 1.0); $('#img').attr('src',fullQuality); }, judgmentIsInImg:function(e){ var e = e || event; var ll = this.present_x; var lt = this.present_y; var rl = this.present_x+this.fictitious_imgwidth; var rt = this.present_y+this.fictitious_imgheight; var x=e.clientX-this.can.getBoundingClientRect().left; var y=e.clientY-this.can.getBoundingClientRect().top; if(ll < x && x < rl && lt < y && y < rt){ this.flag = true; }else{ this.flag = false; } }, clear:function(){ this.ctx.clearRect(0, 0, this.width, this.height); }, getFileUrl:function(id){ var url; try{ if (navigator.userAgent.indexOf("MSIE")>=1) { // IE url = document.getElementById(id).value; } else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox url = window.URL.createObjectURL(document.getElementById(id).files.item(0)); } else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome url = window.URL.createObjectURL(document.getElementById(id).files.item(0)); } }catch(e){ throw new Error('you can ignore it') } return url; }, addListen:function(){ $('#upload').on('change',this.change.bind(this)); $('#move').on('input',this.translate.bind(this)); $('#showimg').on('mousedown',this.mouseDown.bind(this)) .on('mousemove',this.mouseMove.bind(this)) .on('mouseleave mouseup',this.mouseLeave.bind(this)); $('#crop').on('click',this.out.bind(this)); } } return crop.init(); })(jQuery)
如果还有何不足,请多多指正
12.16日修改 因为最近讨论到了头像上传,剪裁的问题,我又对此进行了回顾,发现该 demo 也有不足之处,所以我花了点时间重新修改一下再添加了注释:
最新代码:
-(function($) { var crop = { init: function() { this.img = new Image(); this.can = document.getElementById('showimg'); var ctx = this.ctx = this.can.getContext("2d"); this.width = this.can.width = 500; this.height = this.can.height = 400; ctx.fillStyle = "#aaa"; ctx.fillRect(0, 0, 500, 400); ctx.beginPath(); ctx.moveTo(100, 100); ctx.lineTo(400, 100); ctx.lineTo(400, 300); ctx.lineTo(100, 300); ctx.lineTo(100, 100); ctx.lineWidth = 3; ctx.strokeStyle = '#333' ctx.stroke(); ctx.clip(); ctx.closePath(); this.clear(); this.addListen(); }, render(src) { this.img = new Image(); this.img.onload = function() { var $this = crop; $this.img_width = $this.img.width; //原图像横坐标 $this.img_height = $this.img.height; //原图像纵坐标 $this.fictitious_imgwidth = $this.img_width; //被缩放的图像横坐标 $this.fictitious_imgheight = $this.img_height; //被缩放的图像纵坐标 //因为后面图像的变化后长度都不是原长度了,后面使用图像长度是就使用fictitious属性 $this.init_x = $this.width * 0.5; //图片中心点横坐标 $this.init_y = $this.height * 0.5; //图片中心点纵坐标 //绘图时同过中心点减去fictitious/2长度来确定图像左上角的坐标 $this.ctx.drawImage($this.img, $this.init_x - $this.img_width / 2, $this.init_y - $this.img_height / 2, $this.img_width, $this.img_height); }; this.img.src = src; }, change: function() { this.clear(); $('#move').val(1) //根据实际需要进行初始化 var reader = new FileReader(); var img = $('#upload').get(0).files[0]; reader.onload = function(e) { crop.render(e.target.result); }; reader.readAsDataURL(img); }, translate: function() { var val = document.getElementById("move").value; this.clear(); this.fictitious_imgwidth = this.img_width * val; this.fictitious_imgheight = this.img_height * val; this.ctx.drawImage(this.img, this.init_x - this.fictitious_imgwidth / 2, this.init_y - this.fictitious_imgheight / 2, this.fictitious_imgwidth, this.fictitious_imgheight); }, mouseDown: function(e) { this.orign_x = e.offsetX; this.orign_y = e.offsetY; this.judgmentIsInImg(e); //判断点击是否在图像内 }, mouseMove: function(e) { var e = e || event; if (this.flag) { this.distance_x = e.offsetX - this.orign_x; //鼠标移动的长度 this.distance_y = e.offsetY - this.orign_y; this.clear(); this.substitute_x = this.init_x + this.distance_x; this.substitute_y = this.init_y + this.distance_y; this.ctx.drawImage(this.img, this.substitute_x - this.fictitious_imgwidth / 2, this.substitute_y - this.fictitious_imgheight / 2, this.fictitious_imgwidth, this.fictitious_imgheight); } }, mouseLeave: function() { if (this.flag) { this.init_x = this.substitute_x; this.init_y = this.substitute_y; this.flag = false; } }, out: function() { //输出图像 var crop_canvas = document.createElement('canvas'); crop_canvas.width = 300; crop_canvas.height = 200; crop_ctx = crop_canvas.getContext('2d'); crop_ctx.fillStyle = "#fff"; crop_ctx.fillRect(0, 0, 300, 200); crop_ctx.drawImage(this.img, this.init_x - this.fictitious_imgwidth / 2 - 100, this.init_y - this.fictitious_imgheight / 2 - 100, this.fictitious_imgwidth, this.fictitious_imgheight); //这边的减去100 是原 canvas 阴影边框的长度 var fullQuality = crop_canvas.toDataURL('image/jpeg', 1.0); $('#img').attr('src', fullQuality); }, judgmentIsInImg: function(e) { var e = e || event; var ll = this.init_x - this.fictitious_imgwidth / 2; var lt = this.init_y - this.fictitious_imgheight / 2; var rl = this.init_x + this.fictitious_imgwidth / 2; var rt = this.init_y + this.fictitious_imgheight / 2; //图像四个角的坐标 var x = e.clientX - this.can.getBoundingClientRect().left; var y = e.clientY - this.can.getBoundingClientRect().top; if (ll < x && x < rl && lt < y && y < rt) { this.flag = true; } else { this.flag = false; } }, clear: function() { this.ctx.clearRect(0, 0, this.width, this.height); }, addListen: function() { $('#upload').on('change', this.change.bind(this)); $('#move').on('input', this.translate.bind(this)); $('#showimg').on('mousedown', this.mouseDown.bind(this)) .on('mousemove', this.mouseMove.bind(this)) .on('mouseleave mouseup', this.mouseLeave.bind(this)); $('#crop').on('click', this.out.bind(this)); } } return crop.init(); })(jQuery)
修改之后,进行缩放时不是原来的按左上角坐标缩放,而是按图像中心缩放,因为这样的缩放方式,所以不能靠记录图像左上角坐标进行绘制,而是记录图像中心点位置,还解决了手机端的图片获取兼容问题,原来的获取图片 url 方法只能在 PC端有效;
gitHub:https://github.com/Grewer/JsDemo/tree/master/crop
demo:https://grewer.github.io/JsDemo/crop/crop.html
如果这篇文章帮助到了你,请给我一个 star