图片颜色识别的关键函数为: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>
<div>
<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 这一篇文章就够了