您现在的位置是:首页 > Javascript
当前栏目
js 简单图片取色器
2023-02-26 10:21:42 时间
图片颜色识别的关键函数为:CanvasRenderingContext2D.getImageData(sx, sy, sw, sh)
, 详情参考:MDN getImageData
本文实现的效果图如下:
实现的功能点有:
(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器1核2G仅88元/年、2核4G仅698元/3年,点击这里立即抢购>>>)
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?userCode=wrvvs1rm 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 这一篇文章就够了
你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:
相关文章
- JDK中内嵌JS引擎介绍及使用
- 49195,npm最后的疯狂?盘点10款最有前途JavaScript构建工具
- 译文:5个增强Node.js应用程序增强功能
- 4个例子,吃透 JavaScript 实现的二叉搜索树 BST
- Vue中使用XML和JSON格式互转插件
- JDK中Jshell简单使用(JDK9版本以上或者JDK9版本)
- shiro中的JSP标签支持
- Java技术点-json转对象,对象转json
- SpringBoot+SpringDataJpa @Query之 JPQL使用书写模板(模糊查询and条件查询)
- Spring Boot中的Freemarker模版引擎引用css和js的正确姿势
- Node.js解压版的环境配置及相关常用命令
- JSP学习笔记(6)—— 自定义MVC框架
- JSP学习笔记(5)——Servlet、监听器、过滤器、MVC模式介绍
- Jsp学习笔记(4)——分页查询
- APIJSON简单使用
- JSP学习笔记(3)——JSTL 标签库
- JSP学习笔记(1)——Jsp指令、动作元素和内置对象
- JavaScript ES6 Promise对象
- Web前端——JavaScript扩展补充
- Web前端——表单提交和Js添加选项