来源:
https://juejin.im/post/5dfc74666fb9a016266460fb
注意:容差判断条件我修改成了平方根,更接近PhotoShop的选色容差条件。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0">
<title>边界内抠图 | 洪泛法</title>
<style type="text/css">body{background: lime;}</style>
</head>
<body>
<p>容差范围:<input type="number" id="tolerance" min="0" max="255" step="1" value="32" size="4"></p>
<canvas id="canvas"></canvas>
<script type="text/javascript">
let rgbaPicker;
let tolerance = document.getElementById('tolerance');
let canvas = document.querySelector('#canvas');
let context = canvas.getContext('2d');
let img = document.createElement('img');
img.src = 'monkey.jpg';
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
context.drawImage(img, 0, 0);
canvas.addEventListener('click',function(ev){
var x = ev.clientX-ev.target.getBoundingClientRect().left;
var y = ev.clientY-ev.target.getBoundingClientRect().top;
// 取色
rgbaPicker = context.getImageData(x, y, 1, 1).data;
var strHex = '#';
for (var i = 0; i < rgbaPicker.length - 1; i++) {
var hex = rgbaPicker[i].toString(16);
if (hex.length < 2) {
hex = '0' + hex;
}
strHex += hex;
}
console.log(rgbaPicker,strHex);//16进制色值
cutoutFlood(canvas, x, y, rgbaPicker, tolerance.value);
})
}
// 注意这里的方法多了两个参数`startX`与`startY`,代表洪泛的开始坐标。
function cutoutFlood(canvas, startX, startY, color, range = 0) {
let context = canvas.getContext('2d');
let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);
let pixiArr = imageInfo.data;
let stack = [];
function floodFill8(x, y) {
// 8个方向
let dx = [0, 1, 1, 1, 0, -1, -1, -1];
let dy = [-1, -1, 0, 1, 1, 1, 0, -1];
let map = {}; // 标识已经处理过的像素点,防止重复处理
// 如果开始像素符合条件,则将它放入栈中并标识为已处理
let cell = (x + y * canvas.width) * 4;
if(testColor([pixiArr[cell], pixiArr[cell + 1], pixiArr[cell + 2]], color, range)) {
let firstPixi = `x${x}y${y}`; // `x${x}y${y}`是一个不会重复的唯一标识id
map[firstPixi] = true;
stack.push({
x,
y
});
} else return; // 否则直接结束
let p; // position
while (p = stack.pop()) { // 获取栈顶待处理的符合条件的像素的x与y值
cell = (p.x + p.y * canvas.width) * 4;
pixiArr[cell + 3] = 0; //alpha=0
// 测试周围8个是否符合抠除条件
for (let i = 0; i < 8; i++) {
let nx = p.x + dx[i];
let ny = p.y + dy[i];
// 是否在范围内并且没有被处理过
if (nx >= 0 && nx < canvas.width && ny >= 0 && ny < canvas.height && !map[`x${nx}y${ny}`]) {
cell = (nx + ny * canvas.width) * 4;
if (testColor([pixiArr[cell], pixiArr[cell + 1], pixiArr[cell + 2]], color, range)) {
map[`x${nx}y${ny}`] = true; // 标识此像素已被处理
// 没处理过则放入栈中
stack.push({
x: nx,
y: ny
});
}
}
}
}
}
floodFill8(startX, startY);
context.putImageData(imageInfo, 0, 0);
}
function testColor(current, color, range) {
if (Math.sqrt(
(current[0] - color[0]) * (current[0] - color[0]) +
(current[1] - color[1]) * (current[1] - color[1]) +
(current[2] - color[2]) * (current[2] - color[2])) <= range
) {
return true;
}
return false;
}
</script>
</body>
</html>
Comments | NOTHING