溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript純色二維碼變成彩色二維碼的示例分析

發(fā)布時(shí)間:2021-08-11 14:21:46 來源:億速云 閱讀:146 作者:小新 欄目:web開發(fā)

這篇文章給大家分享的是有關(guān)JavaScript純色二維碼變成彩色二維碼的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

前置知識

drawImage方法可以把圖片畫到canvas上,getImageData方法可以獲得一個矩形區(qū)域所有像素點(diǎn)的信息,返回值的data屬性是一個一維數(shù)組,儲存了所有像素點(diǎn)的信息,一個像素點(diǎn)的信息會占四個元素,分別代表r,g,b和透明度。而像素點(diǎn)在一維數(shù)組中的順序是從左到右,從上到下。最后就是putImageData方法,把更改過的像素信息數(shù)組重新扔回畫布上。

一些小坑

第一個坑就是canvas用屬性去給寬高,別用css; 

第二個坑,做圖片處理好像得服務(wù)器環(huán)境,本地是不行的,聽說是基于什么安全考慮,最后我是通過搭本地服務(wù)器解決了canvas的報(bào)錯。

第三個坑,棧溢出,這個目前還沒找到原因,后面會詳細(xì)講

變色的思路

主要思路來自于《啊哈!算法!》里面深度優(yōu)先搜索和廣度優(yōu)先搜索的章節(jié),該章節(jié)的最后一部分的“寶島探險(xiǎn)”實(shí)現(xiàn)了給不同的區(qū)域依次編號,把編號看成染色,其實(shí)是一樣的。

具體實(shí)現(xiàn)

其實(shí)所謂的彩色二維碼,不是那種每個像素點(diǎn)顏色隨機(jī)的二維碼。仔細(xì)觀察二維碼就會發(fā)現(xiàn),黑色的部分是一塊一塊的,他們分布在白色當(dāng)中,就好像島嶼分布在海里,我們要做的就是把每個黑色塊單獨(dú)染色。黑色塊的實(shí)質(zhì)就是一個一個黑色的像素點(diǎn)。

前面也提到,我們使用canvas是因?yàn)榭梢赃M(jìn)行像素操作,所以我們的操作其實(shí)是給像素點(diǎn)染色,我們顯然不希望給背景色染色,所以背景色需要進(jìn)行一個判斷;前面也提到,背景色好像海洋分割了黑色的顏色塊,那也就是說我們讀一個像素點(diǎn)進(jìn)行染色之后,不停的判斷它右側(cè)的像素點(diǎn)顏色,當(dāng)出現(xiàn)背景色的時(shí)候就說明到達(dá)了邊界,可以停止右方向的染色,但是每個像素點(diǎn)其實(shí)有四個相連接的方向,當(dāng)一個像素點(diǎn)右邊就是背景色,我們應(yīng)該也去嘗試別的方向的可能性,這個就是深度優(yōu)先搜索,通過遞歸,不斷的驗(yàn)證當(dāng)前像素點(diǎn)的下一個位置的顏色,是背景色,那就回來,嘗試別的方向;不是背景色,那就染色,然后對染色之后的這個像素點(diǎn)進(jìn)行四個方向的驗(yàn)證。

有幾點(diǎn)提一下,判斷是不是背景色,肯定得比對rgba的值,所以顏色參數(shù)得做處理,另一個就是像素點(diǎn)信息的數(shù)組,每四個元素代表一個像素,所以想要比對正確的像素信息,這部分也要處理。
可能說的有點(diǎn)亂,我們看一下代碼

第一部分,canvas

// canvas 部分
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d");

var img = new Image();
img.src = path; //這里的path就是圖片的地址

第二部分,顏色的處理

// 分離顏色參數(shù)  返回一個數(shù)組
var colorRgb = (function() {
  var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;

  return function(str) {
    var sColor = str.toLowerCase();
    if (sColor && reg.test(sColor)) {
      if (sColor.length === 4) {
        var sColorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
        }
        sColor = sColorNew;
      }
      //處理六位的顏色值 
      var sColorChange = [];
      for (var i = 1; i < 7; i += 2) {
        sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
      }
      return sColorChange;
    } else {
      var sColorChange = sColor.replace(/(rgb\()|(\))/g, "").split(",").map(function(a) {
        return parseInt(a);
      });
      return sColorChange;
    }
  }
})();

