news 2026/4/16 7:07:15

【前端】【canvas】图片颜色填充工具实现详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【前端】【canvas】图片颜色填充工具实现详解

概述

平时开发中可能会遇到一些图像处理的工作,本文就通过HTML5 Canvas实现一个类似油漆桶工具的颜色填充功能。
功能:将该区域中颜色相近的像素填充为目标颜色,实现类似Photoshop中油漆桶工具的效果。

实现原理

颜色填充算法的核心是种子填充算法,也称为"泛洪填充"。基本思路是从一个起始点(种子)开始,向四周扩散,将颜色相近的像素替换为目标颜色。

如上图,是一个3px,3px 像素的图片。如果鼠标点击到(1,1)坐标的像素,扩散算法就是向上下左右去扩散,被扩散的位置继续向上下左右去扩散,直到超出边界,或者颜色不相近,或者颜色和目标颜色完全相同为止。

实现步骤

  1. 需要先加载一张图片到canvas画布中。
  2. 给画布添加点击事件,获取到点击的坐标。
  3. 根据坐标获取到点击位置的颜色
  4. 修改当前像素点的位置颜色,并且扩散修改相邻位置 颜色相近的 像素点颜色,(递归)

代码结构

1.HTML结构

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Canvas颜色填充工具</title><style>*{margin:0;padding:0;}canvas{display:block;border:1px solid #ccc;/* 添加边框便于观察 */}</style></head><body><canvas></canvas><scriptsrc="main.js"></script></body></html>

2.初始化Canvas和图像加载

constcvs=document.querySelector('canvas');constctx=cvs.getContext('2d',{willReadFrequently:true// 优化性能,频繁读取图像数据});// 初始化:加载图片到Canvasfunctioninit(){constimg=newImage();img.onload=()=>{cvs.width=img.width;cvs.height=img.height;ctx.drawImage(img,0,0,img.width,img.height);};img.src='./img/image.png';// 替换为你的图片路径}init();

核心算法:颜色填充

// 监听Canvas点击事件cvs.addEventListener('click',(e)=>{// 1. 获取点击位置的像素信息constx=e.offsetX;consty=e.offsetY;constimgData=ctx.getImageData(0,0,cvs.width,cvs.height);constclickColor=getColor(x,y,imgData.data);// 2. 改变颜色(目标颜色为半透明黑色)changeColor(x,y,[0,0,0,20],imgData.data,clickColor);// 3. 将修改后的图像数据重新绘制到Canvasctx.putImageData(imgData,0,0);});/** * 颜色填充算法 - 递归实现 * @param {number} x - 当前像素点的x坐标 * @param {number} y - 当前像素点的y坐标 * @param {Array} targetColor - 目标颜色 [R, G, B, A] * @param {Uint8ClampedArray} imgData - 图像像素数据 * @param {Array} clickColor - 初始点击位置的颜色 * @returns {void} */functionchangeColor(x,y,targetColor,imgData,clickColor){// 边界检查:确保坐标在Canvas范围内if(x<0||x>=cvs.width||y<0||y>=cvs.height){return;}// 获取当前像素颜色constcurColor=getColor(x,y,imgData);// 终止条件1:当前颜色与点击颜色差异过大(不是同一区域)if(diff(clickColor,curColor)>100){return;}// 终止条件2:当前颜色已经是目标颜色if(diff(curColor,targetColor)===0){return;}// 将当前像素颜色修改为目标颜色constindex=point2Index(x,y);imgData.set(targetColor,index);// 递归扩散:向上下左右四个方向继续填充changeColor(x+1,y,targetColor,imgData,clickColor);changeColor(x-1,y,targetColor,imgData,clickColor);changeColor(x,y+1,targetColor,imgData,clickColor);changeColor(x,y-1,targetColor,imgData,clickColor);}/** * 计算两个颜色之间的差异 * @param {Array} color1 - 颜色数组 [R, G, B, A] * @param {Array} color2 - 颜色数组 [R, G, B, A] * @returns {number} 颜色差异值 */functiondiff(color1,color2){returnMath.abs(color1[0]-color2[0])+Math.abs(color1[1]-color2[1])+Math.abs(color1[2]-color2[2])+Math.abs(color1[3]-color2[3]);}/** * 将像素坐标转换为图像数据数组的索引 * @param {number} x - x坐标 * @param {number} y - y坐标 * @returns {number} 数组索引 */functionpoint2Index(x,y){// 每个像素由4个值表示:R, G, B, Areturn(y*cvs.width+x)*4;}/** * 获取指定位置的颜色 * @param {number} x - x坐标 * @param {number} y - y坐标 * @param {Uint8ClampedArray} imgData - 图像像素数据 * @returns {Array} 颜色数组 [R, G, B, A] */functiongetColor(x,y,imgData){constindex=point2Index(x,y);return[imgData[index],// RimgData[index+1],// GimgData[index+2],// BimgData[index+3]// A];}

算法优化

问题分析

上述递归实现在处理大面积区域时可能导致栈溢出,因为递归深度可能非常大。此外,性能也有优化空间。

优化方案:使用队列的迭代算法

/** * 优化版颜色填充算法 - 使用队列迭代实现 */functionchangeColorOptimized(startX,startY,targetColor,imgData,clickColor){// 创建队列存储待处理的像素点constqueue=[];queue.push({x:startX,y:startY});// 记录已访问的像素,避免重复处理constvisited=newSet();while(queue.length>0){const{x,y}=queue.shift();constkey=`${x},${y}`;// 检查边界if(x<0||x>=cvs.width||y<0||y>=cvs.height){continue;}// 检查是否已访问if(visited.has(key)){continue;}visited.add(key);// 获取当前像素颜色constcurColor=getColor(x,y,imgData);// 检查颜色是否匹配if(diff(clickColor,curColor)>100){continue;}if(diff(curColor,targetColor)===0){continue;}// 修改颜色constindex=point2Index(x,y);imgData.set(targetColor,index);// 将相邻像素加入队列queue.push({x:x+1,y});queue.push({x:x-1,y});queue.push({x,y:y+1});queue.push({x,y:y-1});}}

颜色过度不自然

前面采用的颜色比对算法,比较简单粗暴,color1和color2的rgba进行相减取绝对值,但是这种遇到极端情况下,效果不好。
这里比较科学的算法是实用欧几里得距离也就是简单的勾股定理

functiondiff(color1,color2){// 计算RGB差值(不考虑Alpha)constrDiff=color1[0]-color2[0];constgDiff=color1[1]-color2[1];constbDiff=color1[2]-color2[2];// 欧几里得距离returnMath.sqrt(rDiff*rDiff+gDiff*gDiff+bDiff*bDiff);}

总结

至此,我们实现了一个基于Canvas的颜色填充工具,并讲解了:

  1. Canvas图像数据处理的基本原理
  2. 种子填充算法的递归和迭代实现
  3. 性能优化技巧
    这个工具可以作为图像处理应用的基础,进一步扩展可实现更复杂的功能,如图像编辑、滤镜应用等。

注意:实际使用中,建议使用优化版的队列迭代算法,避免递归可能导致的栈溢出问题。同时,对于大型图像,可以考虑分块处理或使用Web Worker进行多线程处理以提高性能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 17:20:49

XHS-Downloader:小红书内容下载的完整新手入门指南

XHS-Downloader&#xff1a;小红书内容下载的完整新手入门指南 【免费下载链接】XHS-Downloader 免费&#xff1b;轻量&#xff1b;开源&#xff0c;基于 AIOHTTP 模块实现的小红书图文/视频作品采集工具 项目地址: https://gitcode.com/gh_mirrors/xh/XHS-Downloader X…

作者头像 李华
网站建设 2026/4/16 10:20:28

如何快速释放Windows C盘空间:WindowsCleaner终极指南

如何快速释放Windows C盘空间&#xff1a;WindowsCleaner终极指南 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服&#xff01; 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 当你的Windows电脑C盘亮起红色警告&#xff0c;…

作者头像 李华
网站建设 2026/4/15 21:10:02

TranslucentTB 完整使用指南:Windows 透明任务栏美化终极教程

TranslucentTB 完整使用指南&#xff1a;Windows 透明任务栏美化终极教程 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 想要让您的 Win…

作者头像 李华
网站建设 2026/4/16 9:57:22

原神60帧限制终极突破指南:简单三步实现高帧率游戏体验

原神60帧限制终极突破指南&#xff1a;简单三步实现高帧率游戏体验 【免费下载链接】genshin-fps-unlock unlocks the 60 fps cap 项目地址: https://gitcode.com/gh_mirrors/ge/genshin-fps-unlock 你是否曾经在原神中感受到画面卡顿&#xff0c;明明拥有强大的硬件却只…

作者头像 李华
网站建设 2026/4/16 11:08:59

小红书数据采集框架完全手册

小红书数据采集框架完全手册 【免费下载链接】xhs 基于小红书 Web 端进行的请求封装。https://reajason.github.io/xhs/ 项目地址: https://gitcode.com/gh_mirrors/xh/xhs 在小红书内容生态快速发展的当下&#xff0c;掌握高效数据采集方法成为内容创作者和数据分析师的…

作者头像 李华
网站建设 2026/4/16 12:04:47

VMware解锁工具Unlocker 3.0:完美运行macOS虚拟机的终极指南

想要在VMware虚拟机中体验macOS系统却屡屡碰壁&#xff1f;Unlocker 3.0正是解决这一难题的开源工具&#xff0c;它能一键解除VMware对macOS的限制&#xff0c;让普通电脑也能流畅运行苹果操作系统。本文详细解析这款工具的核心功能、安装步骤及实用技巧&#xff0c;帮助用户快…

作者头像 李华