canvas实现动态替换人物的背景颜色
起因
今天遇见一个特别有意思的小功能。
就是更换人物图像的背景颜色。
大致操作步骤就是:点击人物-实现背景颜色发生变化
将图片绘画到canvas画布上
我们需要将图片绘制到canvas画布上。
这样做的目的是为了方便我们去操作像素点来更改颜色。
首先创建 Image 的实例。将图片的地址赋值给图片实例src。
当图片加载完成后,onload 事件可以知道图片是否加载完成
根据 Image的实例将图片大小赋值给画布,让他们大小保持一致。
最后使用 ctx.drawImage来进行绘画就行
特别提醒的是:src 属性一定要写到 onload 的后面,否则程序在 IE 中会出错。
<body>
<canvas id="canvas">
</body>
<script type="text/javascript">
// 获取dom节点
const canvas = document.getElementById('canvas')
//获取上下文
const ctx = canvas.getContext('2d');
// 将图片绘制到canvas画布上
function initPic(picInfo){
// 创建一个图片的实例
const img = new Image()
// 引入图片的地址
img.src = picInfo.url
img.onload =()=>{
// 设置画布的宽高与图片的保持一致
canvas.width= img.width
canvas.height= img.height
// 开始绘画
ctx.drawImage(img, 0, 0 );
}
}
initPic({
url: './src/assets/person.png'
})
</script>

drawImage 的简单介绍
canvas的drawImage()提供了更多在canvas上绘制图像的方法。
drawImage() 方法有三种形式:
drawImage(image, dx, dy) 在指定的 (dx, dy) 位置绘制图像。
drawImage(image, dx, dy, width, height) 在指定的 (dx, dy) 位置,并使用指定的宽度和高度绘制图像。
drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, dx, dy, width, height) 在指定的 (dx, dy) 位置,并使用指定的宽度和高度绘制图像。图像的源坐标和尺寸也指定了。
image:允许任何的画布图像源
注册事件获取点击时的坐标对应的颜色
我们通过 e.offsetX, e.offsetY 可以轻松拿到点击的坐标x,y。
可以通过 getImageData 获取到图片的所有像素点的颜色。
但是怎么通过点击的位置(x,y)获取到对应的的像素索引呢?
其实他们的关系是这样的:
// 每个像素占用4个字节(RGBA)
const index = (y * image.width + x) * 4;
根据上面这个公式,我们可以知道坐标对应的像素索引。
有了索引,我们可以拿到坐标对应的颜色
function clickMy(e){
// 获取点击时的坐标
let x = e.offsetX
let y = e.offsetY
// 获取所有的像素点颜色
let imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
console.log('获取所有的像素点颜色', imagedata)
// 这个坐标点对应的颜色
let clickColor = getColor(x,y, imagedata)
console.log('这个坐标点对应的颜色', clickColor)
}
// 计算点击坐标对应的像素索引
function bgIndex(x,y){
return (y * canvas.width + x) * 4;
}
// 根据索引得到颜色值
function getColor(x,y,imgData){
let i = bgIndex(x,y)
return [
imgData.data[i],
imgData.data[i+1],
imgData.data[i+2],
imgData.data[i+3]
]
}
// 注册事件
canvas.addEventListener("click", clickMy, false)

更改当前像素点的颜色
现在我们希望点击的这个点的颜色变成红色。
现在的我们可以拿到所有像素点,当前的坐标,坐标对应的颜色。
现在我们的主角出场了(此时灯光闪烁,五彩的光打在他的身上)
context.putImageData(imageData, x, y);
第1个参数:imageData: 包含了图像的所有像素数据,
通过ctx.getImageData(0, 0, canvas.width, canvas.height)可以获取到;
第2,3个参数表示坐标。
它用于将图像数据绘制到画布上。
这个方法允许开发者操作和绘制像素级别的数据,
从而实现复杂的图像效果和处理。
function clickMy(e){
// 获取点击时的坐标
let x =e.offsetX
let y = e.offsetY
// 获取所有的像素点颜色
let imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
console.log('获取所有的像素点颜色', imagedata)
// 这个坐标点对应的颜色
let clickColor = getColor(x,y, imagedata)
console.log('这个坐标点对应的颜色', clickColor)
// 最后更改为红色的rgba值
let targetBgArr = [255,0,0,255]
// 更改颜色
function changeColor(x,y){
let i = bgIndex(x,y)
imagedata.data.set(targetBgArr, i)
}
changeColor(x,y)
// 更改当前像素点的颜色
ctx.putImageData(imagedata, 0, 0);
}

将被点击的点的相似颜色全部变为红色
我们通过两个颜色的rgba值相减,看rgba的各个绝对值之和。
来判断颜色的相似。
同时我页需要注意边界范围与颜色已经变为了目标颜色。
这个时候我们就需要停止调用函数了
核心代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="canvas">
</body>
<script type="text/javascript">
// 获取dom节点
const canvas = document.getElementById('canvas')
// 获取上下文
const ctx = canvas.getContext('2d',{
willReadFrequently:true
});
function initPic(picInfo){
// 创建一个图表的实例
const img = new Image()
img.onload =()=>{
// 设置画布的宽高与图片的保持一致
canvas.width= img.width
canvas.height= img.height
// 开始绘画
ctx.drawImage(img, 0, 0 );
}
// 引入图片的地址
img.src = picInfo.url
}
function clickMy(e){
// 获取点击时的坐标
let x =e.offsetX
let y = e.offsetY
// 获取所有的像素点颜色
let imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
console.log('获取所有的像素点颜色', imagedata)
// 这个坐标点对应的颜色
let clickColor = getColor(x,y, imagedata)
console.log('这个坐标点对应的颜色', clickColor)
// 最后更改为红色的rgba值
let targetBgArr = [255,0,0,255]
function changeColor(x,y){
// 边界范围
if(x<0 || x>canvas.width || y<0 || y>canvas.height){
return
}
let color = getColor(x,y,imagedata )
// 相似颜色的相差值
if(diffBg(color,clickColor)>150){
return
}
// 已经变为了目标色(红色)
if(diffBg(color,targetBgArr)==0){
return
}
let i = bgIndex(x,y)
// 在内存中更改像素的颜色
imagedata.data.set(targetBgArr, i)
// 改变周围(上下左右)的颜色
changeColor(x+1,y)
changeColor(x-1,y)
changeColor(x,y+1)
changeColor(x,y-1)
}
changeColor(x,y)
// 将内存中的像素点的颜色(重新绘制在画布上)
ctx.putImageData(imagedata, 0, 0);
}
// 计算点击坐标对应的像素索引
function bgIndex(x,y){
return (y * canvas.width + x) * 4;
}
// 根据索引得到颜色值
function getColor(x,y,imgData){
let i = bgIndex(x,y)
return [
imgData.data[i],
imgData.data[i+1],
imgData.data[i+2],
imgData.data[i+3]
]
}
// 查看两个颜色的相差值
function diffBg(color1,color2){
// 我们是取两个颜色的绝对值相加
return Math.abs(color1[0] -color2[0]) +
Math.abs(color1[1] -color2[1]) +
Math.abs(color1[2] -color2[2]) +
Math.abs(color1[3] -color2[3])
}
// 注册事件
canvas.addEventListener("click", clickMy, false)
initPic({
url: '../assets/person1.png'
})
</script>
</html>

更改为按钮,背景发生改变
上面我们实现了,点击背景色,实现颜色的更改。
但是实际的过程中。
我们是不知道背景颜色的,怎么去确认背景颜色呢?
其实,可以默认坐标为(4,4)是背景颜色。
在实际的过程中,其实这个位置99.9999%是背景色。
我们是先选择颜色,然后点击确定,实现颜色的更改
现在我们来优化一下。让用户自己选择背景色,选择好后。
点击确定,背景颜色就发生变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
<div>
<canvas id="canvas">
</div>
<input type="color" id="color" >
<button id="red">确定</button>
</body>
<script type="text/javascript">
// 获取dom节点
const canvas = document.getElementById('canvas')
// 获取上下文
const ctx = canvas.getContext('2d',{
willReadFrequently:true
});
// 将16进制转化为rgba的颜色值
function changeRGBA(hex) {
// 去除 # 开头的第一个字符
hex = hex.slice(1);
// 将16进制字符串转换rgba
let rgba = [];
for (let i = 0; i < 6; i += 2) {
let byte = parseInt(hex.substr(i, 2), 16);
rgba.push(byte);
}
// 添加 alpha 通道
rgba.push(255);
// 返回 RGBA 颜色值
return rgba;
}
function initPic(picInfo){
// 创建一个图表的实例
const img = new Image()
img.onload =()=>{
// 设置画布的宽高与图片的保持一致
canvas.width= img.width
canvas.height= img.height
// 开始绘画
ctx.drawImage(img, 0, 0 );
}
// 引入图片的地址
img.src = picInfo.url
}
function clickMy(e, type){
let color = document.getElementById('color')
// 4,4的地方默认为是背景颜色
let x = 4
let y = 4
// 获取所有的像素点颜色
let imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 这个坐标点对应的颜色
let clickColor = getColor(x,y, imagedata)
console.log('这个坐标点对应的颜色', clickColor)
// 颜色为用户选择的值
let targetBgArr = changeRGBA(color.value)
function changeColor(x,y){
// 边界范围
if(x<0 || x>canvas.width || y<0 || y>canvas.height){
return
}
let color = getColor(x,y,imagedata )
// 相似颜色的相差值
if(diffBg(color,clickColor)>150){
return
}
// 已经变为了目标色(红色)
if(diffBg(color,targetBgArr)==0){
return
}
let i = bgIndex(x,y)
// 在内存中更改像素的颜色
imagedata.data.set(targetBgArr, i)
// 改变周围(上下左右)的颜色
changeColor(x+1,y)
changeColor(x-1,y)
changeColor(x,y+1)
changeColor(x,y-1)
}
changeColor(x,y)
// 将内存中的像素点的颜色(重新绘制在画布上)
ctx.putImageData(imagedata, 0, 0);
}
// 计算点击坐标对应的像素索引
function bgIndex(x,y){
return (y * canvas.width + x) * 4;
}
// 根据索引得到颜色值
function getColor(x,y,imgData){
let i = bgIndex(x,y)
return [
imgData.data[i],
imgData.data[i+1],
imgData.data[i+2],
imgData.data[i+3]
]
}
// 查看两个颜色的相差值
function diffBg(color1,color2){
// 我们是取两个颜色的绝对值相加
return Math.abs(color1[0] -color2[0]) +
Math.abs(color1[1] -color2[1]) +
Math.abs(color1[2] -color2[2]) +
Math.abs(color1[3] -color2[3])
}
// 注册事件
canvas.addEventListener("click", clickMy, false)
red.addEventListener("click", clickMy, false)
initPic({
url: '../assets/person1.png'
})
</script>
</html>

最后的功能-下载
上面我们已经成功实现让用户选择颜色。
更换用户自己选择的颜色。
下载我们只需要实现下载功能就好了。
下载功能主要使用 canvas.toDataURL
然后利用a标签进行下载
<button id="down">下载</button>
down.addEventListener('click',()=>{
let imgURL = canvas.toDataURL({format: "image/png", quality:1, width:canvas.width, height:canvas.height});
let link = document.createElement('a');
link.download = "人物图片";
link.href = imgURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})

canvas实现动态替换人物的背景颜色的更多相关文章
- Android使用属性动画ValueAnimator动态改变SurfaceView的背景颜色
以下是主要代码,难点和疑问点都写在注释中: /** * 开始背景动画(此处为属性动画) */ private void startBackgroundAnimator(){ /* *参数解释: *ta ...
- iOS平台设置系统状态栏(通知栏、顶部状态栏)样式背景颜色或透明
5+App开发 状态栏 配置系统状态栏样式 iOS平台可支持对系统状态栏样式的配置,在应用manifest.json文件的plus->distribute->apple下添加UIStatu ...
- Atitit 动态按钮图片背景颜色与文字组合解决方案
Atitit 动态按钮图片背景颜色与文字组合解决方案 转换背景颜色,setFont("cywe_img", fontScale, 50, 5) 设置文字大小与坐标 文字分拆,使用字 ...
- Javascript技巧实例精选(1)—鼠标选择动态改变网页背景颜色
>>点击这里下载html源文件代码<< 采用Javascript实现,用鼠标点击相应颜色,动态改变网页背景颜色 这是截图 相应的Javascript源代码为: var hex ...
- 【C#/WPF】Button按钮动态设置Background背景颜色
学习笔记: 在XAML中给Button设置颜色大家都懂的,本篇只是记录用C#代码动态生成的按钮设置Background背景颜色. new一个Button,设置Background时可看到该属性类型是S ...
- EasyUI/TopJUI之如何动态改变下拉列表框ComboBox输入框的背景颜色
简单记录一下 前段时间接到客户需求:动态改变下拉列表框ComboBox输入框的背景颜色. 刚开始想的很简单在用户选择列表项的时候,判断一下列表框的value值添加相应的背景颜色就OK了,然而在实际操作 ...
- 【VS开发】VS2010 MFC中控件、对话框等背景颜色动态修改的方法
[VS开发]VS2010 MFC中控件.对话框等背景颜色动态修改的方法 标签(空格分隔):[VS开发] 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明: ...
- CSS 之动态变换背景颜色
先上效果图 HTML代码: 123456789 <div class="header"> <h1>GCCHRN'S BLOG</h1> < ...
- jquery动态改变背景颜色插件
GETHUB下载地址 背景颜色用animate方法时时无法改变颜色的 所以要使用插件进行补充. 用法: <!DOCTYPE html> <html> <head> ...
- JavaScript实例技巧精选(14)—动态变化背景颜色
>>点击这里下载完整html源码<< 这是截图: 网页背景颜色随时间变化,核心代码如下: <SCRIPT LANGUAGE="JavaScript"& ...
随机推荐
- 2023-07-13 C#深拷贝功能以及推荐使用方式
C#深拷贝功能以及推荐使用方式 [作者]长生 深拷贝 深拷贝是用于在对引用对象进行复制时的一种操作方式.平常我们新建一个对象,然后直接赋值,只是对地址引用的赋值,在修改新建的对象时,也会对我们复制的对 ...
- python将两个列表组合成元组
point_x = [A_x, B_x, C_x, D_x] point_y = [A_y, B_y, C_y, D_y] points_tulpe = list(zip(point_x, point ...
- 检测文件的格式——chardet模块
f3 = open(file=path,mode='rb') data = f3.read() # print(data) f3.close() result = chardet.detect(dat ...
- [python]使用diagrams绘制架构图
简介 diagrams是python的一个第三方库,用于实现使用代码绘制架构图. 安装 依赖于 Graphviz,安装diagrams之前需要先安装 Graphviz(下载压缩包后,将bin目录添加到 ...
- [ansible]简介安装入门
简介 ansible是一种运维自动化工具,默认通过ssh协议管理机器.只需要在一台机器上安装好,就可以通过这台电脑管理一组远程的机器.而被管理的linux机器只要有python环境,就不需要额外安装a ...
- VMware中的虚拟机Debian10的服务器配置,使主机(win10)能够通过本地域名(如www.xxx.com)访问该服务器
VMware中的虚拟机Debian10的服务器配置,使主机(win10)能够通过本地域名(如www.xxx.com)访问该服务器 安装过程 下载debian-10.13.0-amd64-DVD-1.i ...
- 2023-08-14:用go语言写算法。给出两个长度相同的字符串 str1 和 str2 请你帮忙判断字符串 str1 能不能在 零次 或 多次 转化 后变成字符串 str2 每一次转化时,你可以将
2023-08-14:用go语言写算法.给出两个长度相同的字符串 str1 和 str2, 请你帮忙判断字符串 str1 能不能在 零次 或 多次 转化 后变成字符串 str2, 每一次转化时,你可以 ...
- Java内存溢出时,还能正常处理请求吗?
当你被问到"当Java程序发生内存溢出时,进程还能正常处理请求吗?"这样的面试题,会不会很懵?这里分享一次网友车辙在当初刚毕业那几年,意义风发,总觉得天下没有自己不会的面试题.然后 ...
- Minio 安装部署
minio 入门 简介 简介 自己看官网 https://min.io/docs/minio/kubernetes/upstream/ 安装部署 (1)Linux服务器安装 minio 可以采用以下几 ...
- shiro框架基本概念介绍
什么是Shiro: Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证.授权.加密和会话管理 Shiro的核心功能包括: 身份验证(Authentication):验证用户的身份,确保用户 ...