以下代码(jsFiddle)在画布上的任意点绘制一个红色正方形,注意擦除前一个(通过在ctx.fillRect()
上填充一个白色正方形:
<html>
<canvas id='canvas' width=300 height=300/>
<script>
const ctx = document.getElementById('canvas').getContext('2d');
let prevRect = null;
for (let i = 0 ; i < 10; i++) {
if (prevRect != null) {
ctx.fillStyle='white';
ctx.fillRect(prevRect.x, prevRect.y, 50, 50);
}
ctx.fillStyle='red';
const newRect = {x: Math.random()*(300-50), y: Math.random()*(300-50)};
ctx.fillRect(newRect.x, newRect.y, 50, 50);
prevRect = newRect;
}
</script>
</html>
该代码无法完全擦除先前的正方形,并且工件残留在屏幕上。相反,如果执行以下操作:
const newRect = {x: Math.floor(Math.random()*(300-50)), y: Math.floor(Math.random()*(300-50))};
...然后一切都会按预期进行。
我的问题是为什么。将值保留在prevRect
中似乎完全没有必要进行截断,因此对fillRect()
的两次调用使用完全相同的坐标(即使使用浮点数),因此两个正方形应始终完美匹配对齐。
答案 0 :(得分:2)
通过采用背景颜色并根据百分比对前景颜色进行插值,可以使前景与背景重叠多少。 现在,当您尝试“删除”它时,您将对背景色执行相同的操作。
所以从数学上来说,像素的颜色是:
var afterRender = interpolate(background, forground, percentage);
var afterDelete = interpolate(afterRender, background, percentage);
让我们处理一些数字:(一个简单而肮脏的例子)
const update = () => {
var bgColor = +bg.value.replace(/^#?/, "0x");
bg.style.backgroundColor = toColor(bgColor);
var fgColor = +fg.value.replace(/^#?/, "0x");
fg.style.backgroundColor = toColor(fgColor);
var percentage = overlap.value / 100;
var afterRenderColor = interpolate(bgColor, fgColor, percentage);
afterRender.textContent = afterRender.style.background = toColor(afterRenderColor);
// now trying to erase this by overwriting with the background-color
var afterDeleteColor = interpolate(afterRenderColor, bgColor, percentage);
afterDelete.textContent = afterDelete.style.background = toColor(afterDeleteColor);
}
const toColor = v => "#" + v.toString(16).padStart(6, 0).toUpperCase();
const interpolate = (a, b, t) => ((a&0xFF0000) * (1-t) + (b&0xFF0000) * t) & 0xFF0000
| ((a&0x00FF00) * (1-t) + (b&0x00FF00) * t) & 0x00FF00
| ((a&0x0000FF) * (1-t) + (b&0x0000FF) * t) & 0x0000FF;
[bg, fg, overlap].forEach(input => input.onchange = input.oninput = update);
update();
#bg,
#fg,
#afterRender,
#afterDelete {
border: 1px solid black;
padding: 20px;
}
<label>Background: <input id="bg" type="text" pattern="/#?[0-9a-f]{6}/" value="#FFFFFF"/></label>
<br>
<label>Foreground: <input id="fg" type="text" pattern="/#?[0-9a-f]{6}/" value="#FF0000"/></label>
<br>
<label>"half a pixel" of overlap: <input id="overlap" type="range" min="0" max="100" value="50"></label>
more or less ;)
<br>
<br>
Color after rendering "half a pixel" of the foreground over the background:
<div id="afterRender"></div>
<br>
Color after trying to erase that by rendering "half a pixel" of the background-color over that:
<div id="afterDelete"></div>
答案 1 :(得分:0)
问题源于在画布上绘制事物的基本方式。绘制正方形时,形状的边缘会稍微“羽化”。因此,当您在前一个红色框上绘制白色框时,红色的残余会渗入半透明边缘。
如果您绘制10个白框而不是一个,那么问题就消失了。或者,如果您将白色框的尺寸扩大了0.5像素,那可能会有所帮助。
const ctx = document.getElementById('canvas').getContext('2d');
let prevRect = null;
for (let i = 0 ; i < 10; i++) {
if (prevRect != null) {
ctx.fillStyle='white';
ctx.fillRect(prevRect.x - 0.75, prevRect.y - 0.75, 51.5, 51.5);
}
ctx.fillStyle='red';
const newRect = {x: Math.random()*(300-50), y: Math.random()*(300-50)};
ctx.fillRect(newRect.x, newRect.y, 50, 50);
prevRect = newRect;
}
body, html { padding: 0; }
canvas { border: 1px solid black; }
<canvas id=canvas height=300 width=300></canvas>
看起来每边都大0.75,效果很好,但是可以肯定,这是画布“逻辑”大小与实际屏幕大小的函数。