颜色识别的核心步骤:
1.选择图片后利用FileReader API将图片转成base64的格式
2.创建一个img标签, 将base64的内容赋值给img标签的src属性
3.调用canvas的drawImage()方法让canvas绘制这张图片
4.调用canvas的getImageData()方法获取某个像素点的颜色值
5.把这个颜色值以rgb和16进制的形式显示出来
运行效果
初始页面就一个显示框和一个选择图片的按钮,
选择图片后, 把鼠标移动到图片上就会显示图片的颜色值, 鼠标移动会跟随显示:
源代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>图片颜色识别</title>
<style>
body {
display: flex;
justify-content: center;
/*align-items: center;*/
background: white;
margin-top: 20px;
}
#color {
height: 30px;
line-height: 30px;
text-align: center;
}
#canvas {
border: 1px dashed #000;
}
.tooltip {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
min-width: 150px;
min-height: 80px;
margin: 10px;
padding: 10px 20px;
border: 1px solid #b3c9ce;
border-radius: 4px;
color: #eee;
font-size: 16px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
z-index: 9999999999999999999999;
}
.fileWrap {
margin-left: 200px;
}
</style>
</head>
<body>
<div class="wrapper">
<canvas id="canvas" width="600" height="400"></canvas>
<br>
<br>
<div class="fileWrap">
<input type="file" onchange="selectImg(this)">
</div>
<br>
<div id="color"></div>
<br>
</div>
<script>
var canvas = document.getElementById('canvas');
var canvasW = canvas.width;
var canvasH = canvas.height;
var ctx = canvas.getContext('2d');
var isSelectImg = false;
function selectImg(that) {
isSelectImg = true;
// console.log("that: ", that);
var file = that.files[0];
// console.log(file);
var reader = new FileReader();
// console.log("reader: ", reader);
reader.readAsDataURL(file);
reader.onload = (e) => {
var img = new Image();
img.src = reader.result;
img.onload = () => {
// console.log("img.width, img.height: ", img.width, img.height);
ctx.clearRect(0, 0, canvasW, canvasH);
if (img.width < canvasW && img.height < canvasH) {
canvas.height = canvasH;
ctx.drawImage(img, 0, 0, img.width, img.height);
} else {
canvas.height = canvasW * img.height / img.width; // 保持图片不变形
ctx.drawImage(img, 0, 0, canvasW, canvas.height);
}
}
}
}
// RGB to Hex
function colorRGBtoHex(rgb_color) {
if (/(^( *(rgb)\(( *\d{1,3} *,){2} *\d{1,3} *\)) *$)/.test(rgb_color)) {
var rgb = rgb_color.split(',');
var r = parseInt(rgb[0].split('(')[1]);
var g = parseInt(rgb[1]);
var b = parseInt(rgb[2].split(')')[0]);
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
return hex;
}
return rgb_color;
}
// 跟随鼠标移动的提示工具
function toolTip() {
let tooltipElem, timer;
let color = document.getElementById('color');
document.onmouseover = function(e) {
var target = e.target;
if (!isSelectImg) return;
if (target.tagName !== 'CANVAS') {
return;
}
tooltipElem = document.createElement('div');
tooltipElem.className = "tooltip";
document.body.append(tooltipElem);
document.onmousemove = function(e) {
tooltipElem.style.left = e.clientX + 'px';
tooltipElem.style.top = e.clientY + 'px';
var x = e.layerX;
var y = e.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
// console.log("pixel: ", pixel);
var data = pixel.data;
var rgb =
'rgb(' + data[0] +
',' + data[1] +
',' + data[2] +
// ',' + (data[3] / 255) + // rgba改成rgb
')';
// 函数节流
clearInterval(timer);
timer = setTimeout(function() {
// 显示颜色值
color.style.background = rgb;
color.textContent = rgb;
tooltipElem.innerText = colorRGBtoHex(rgb);
tooltipElem.style.background = rgb;
}, 15)
}
};
document.onmouseout = function() {
if (tooltipElem) {
tooltipElem.remove();
tooltipElem = null;
}
document.onmousemove = null;
clearInterval(timer)
}
}
toolTip();
</script>
</body>
</html>
图片颜色识别升级版
升级功能:
1.添加颜色保存到数据库
2.如果图片中的颜色和数据库中已有的颜色一致, 就显示颜色名称
升级后的运行效果
初始界面
选择一张图片(我选的这张图片是从颜色对照中截的图)
新增颜色, 输入颜色名称和颜色值, 然后点击保存:
刷新页面可以看到颜色对照最后有刚刚新增的颜色:
升级后的源代码
后端Node.js启动文件(使用了Express + MySQL):
let my_mysql = require('mysql');
let express = require('express');
let path = require('path');
let conn = my_mysql.createConnection({
host : 'localhost',
user : 'root',
password : '123456',
database : 'color'
});
conn.connect();
var app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());//数据JSON类型
app.use(bodyParser.urlencoded({ extended: false }));//解析post请求数据
app.use(express.static(path.join(__dirname, 'public')));
app.get('/getAllColor', async function (req, res) {
getAllColor(req, res);
})
app.post('/insertColor', async function (req, res) {
insertColor(req, res)
})
function getAllColor(req, res) {
conn.query(`SELECT * FROM color`, function (err, result) {
if (err) {
res.send({code: 1, msg: '查询数据库错误'});
console.log('[SELECT ERROR] - ', err.message);
return;
}
// console.log("result: ", result);
res.send(result)
})
}
function insertColor(req, res) {
console.log(req.body.name, req.body.value);
var name = req.body.name;
var value = req.body.value;
if (name && name.length > 20) return false;
if (value && value.length > 30) return false;
let sql = "INSERT INTO color(name, value) VALUES(?,?)";
let sqlVal = [name, value];
console.log("sqlVal: ", sqlVal);
conn.query(sql, sqlVal, function (err, result) {
if (err) {
console.log("err: ", err);
res.send('添加失败')
return false;
} else {
// console.log("result: ", result);
res.send('添加成功')
return true;
}
})
}
var port = 4006;
console.log("启动服务器的时间: ", new Date().toLocaleString());
console.log("打开浏览器访问: http://localhost:"+port);
app.listen(port);
数据库表设计:
前端首页(使用了Vue + jQuery + Layer):
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>颜色识别</title>
<style>
body {
display: flex;
justify-content: center;
/*align-items: center;*/
background: white;
margin-top: 20px;
}
#color {
height: 30px;
line-height: 30px;
text-align: center;
}
#canvas {
border: 1px dashed #000;
}
.tooltip {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
min-width: 150px;
min-height: 80px;
margin: 10px;
padding: 10px 20px;
border: 1px solid #b3c9ce;
border-radius: 4px;
color: #eee;
font-size: 16px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
z-index: 9999999999999999999999;
}
.fileWrap {
margin-left: 200px;
}
form input {
height: 25px;
width: 250px;
}
form div {
text-align: center;
margin-top: 5px;
line-height: 25px;
height: 25px;
}
.left {}
#colorList {
width: 350px;
height: 500px;
overflow: auto;
margin-left: 20px;
}
#colorList .name,
#colorList .value,
#colorList .colors {
display: inline-block;
width: 100px;
height: 30px;
overflow: hidden;
box-sizing: border-box;
}
</style>
<script src="lib/vue.js"></script>
<script src="lib/jq.js"></script>
<script src="layer/layer.js"></script>
</head>
<body>
<div class="left">
<canvas id="canvas" width="600" height="400"></canvas>
<br>
<br>
<div class="fileWrap">
<input type="file" onchange="selectImg(this)">
</div>
<br>
<div id="color"></div>
<br>
<form id="form">
<input type="text" id="name" placeholder="颜色名称" autocomplete="off">
<input type="text" id="value" oninput="realTimeShowColor(this.value)" placeholder="颜色值(可以使用rgb和16进制)" autocomplete="off">
<button type="button" onclick="insertColor()">保存颜色值</button>
<div id="realTimeShowColorId"></div>
</form>
</div>
<div class="right">
<div id="colorList">
<h2>颜色对照(点击颜色值可以复制)</h2>
<div>
<span class="name">颜色名称</span>
<span class="value">颜色值</span>
<span class="colors">颜色效果</span>
</div>
<div v-for="color in colors">
<span class="name" @click="copyColorValue">{{color.name}}</span>
<span class="value" @click="copyColorValue">{{color.value}}</span>
<span :style="'background:'+ color.value" class="colors"></span>
</div>
</div>
</div>
<script>
var alert = layer.msg; // 覆盖浏览器自带的alert
var canvas = document.getElementById('canvas');
var realTimeShowColorId = document.getElementById('realTimeShowColorId')
var canvasW = canvas.width;
var canvasH = canvas.height;
var ctx = canvas.getContext('2d');
var isSelectImg = false;
// 匹配rgb或者16进制,rgb颜色值大于255会被当做255,因此暂时不做更具体的匹配或判断了
var regExp = /(^(rgb\((\d{1,3},){2}\d{1,3}\))$)|(^(#[0-9a-fA-F]{6})$)|(^(#[0-9a-fA-F]{3})$)/;
function selectImg(that) {
isSelectImg = true;
// console.log("that: ", that);
var file = that.files[0];
// console.log(file);
var reader = new FileReader();
console.log("reader: ", reader);
reader.readAsDataURL(file);
reader.onload = (e) => {
var img = new Image();
img.src = reader.result;
img.onload = () => {
// console.log("img.width, img.height: ", img.width, img.height);
ctx.clearRect(0, 0, canvasW, canvasH);
if (img.width < canvasW && img.height < canvasH) {
canvas.height = canvasH;
ctx.drawImage(img, 0, 0, img.width, img.height);
} else {
canvas.height = canvasW * img.height / img.width; // 保持图片不变形
ctx.drawImage(img, 0, 0, canvasW, canvas.height);
}
}
}
}
function insertColor() {
var name = document.getElementById('name').value
var value = document.getElementById('value').value
if (!name) {
alert('颜色名称不能为空')
return;
}
value = value.replace(/\s/g, ''); // 去掉空格
if (!regExp.test(value)) {
alert('颜色值输入有误')
return;
}
$.ajax({
type: 'post',
url: '/insertColor',
data: {
name: name,
value: value
},
success(d) {
// console.log("d: ", d);
getAllColor();
document.forms[0].reset();
alert('添加成功')
}
})
}
var vm = new Vue({
el: '#colorList',
data: {
colors: [],
},
methods: {
copyColorValue(e) {
console.log("that: ", e.target.textContent);
var input = document.createElement('input');
input.value = e.target.textContent;
document.body.appendChild(input)
input.select();
document.execCommand("copy");
input.remove();
alert('复制成功~')
}
},
created() {
// document.addEventListener('mousemove', function (e) {
// console.log("e: ", e);
// })
}
})
function getAllColor() {
$.ajax({
url: '/getAllColor',
success(d) {
// console.log("getAllColor: ", d);
vm.colors = d;
}
})
}
getAllColor();
// RGB to Hex
function colorRGBtoHex(color) {
if (/(^( *(rgb)\(( *\d{1,3} *,){2} *\d{1,3} *\)) *$)/.test(color)) {
var rgb = color.split(',');
var r = parseInt(rgb[0].split('(')[1]);
var g = parseInt(rgb[1]);
var b = parseInt(rgb[2].split(')')[0]);
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
return hex;
}
return color;
}
function toolTip() {
let tooltipElem, timer;
let color = document.getElementById('color');
document.onmouseover = function(e) {
var target = e.target;
if (!isSelectImg) return;
if (target.tagName !== 'CANVAS') {
return;
}
tooltipElem = document.createElement('div');
tooltipElem.className = "tooltip";
document.body.append(tooltipElem);
document.onmousemove = function(e) {
tooltipElem.style.left = e.clientX + 'px';
tooltipElem.style.top = e.clientY + 'px';
var x = e.layerX;
var y = e.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
// console.log("pixel: ", pixel);
var data = pixel.data;
var rgb =
'rgb(' + data[0] +
',' + data[1] +
',' + data[2] +
// ',' + (data[3] / 255) + // rgba改成rgb
')';
color.style.background = rgb;
color.textContent = rgb;
tooltipElem.innerHTML = rgb;
tooltipElem.style.background = rgb;
// console.log("rgb: ", rgb);
// console.log(vm.colors);
let currentElemColor = colorRGBtoHex(rgb);
let colors = vm.colors;
clearInterval(timer);
timer = setTimeout(function() {
for (let i = 0; i < colors.length; i++) {
var hex = colorRGBtoHex(colors[i].value)
// console.log("hex: ", hex);
// console.log("colorRGBtoHex(rgb): ", colorRGBtoHex(rgb));
if (hex === currentElemColor) {
color.textContent = colors[i].name;
tooltipElem.innerHTML = colors[i].name;
break;
}
}
}, 100)
}
};
document.onmouseout = function() {
if (tooltipElem) {
tooltipElem.remove();
tooltipElem = null;
}
document.onmousemove = null;
clearInterval(timer)
}
}
toolTip();
function realTimeShowColor(value) {
// console.log("value: ", value);
value = value.replace(/\s/g, ''); // 去掉空格
if (!value) {
realTimeShowColorId.style.background = 'transparent'
realTimeShowColorId.textContent = ''
return;
}
if (regExp.test(value)) {
realTimeShowColorId.style.background = value
realTimeShowColorId.textContent = '颜色效果'
} else {
realTimeShowColorId.style.background = 'transparent'
realTimeShowColorId.textContent = '颜色值不正确'
}
}
</script>
</body>
</html>