> 웹 프론트엔드 > JS 튜토리얼 > 단색 QR 코드를 컬러 QR 코드로 변환하는 방법(JavaScript)

단색 QR 코드를 컬러 QR 코드로 변환하는 방법(JavaScript)

零下一度
풀어 주다: 2017-04-28 10:21:48
원래의
3849명이 탐색했습니다.

이 글에서는 주로 자바스크립트 단색 QR 코드를 컬러 QR 코드로 변환하는 솔루션을 자세히 소개합니다. 관심 있는 친구들이 참고할 수 있습니다.

이 글에서는 주로 방법에 대해 소개합니다. 단색 QR 코드를 컬러로 변환합니다.

얼마 전 회사 사업에서 고객이 배경에 생성된 단색 QR 코드를 좋아하지 않는다는 요구가 있었습니다. 그들은 단색 QR 코드를 원하지 않았습니다. 코드. 그러다가 이 작업이 나에게 맡겨졌다. 이미지 처리이기 때문에 캔버스에 의존하는 것이 주된 아이디어였고, 캔버스는 픽셀 작업을 수행할 수 있기 때문에 몇 가지 시도를 했으나 몇 가지 함정에 부딪혔다.

전제 지식

drawImage 메소드는 캔버스에 그림을 그릴 수 있으며, getImageData는 메소드는 직사각형 영역의 모든 픽셀 정보를 얻을 수 있습니다. 반환 값의 데이터 속성은 모든 픽셀의 정보를 저장하는 1차원 배열입니다. 각각 r, g, b 및 투명도입니다. 1차원 배열의 픽셀 순서는 왼쪽에서 오른쪽, 위에서 아래입니다. 마지막은 변경된 픽셀 정보 배열을 캔버스로 다시 보내는 putImageData 메서드입니다.

몇 가지 작은 함정

첫 번째 함정은 캔버스가 속성을 사용하여 너비와 높이를 제공한다는 것입니다. CSS를 사용하지 마세요.

두 번째 함정 함정, 이미지 처리가 로컬에서는 불가능한 서버 환경이 필요한 것 같습니다.. 보안상의 문제로 인해 결국 로컬 서버를 설정하여 캔버스 오류를 해결했습니다.

세 번째 함정은 아직까지 원인을 찾지 못한 경우입니다.

색상 변경 아이디어

주요 아이디어는 "아하!"에서 나옵니다. 연산! 깊이 우선 탐색과 너비 우선 탐색 장에서는 장의 마지막 부분인 '보물섬 모험'에서 서로 다른 지역의 순차적인 번호 매기기를 구현합니다. 숫자를 색칠이라고 생각하지만 실제로는 동일합니다.

구체적인 구현

사실 컬러QR코드는 픽셀 하나하나의 색상이 랜덤하게 나오는 그런 QR코드가 아닙니다. QR코드를 자세히 보면 검은 부분이 하나하나씩 흰색 부분에 분포되어 있는 것을 알 수 있는데, 마치 바다에 분포되어 있는 섬처럼 검은 부분을 하나하나 염색해야 하는 것입니다. . 검은색 블록의 핵심은 검은색 픽셀이다.

앞서 언급했듯이 픽셀 작업을 수행할 수 있기 때문에 캔버스를 사용하므로 실제로 픽셀을 염색하는 작업입니다. 분명히 배경색을 염색하고 싶지 않으므로 배경색을 판단해야 합니다. 이전에는 배경색이 검은 색 블록을 나누는 바다와 같다고도 했는데, 이는 픽셀을 읽고 염색한 후 배경색이 결정되면 끊임없이 픽셀의 색상을 판단한다는 의미입니다. 경계가 올바른 방향으로 색칠되는 것을 멈출 수 있지만 실제로는 각 픽셀의 오른쪽이 배경색인 경우 다른 방향의 가능성도 시도해야 합니다. 깊이 우선 탐색은 재귀를 통해 현재 픽셀의 다음 위치의 색상이 배경색인지 지속적으로 확인하고, 다시 돌아와서 배경색이 아닌 경우 다른 방향을 시도한 다음 염색합니다. 염색된 픽셀의 네 방향을 확인합니다.

몇 가지 언급할 사항이 있습니다. 배경색인지 확인하려면 rgba 값을 비교해야 하므로 다른 하나는 픽셀 정보의 배열입니다. 따라서 정확한 픽셀 정보를 비교하려면 이 부분도 처리해야 합니다.
조금 헷갈릴 수도 있으니 코드를 살펴보겠습니다

첫 번째 부분, canvas

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

var img = new Image();
img.src = path; //这里的path就是图片的地址
로그인 후 복사

두 번째 부분, 색상 처리

// 分离颜色参数  返回一个数组
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;
    }
  }
})();
로그인 후 복사

세 번째 부분 초기 매개변수 제공

중복 작업을 피하기 위해 마커 배열을 사용하여 판단된 위치를 기록합니다

// 参数
var bg = colorRgb("#fff"); //忽略的背景色
var width = 220;
var height = 220;
var imgD; //预留给 像素信息
var colors = ["#368BFF", "#EF2767", "#F17900", "#399690", "#5aa6f7", "#fd417e", "#ffc000", "#59b6a6"];  //染色数组
// 随机colors数组的一个序号
var ranNum = (function() {
  var len = colors.length;
  return function() {
    return Math.floor(Math.random() * len);
  }
})();

// 标记数组 
var book = []; 
for (var i = 0; i < height; i++) { 
  book[i] = []; 
  for (var j = 0; j < width; j++) { 
    book[i][j] = 0; 
  } 
}
로그인 후 복사

네 번째 부분인 픽셀 정보를 얻고, 각 픽셀을 순회한 후 마지막으로 캔버스에 다시 던집니다.

표시된 경우 건너뛰고, 그렇지 않은 경우 무작위로 색상을 지정합니다. , 깊이 우선 검색 및 염색

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)) { //没标记过 且是非背景色
        book[i][j] = 1;
        var color = colorRgb(colors[ranNum()]);
        dfs(i, j, color);  //深度优先搜索
      }
    }
  }

  ctx.putImageData(imgD, 0, 0);
}
// 验证该位置的像素 不是背景色为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];
}


// 返回对应像素点的序号
function calc(width, i, j) {
  if (j < 0) {
    j = 0;
  }
  return 4 * (i * width + j);
}
로그인 후 복사

키 코드

작업을 단순화하기 위해 방향 배열을 사용합니다. 시도할 방향은 시계 방향이라는 데 동의했습니다. 오른쪽에서.

// 方向数组
var next = [
  [0, 1], //右
  [1, 0], //下
  [0, -1], // 左
  [-1, 0] //上 
];

// 深度优先搜索 
function dfs(x, y, color) {
  changeColor(x, y, color);
  for (var k = 0; k <= 3; k++) {
    // 下一个坐标
    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;
}
로그인 후 복사

제가 겪은 마지막 함정은 길이와 너비가 220보다 크면 스택이 오버플로되지만 이 값보다 작으면 문제가 없다는 것입니다. 구체적인 이유는 명확하지 않습니다. . 거기에 문제가 있어서 무한 루프가 발생하는 것 같습니다.

모든 코드는 여기에

// 分离颜色参数  返回一个数组
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;
    }
  }
})();

// 验证该位置的像素 不是背景色为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];
}

// 返回对应像素点的序号
function calc(width, i, j) {
  if (j < 0) {
    j = 0;
  }
  return 4 * (i * width + j);
}
// 方向数组
var next = [
  [0, 1], //右
  [1, 0], //下
  [0, -1], // 左
  [-1, 0] //上 
];

// 深度优先搜索 
function dfs(x, y, color) {
  changeColor(x, y, color);
  for (var k = 0; k <= 3; k++) {
    // 下一个坐标
    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;
}

/*****上面为封装的函数*****/

/***参数***/
var bg = colorRgb("#fff"); //忽略的背景色
var width = 220;
var height = 220;
var imgD; //预留给 像素信息数组
var colors = ["#368BFF", "#EF2767", "#F17900", "#399690", "#5aa6f7", "#fd417e", "#ffc000", "#59b6a6"];  //染色数组
// 随机colors数组的一个序号
var ranNum = (function() {
  var len = colors.length;
  return function() {
    return Math.floor(Math.random() * len);
  }
})();

// 标记数组 
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)) { //没标记过 且是非背景色
        book[i][j] = 1;
        var color = colorRgb(colors[ranNum()]);
        dfs(i, j, color);  //深度优先搜索
      }
    }
  }

  ctx.putImageData(imgD, 0, 0);
}
로그인 후 복사

요약

조금 길어 보이지만 대부분의 기능은 실제로 픽셀 정보를 처리합니다. 이를 구현하기 위해서는 깊이 우선 탐색을 이해하는 것이 가장 중요하며, 각 픽셀을 깊이 우선 탐색하여 염색된 것은 자연스럽게 표시되므로 표시되지 않은 새로운 픽셀이 나타나면 자연스럽게 새로운 색상 블록을 의미합니다. 세부적인 측면에서는 imgD.data와 픽셀 일련 번호 간의 대응에만 주의를 기울이면 다른 모든 것은 괜찮을 것입니다. 하지만 한 가지 주의할 점은 픽셀이 매우 작기 때문에 육안으로는 단절된 것처럼 보이는 컬러 블록이 서로 연결되어 같은 색상으로 염색될 수 있다는 점입니다.

사진 올리는 걸 깜빡해서 몇 장 올립니다. QQ 스크린샷을 찍다가 실수로 바깥쪽 테두리가 잘렸네요.

위 내용은 단색 QR 코드를 컬러 QR 코드로 변환하는 방법(JavaScript)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