图片颜色识别的关键函数为:
CanvasRenderingContext2D.getImageData(sx, sy, sw, sh)
, 详情参考:
MDN getImageData
本文实现的效果图如下:
实现的功能点有:
1). 加载本地图片
2). 鼠标悬浮显示出相应的颜色
3). 按键盘快捷键复制代码
4). 额外小功能,图片支持拖曳加载
接下来详细介绍每一步的实现:
-
加载本地图片
// 绘制图片到 canvas
function drawImage() {
if (canvasWidth != -1) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight); // 清除 canvas 内容
// 获取图片宽高
imageWidth = image.width;
imageHeight = image.height;
// 计算 canvas 宽高(需要预留提示框的位置)
canvasWidth = imageWidth + toolTipWidth;
if (imageWidth > canvasWidth) {
canvasWidth = imageWidth;
canvasHeight = imageWidth + toolTipHeight;
if (imageHeight > canvasHeight) {
canvasHeight = imageHeight;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// 获取元素数据
rect = canvas.getBoundingClientRect();
ctx.drawImage(image, 0, 0); // 绘制图片
// 通过 getImageData 获取图片的所有像素颜色数组(从左往右,从上往下)
imageData = ctx.getImageData(0, 0, image.width, image.height).data;
// for (let i = 0, len = imageData.length; i < len; i = i + 4) {
// let item = {
// x: ((i / 4) % imageWidth) + 1,
// y: Math.floor(i / 4 / imageWidth) + 1,
// };
// 加载图像
function loadImage(url) {
image.src = url;
image.onload = function () {
drawImage(image);
// 上传文件
function uploadImage(img) {
// 判断是否有选中文件
if (!img) return;
// 检测是否是图片类型
if (img.type.indexOf('image') !== 0) {
alert('只能选择图片');
return;
// 定义文件读取对象,用于读取文件
var reader = new FileReader();
reader.readAsDataURL(img); // 读取图片内容为 url 格式
reader.onload = function () {
loadImage(reader.result); // 加载图片
// 定义选择图片
imgSelector.addEventListener('change', function (e) {
uploadImage(e.target.files[0]); // 选中的图片文件
});
看似是一步,实际这一步里面包含了很多步:监听
input-file change
事件 --> 通过
FileReader
读取本地文件 --> 将读取到的本地文件(
reader.result
)放置到
Image
--> 将
image
绘制到
canvas
--> 读取
getImageData
图片颜色。
-
鼠标悬浮显示颜色信息
// 将图片的 r, g, b 分别转换为 16进制的颜色
function colorItemHex(itemNumber) {
let hex = itemNumber.toString(16);
return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
// 添加鼠标滑动事件
canvas.addEventListener('mousemove', function (evt) {
clearTimeout(t);
t = setTimeout(() => {
if (imageData != null) {
getBoundingClient()中的[left, top] 获取元素距离视图左边和上边的距离
clientX 和 clientY 获取鼠标距离试图左边和上边的距离
let x = evt.clientX - rect.left;
let y = evt.clientY - rect.top;
ctx.clearRect(0, 0, canvasWidth, canvasHeight); // 清除 canvas 内容
// 重新绘制
ctx.drawImage(image, 0, 0);
// 根据坐标计算像素点位置,详情参考:desc.jpg
var i = ((y - 1) * imageWidth + x - 1) * 4;
if (x >= imageWidth || y >= imageHeight) {
return;
var tsPointX = x,
tsPointY = y; // 提示框的位置
if (x + toolTipWidth > canvasWidth) {
// 右边无法绘制出提示框, 左边绘制
tsPointX = x - toolTipWidth;
if (y + toolTipHeight > canvasHeight) {
// 下边无法绘制出提示框,上边绘制
tsPointY = y - toolTipHeight;
// 重新绘制新的提示框
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; // 矩形填充颜色
ctx.fillRect(tsPointX, tsPointY, toolTipWidth, toolTipHeight); // 绘制矩形
ctx.strokeStyle = 'blue'; // 边框颜色
ctx.strokeRect(tsPointX, tsPointY, toolTipWidth, toolTipHeight); // 绘制矩形边框
// rgb 模式为颜色值的十进制数模式
let red = imageData[i];
let green = imageData[i + 1];
let blue = imageData[i + 2];
let alpha = imageData[i + 3];
let hex =
'#' +
colorItemHex(red) +
colorItemHex(green) +
colorItemHex(blue);
currHTML = hex;
// 设置文本样式
ctx.font = '16px sans-serif';
ctx.fillStyle = 'black';
if (alpha === 255) {
currRGB = `(${red}, ${green}, ${blue})`;
// 255 为完全不透明, 0 - 完全透明
ctx.fillText(
`RGB: ${currRGB}`,
tsPointX + 7,
tsPointY + 20 // 40 = 20 + 16(字体大小) + 5
} else {
// 颜色值的 alpha 是 0~255,而 css rgba() 函数的 alpha 为 0~1
let cssAlpha = Number((alpha / 255).toFixed(2));
currRGB = `(${red}, ${green}, ${blue}, ${cssAlpha})`;
ctx.fillText(
`RGBA: ${currRGB}`,
tsPointX + 7,
tsPointY + 20 // 40 = 20 + 16 * 2(字体大小) + 5
// hex 为颜色值的16进制模式
// 关于进制之间的手动转换可以参考:https://www.cnblogs.com/ysocean/p/7513061.html
ctx.fillText('HTML: ' + hex, tsPointX + 7, tsPointY + 40);
ctx.fillText('按 C 复制 HTML 代码', tsPointX + 7, tsPointY + 60);
ctx.fillText('按 V 复制 RGB 代码', tsPointX + 7, tsPointY + 80);
}, 150);
// 添加鼠标移出事件
canvas.addEventListener('mouseout', function () {
clearTimeout(t);
ctx.clearRect(0, 0, canvasWidth, canvasHeight); // 清除 canvas 内容
// 重新绘制
ctx.drawImage(image, 0, 0);
currHTML = currRGB = null;
});
这一步也是有很多步的:添加鼠标滑动事件 --> 根据鼠标坐标点获取在 ImageData 数组中的位置 --> 获取到图片
RGBA
--> 通过
canvas
绘制提示框
注意要点:
-
要在鼠标移出的时候,清除之前的提示框
-
如何根据坐标计算在图片颜色集中的位置,详细解释,会在下面放图说明
-
获取到的
RGBA
是十进制式的数据,如果需要
HTML
代码,则还需要转换为十六进制,
进制间的相互转换
-
加入
setTimeout
避免滑动过快频繁触发
-
按键盘快捷键复制代码
// 复制内容到剪贴板
function copy(copyValue) {
var $tmpCopyNode = document.createElement('input');
$tmpCopyNode.type = 'text';
$tmpCopyNode.className = 'copy-node';
$tmpCopyNode.value = copyValue;
document.body.append($tmpCopyNode);
$tmpCopyNode.select();
document.execCommand('copy');
document.body.removeChild($tmpCopyNode);
// 监听键盘事件
document.addEventListener('keydown', function (e) {
if (e.keyCode === 67) {
// 按下了 C 键,复制 HTML 代码
if (currHTML != null) {
copy(currHTML);
} else if (e.keyCode === 86) {
// 按下了 V 键,复制 RGB 代码
if (currRGB != null) {
copy(currRGB);
});
这里的难点就在于
复制
功能的实现,这里的复制是通过
input
实现的,所以需要让
input
透明并且不能显示在屏幕上,这个就需要
CSS
的配合:
/* 一个用于复制内容的输入框的样式 */
.copy-node {
/* 将位置放到屏幕外 */
position: fixed;
top: -100px;
left: -100px;
/* 将背景和颜色设置为透明, 避免显现 */
border: none;
outline: none;
background-color: transparent;
color: transparent;
}
-
拖曳加载图片
/* 要实现拖曳上传的功能,以下2个事件必须监听 */
// 监听当被拖动元素在目的地元素内时触发, 取消浏览器的默认行为,要不然浏览器会默认打开新的标签页预览图片
$imgSelectorBtn.addEventListener('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
// 当被拖动元素在目的地元素里放下时触发, 一般需要取消浏览器的默认行为
$imgSelectorBtn.addEventListener('drop', function (e) {
e.stopPropagation();
e.preventDefault();
uploadImage(e.dataTransfer.files[0]); // 上传文件
});
下面补上两段代码:
-
按钮的
CSS
代码
/* 定义文件选择按钮 */
.img-selector {
display: none;
.file {
position: absolute;
width: 100%;
font-size: 90px;
.img-selector-btn {
color: #ffffff;
background: #06980e;
text-align: center;
cursor: pointer;
border: 1px solid #cccccc;
padding: 7px 10px;
display: inline-block;
box-sizing: border-box;
.img-selector-btn:hover {
background: #04bc0d;
}
-
所有的
HTML
标签
<canvas id="canvas"></canvas>
<input
type="file"
class="img-selector"
id="imgSelector"
accept="image/*"
<label
id="imgSelectorBtn"
class="img-selector-btn"
for="imgSelector"
title="JPG,GIF,PNG"
</label>
</div>
-
所有声明的变量及说明:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var posColors = []; // 保存所有位置的像素信息
var rect = null; // canvas 元素矩阵数据
var t = -1; // 定时器,避免频繁触发鼠标悬浮事件
var imageData = null; // 图片像素点颜色数组
var imageWidth = -1,
imageHeight; // 图片宽度,高度
var canvasWidth = -1,
canvasHeight = -1; // canvas 宽度和高度
var toolTipWidth = 175,
toolTipHeight = 90; // tooltip 提示框的宽度和高度
var currRGB = null,
currHTML = null;
var $copyNode = document.getElementById('copyNode'); // 用于复制操作的编辑框
var $imgSelectorBtn = document.getElementById('imgSelectorBtn'); // 图片选择按钮
var image = new Image(); // 构造图片
关于
canvas
的内容可参考菜鸟教程相关专题:
HTML5 Canvas
、
学习 HTML5 Canvas 这一篇文章就够了