第三部分,給初始參數(shù)

為了避免多余的操作,我們用一個標(biāo)記數(shù)組來記錄判斷過的位置

// 參數(shù)
var bg = colorRgb("#fff"); //忽略的背景色
var width = 220;
var height = 220;
var imgD; //預(yù)留給 像素信息
var colors = ["#368BFF", "#EF2767", "#F17900", "#399690", "#5aa6f7", "#fd417e", "#ffc000", "#59b6a6"];  //染色數(shù)組
// 隨機(jī)colors數(shù)組的一個序號
var ranNum = (function() {
  var len = colors.length;
  return function() {
    return Math.floor(Math.random() * len);
  }
})();

// 標(biāo)記數(shù)組 
var book = []; 
for (var i = 0; i < height; i++) { 
  book[i] = []; 
  for (var j = 0; j < width; j++) { 
    book[i][j] = 0; 
  } 
}

第四部分,獲取像素信息,對每個像素點(diǎn)進(jìn)行遍歷處理,最后扔回canvas

如果標(biāo)記過,那就跳過,如果沒標(biāo)記過,那就隨機(jī)一個顏色,深度優(yōu)先搜索并染色

img.onload = function() {
  ctx.drawImage(img, 0, 0, width, height);
  imgD = ctx.getImageData(0, 0, width, height);

  for (var i = 0; i < height; i++) {
    for (var j = 0; j < width; j++) {
      if (book[i][j] == 0 && checkColor(i, j, width, bg)) { //沒標(biāo)記過 且是非背景色
        book[i][j] = 1;
        var color = colorRgb(colors[ranNum()]);
        dfs(i, j, color);  //深度優(yōu)先搜索
      }
    }
  }

  ctx.putImageData(imgD, 0, 0);
}


// 驗(yàn)證該位置的像素 不是背景色為true
function checkColor(i, j, width, bg) {
  var x = calc(width, i, j);

  if (imgD.data[x] != bg[0] && imgD.data[x + 1] != bg[1] && imgD.data[x + 2] != bg[2]) {
    return true;
  } else {
    return false;
  }
}

// 改變顏色值
function changeColor(i, j, colorArr) {
  var x = calc(width, i, j);
  imgD.data[x] = colorArr[0];
  imgD.data[x + 1] = colorArr[1];
  imgD.data[x + 2] = colorArr[2];
}


// 返回對應(yīng)像素點(diǎn)的序號
function calc(width, i, j) {
  if (j < 0) {
    j = 0;
  }
  return 4 * (i * width + j);
}

關(guān)鍵代碼

我們通過一個方向數(shù)組,來簡化一下操作,我們約定好,嘗試的方向?yàn)轫槙r(shí)針,從右邊開始。

// 方向數(shù)組
var next = [
  [0, 1], //右
  [1, 0], //下
  [0, -1], // 左
  [-1, 0] //上 
];

// 深度優(yōu)先搜索 
function dfs(x, y, color) {
  changeColor(x, y, color);
  for (var k = 0; k <= 3; k++) {
    // 下一個坐標(biāo)
    var tx = x + next[k][0];
    var ty = y + next[k][1];

    //判斷越界
    if (tx < 0 || tx >= height || ty < 0 || ty >= width) {
      continue;
    }


    if (book[tx][ty] == 0 && checkColor(tx, ty, width, bg)) {
      // 判斷位置
      book[tx][ty] = 1;
      dfs(tx, ty, color);
    }

  }
  return;
}

我遇到的最后一個坑就是當(dāng)長寬大于220時(shí)就會棧溢出,但是小于這個值就不會有問題,具體的原因還不清楚,猜測可能是判斷那里有問題,導(dǎo)致死循環(huán)了。

全部代碼在這里

// 分離顏色參數(shù)  返回一個數(shù)組
var colorRgb = (function() {
  var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;

  return function(str) {
    var sColor = str.toLowerCase();
    if (sColor && reg.test(sColor)) {
      if (sColor.length === 4) {
        var sColorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
        }
        sColor = sColorNew;
      }
      //處理六位的顏色值 
      var sColorChange = [];
      for (var i = 1; i < 7; i += 2) {
        sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
      }
      return sColorChange;
    } else {
      var sColorChange = sColor.replace(/(rgb\()|(\))/g, "").split(",").map(function(a) {
        return parseInt(a);
      });
      return sColorChange;
    }
  }
})();

