Canvas应用,泛洪算法(Flood Fill),图片缩放,相对位置等比缩放,判断一个点是否在一个平面闭合多边形,nginx代理html
在线抠图,智能抠图,一键抠图,钢笔抠图,矩阵抠图,图片处理,图片压缩,图片尺寸,图片格式,图片透明度,图片下载前端
Jquery,Canvas,Jcrop, Layer,MiniColorshtml5
发表这篇随笔,一方面帮助前端的同窗认识Canvas的实际应用场景和了解相关技术点,另外一方面让sukoutu.com可以帮助更多的人去快速高效的完成抠图。nginx
做为一名后端研发去开发一套纯前端的在线抠图工具实际上看起来有些疑惑和怪异,或许是由于 14年5月发表的一个随笔 html5 canvas+js实现ps钢笔抠图 的延续。算法
发表在知乎上的一篇文章 能让你知道sukoutu.com上线的起因、目的以及相关功能简介。canvas
网站几乎没有和后端服务作交互。后端用nginx做为server,另外代理了三方房展的图片访问(解决前端跨域访问网络图片跨域禁止canvas渲染的问题,经过nginx代理访问规避这个问题)。图片上传及图片操做等均在客户端浏览器上完成(图片操做流畅的很),由于有Canvas,因此整个工具网站的核心都在于js的交互及canvas的应用。后端
这里参考了一些开源项目和泛洪算法(Flood Fill)的实现案例。对于一次纯色或者背景色差比较大的图片抠图,基本都能实现一键抠图的效果,经过点击滑选闭合色区来实现抠图,可是对于背景色差复杂的图片就很难实现本身想要的抠图了。api
算法参考: 图像分割经典算法--《泛洪算法》(Flood Fill)跨域
实现原理:底层放图片,顶层放canvas,将图片按照对等尺寸渲染至到画布上获得ImageData,鼠标点击获取ImageData RGB 色值,用floodfill算法获得闭合外围坐标,在顶层canvas渲染边线(ctx.putImageData)
浏览器
1 init: function (img) { 2 this.data.img = img; 3 this.data.tempCanvas = document.createElement('canvas'); 4 var tempCtx = this.data.tempCanvas.getContext('2d'); 5 //切记,原始图像大小 6 tempCtx.canvas.width = panel.data.targetImgWidth; 7 tempCtx.canvas.height = panel.data.targetImgHeight; 8 tempCtx.drawImage(img, 0, 0, panel.data.targetImgWidth, panel.data.targetImgHeight); 9 this.data.imgData = tempCtx.getImageData(0, 0, panel.data.targetImgWidth, panel.data.targetImgHeight); 10 },
钢笔抠图参考 html5 canvas+js实现ps钢笔抠图
实现原理:底层放图片,顶层放canvas,鼠标点击坐标存放到array中,canvas根据坐标list画线画点,canvas的 globalCompositeOperation = "destination-out" 属性能够实现经过由多个点构成的闭合区间设置成透明色穿透画布背景色或是背景图片。
这里用的是很是实用的Jcrop插件,参考Jcrop官网 ,提供了丰富的api,好比等比、移动等实用的功能。
解决图片加载要等比缩放到画布中,而且居中显示。
1 function imgScale(src, w, h, fun) { 2 var img = new Image(); 3 img.src = src; 4 img.onload = function () { 5 var wi = img.width; 6 var he = img.height; 7 var toHe = he * w / wi; 8 var toWi = 0; 9 if (toHe > h) { 10 toWi = wi * h / he; 11 toHe = h; 12 } else { 13 toWi = w; 14 } 15 fun(toWi, toHe); 16 }; 17 }
在实现钢笔抠图或矩阵抠图须要放大,而且钢笔及矩阵坐标list也要等比缩放,须要用到以下算法来实现。
1 _scale = function (c, a, f) { 2 x1 *= c.x; 3 y1 *= c.y; 4 x2 *= c.x; 5 y2 *= c.y; 6 if (a) { 7 _values = [1.0000000000000002, -0, -0, 1.0000000000000002]; 8 var b = transform(a.x - translationStart.x, a.y - translationStart.y); 9 var d = transform(c.x * -b.x, c.y * -b.y); 10 translation.x = d.x + a.x; 11 translation.y = d.y + a.y 12 } 13 }; 14 15 transform = function (b, d, a) {//两中心x差,两中心y差 16 var c = new Point(_values[0] * b + _values[1] * d, _values[2] * b + _values[3] * d); 17 return c 18 };
图片压缩、图片尺寸调整、图片背景色、图片透明度实际都可经过canvas属性来操做
//透明度 ctx.globalAlpha = imgCreate.cutObj.alpha; //背景色 ctx.fillStyle = imgCreate.cutObj.color; //根据坐标list裁剪图片 var ctx = $("#createCanvas")[0].getContext('2d'); ctx.beginPath(); ctx.moveTo(0, 0); for (var i = 0; i < proxy.cutObj.pointArray.length; i++) { ctx.lineTo(proxy.cutObj.pointArray[i].pointx, proxy.cutObj.pointArray[i].pointy); } ctx.lineTo(proxy.cutObj.pointArray[0].pointx, proxy.cutObj.pointArray[0].pointy); ctx.clip(); ctx.drawImage(proxy.cutObj.imgObj, tempPointArray[0].pointx * -1, tempPointArray[0].pointy * -1, proxy.cutObj.width, proxy.cutObj.height); //生成及下载图片兼容(ie\谷歌\火狐) var fileName = proxy.cutObj.name + "." + proxy.cutObj.suffix.replace("e", ""); if (window.navigator.msSaveOrOpenBlob) { var imgData = $("#createCanvas")[0].msToBlob(); var blobObj = new Blob([imgData]); window.navigator.msSaveOrOpenBlob(blobObj, fileName); } else { var imgData = $("#createCanvas")[0].toDataURL("image/" + imgCreate.cutObj.suffix, parseFloat(imgCreate.cutObj.quality)); imgData = imgData.replace("image/" + imgCreate.cutObj.suffix, 'image/octet-stream'); var a = document.createElement('a'); var event = new MouseEvent('click'); a.download = fileName; a.href = URL.createObjectURL(dataURIToBlob(imgData)); a.dispatchEvent(event); }