在線摳圖網站速摳圖sukoutu.com全面技術解析之canvas應用

  • 技術關鍵詞

          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的應用。後端

  • 智能摳圖(仿PS魔法棒效果)

         這裏參考了一些開源項目和泛洪算法(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應用

      圖片壓縮、圖片尺寸調整、圖片背景色、圖片透明度實際都可經過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);
}
相關文章
相關標籤/搜索