// 驗(yàn)證該位置的像素 不是背景色為true
function checkColor(i, j, width, bg) {
  var x = calc(width, i, j);

  if (imgD.data[x] != bg[0] && imgD.data[x + 1] != bg[1] && imgD.data[x + 2] != bg[2]) {
    return true;
  } else {
    return false;
  }
}

// 改變顏色值
function changeColor(i, j, colorArr) {
  var x = calc(width, i, j);
  imgD.data[x] = colorArr[0];
  imgD.data[x + 1] = colorArr[1];
  imgD.data[x + 2] = colorArr[2];
}


// 返回對應(yīng)像素點(diǎn)的序號
function calc(width, i, j) {
  if (j < 0) {
    j = 0;
  }
  return 4 * (i * width + j);
}

// 方向數(shù)組
var next = [
  [0, 1], //右
  [1, 0], //下
  [0, -1], // 左
  [-1, 0] //上 
];

// 深度優(yōu)先搜索 
function dfs(x, y, color) {
  changeColor(x, y, color);
  for (var k = 0; k <= 3; k++) {
    // 下一個坐標(biāo)
    var tx = x + next[k][0];
    var ty = y + next[k][1];

    //判斷越界
    if (tx < 0 || tx >= height || ty < 0 || ty >= width) {
      continue;
    }


    if (book[tx][ty] == 0 && checkColor(tx, ty, width, bg)) {
      // 判斷位置
      book[tx][ty] = 1;
      dfs(tx, ty, color);
    }

  }
  return;
}

/*****上面為封裝的函數(shù)*****/

/***參數(shù)***/
var bg = colorRgb("#fff"); //忽略的背景色
var width = 220;
var height = 220;
var imgD; //預(yù)留給 像素信息數(shù)組
var colors = ["#368BFF", "#EF2767", "#F17900", "#399690", "#5aa6f7", "#fd417e", "#ffc000", "#59b6a6"];  //染色數(shù)組
// 隨機(jī)colors數(shù)組的一個序號
var ranNum = (function() {
  var len = colors.length;
  return function() {
    return Math.floor(Math.random() * len);
  }
})();

// 標(biāo)記數(shù)組 
var book = []; 
for (var i = 0; i < height; i++) { 
  book[i] = []; 
  for (var j = 0; j < width; j++) { 
    book[i][j] = 0; 
  } 
}


// canvas 部分
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d");

var img = new Image();
img.src = path; //這里的path就是圖片的地址
img.onload = function() {
  ctx.drawImage(img, 0, 0, width, height);
  imgD = ctx.getImageData(0, 0, width, height);

  for (var i = 0; i < height; i++) {
    for (var j = 0; j < width; j++) {
      if (book[i][j] == 0 && checkColor(i, j, width, bg)) { //沒標(biāo)記過 且是非背景色
        book[i][j] = 1;
        var color = colorRgb(colors[ranNum()]);
        dfs(i, j, color);  //深度優(yōu)先搜索
      }
    }
  }

  ctx.putImageData(imgD, 0, 0);
}

總結(jié)

雖然看起來有點(diǎn)長,其實(shí)大部分函數(shù)都在處理像素點(diǎn)的信息。實(shí)現(xiàn)起來,主要就是得對深度優(yōu)先搜索有所了解,每個像素點(diǎn)都進(jìn)行深度優(yōu)先搜索,染過色的自然被標(biāo)記過,所以當(dāng)一個新的沒標(biāo)記過的像素點(diǎn)出現(xiàn)時(shí),自然意味著新的顏色塊。細(xì)節(jié)方面,就是注意一下imgD.data和像素點(diǎn)序號之間的對應(yīng)關(guān)系,別的也就還好了。不過注意一點(diǎn)就是,因?yàn)橄袼攸c(diǎn)很小,所以肉眼覺得不相連的色塊也有可能是連在一起的,會染成一樣的顏色。

忘了放圖了,這里放幾張,拿qq截的,把外面的邊框不小心也截了,嘛,湊活看看吧

JavaScript純色二維碼變成彩色二維碼的示例分析

感謝各位的閱讀!關(guān)于“JavaScript純色二維碼變成彩色二維碼的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI