如何将纯色二维码变成彩色二维码(JavaScript)
这篇文章主要为大家详细介绍了JavaScript纯色二维码变成彩色二维码的方案,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文章主要讨论的是如何将一个纯色二维码变成彩色的。
前段时间公司业务上有这么一个需求,客户不喜欢后台生成的纯色二维码,纯蓝,纯紫,纯绿都不行,想要彩色二维码。然后这个任务都落到我头上了,因为是图片处理,那主要思路就是靠canvas,canvas可以进行像素操作,所以我进行了一些尝试,也踩了一点小坑,具体记录如下。
前置知识
drawImage方法可以把图片画到canvas上,getImageData方法可以获得一个矩形区域所有像素点的信息,返回值的data属性是一个一维数组,储存了所有像素点的信息,一个像素点的信息会占四个元素,分别代表r,g,b和透明度。而像素点在一维数组中的顺序是从左到右,从上到下。最后就是putImageData方法,把更改过的像素信息数组重新扔回画布上。
一些小坑
第一个坑就是canvas用属性去给宽高,别用css;
第二个坑,做图片处理好像得服务器环境,本地是不行的,听说是基于什么安全考虑,最后我是通过搭本地服务器解决了canvas的报错。
第三个坑,栈溢出,这个目前还没找到原因,后面会详细讲
变色的思路
主要思路来自于《啊哈!算法!》里面深度优先搜索和广度优先搜索的章节,该章节的最后一部分的“宝岛探险”实现了给不同的区域依次编号,把编号看成染色,其实是一样的。
具体实现
其实所谓的彩色二维码,不是那种每个像素点颜色随机的二维码。仔细观察二维码就会发现,黑色的部分是一块一块的,他们分布在白色当中,就好像岛屿分布在海里,我们要做的就是把每个黑色块单独染色。黑色块的实质就是一个一个黑色的像素点。
前面也提到,我们使用canvas是因为可以进行像素操作,所以我们的操作其实是给像素点染色,我们显然不希望给背景色染色,所以背景色需要进行一个判断;前面也提到,背景色好像海洋分割了黑色的颜色块,那也就是说我们读一个像素点进行染色之后,不停的判断它右侧的像素点颜色,当出现背景色的时候就说明到达了边界,可以停止右方向的染色,但是每个像素点其实有四个相连接的方向,当一个像素点右边就是背景色,我们应该也去尝试别的方向的可能性,这个就是深度优先搜索,通过递归,不断的验证当前像素点的下一个位置的颜色,是背景色,那就回来,尝试别的方向;不是背景色,那就染色,然后对染色之后的这个像素点进行四个方向的验证。
有几点提一下,判断是不是背景色,肯定得比对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; } }
第四部分,获取像素信息,对每个像素点进行遍历处理,最后扔回canvas
如果标记过,那就跳过,如果没标记过,那就随机一个颜色,深度优先搜索并染色
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截的,把外面的边框不小心也截了,嘛,凑活看看吧
以上是如何将纯色二维码变成彩色二维码(JavaScript)的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

1、打开软件,进入wps文字操作界面。2、在该界面内找到插入选项。3、点击插入选项,在其编辑工具区里找到二维码选项。4、点击二维码选项,弹出二维码对话框。5、在左侧选择文本选项,在文本框里就能输入我们的信息了。6、在右侧可以设置二维码的形状和二维码的颜色。

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

企业微信二维码加载不出来怎么办?当我们在登录企业微信电脑版的时候发现二维码加载不出来无法显示的时候该怎么解决,这里小编就给大家详细介绍一下企业微信二维码加载不出来的解决方法,有需要的小伙伴快来看看吧! 方法一、网络原因 1、可能是网速缓慢,导致加载缓慢从而无法显示,可以断开之后重新连接。 2、检查电脑自身的网络问题,看看是否连接上了网络,可以重新启动一下网络设备。 方法二、维护更新 可能由于企业微信版本过低,导致无法生成二维码,可以将软件更新升级成最新版本。 方法三、防火墙 1

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest
