JavaScript 不断改变 SVG 元素的颜色 - 闪烁



我有一个元素,我正在用计时器改变它的颜色:

r = 0.05;
delta = 0.05;
function changecol(){
if (r < 0.05 || r > 0.95) delta = delta * (-1);
r += delta;
col = '#'+(Math.floor(r*255)).toString(16)+(Math.floor(r*255)).toString(16)+(Math.floor(r*255)).toString(16);
svg_elem.style.fill = col;
setTimeout("changecol()", 50);
}

所以颜色从白色变成黑色,然后回来,r*255保持它在 00 和 FF 之间,一切都很顺利,但是当它变成黑色时,它会闪烁到白色并开始基本上上升。我是否错过了计算中的一些错误?

这是一个带有演示的 jFiddle:https://jsfiddle.net/b4f58bz2/

我在 jsfiddle 上运行了一个 console.debug,我发现(Math.floor(r*255)).toString(16)闪烁时的结果是十六进制中的"C",就在"0"之前。问题是,当转换为css颜色时,它是:#ccc等于#cccccc这是一种非常浅的颜色而不是深色。如果(Math.floor(r*255)).toString(16)的长度小于 2,则必须用前导 0 填充 的结果,例如:

color = (Math.floor(r*255)).toString(16);
if (color.length < 2) {
color = "0" + color;
}

然后只需制作:

col = '#' + color + color + color;

我希望这能让你走上正确的轨道。

要从黑色更改为白色,您可以简单地使用饱和度为 0% 的 HSL 的亮度部分,并将其作为颜色参数:

setTimeout()替换为requestAnimationFrame()也将使动画更加流畅,因为这将同步到监视器vblank更新。只需通过将增量降低到大约一半来补偿。

使用div 的示例

var l = 5;
var delta = 2.5;
(function changecol() {
if (l < 5 || l > 95) delta = -delta;
l += delta;
d.style.backgroundColor = "hsl(0,0%," + l + "%)";
requestAnimationFrame(changecol);
})();
<div id=d>Ping-pong fade</div>

这花了一点时间。

问题是你对 toString(16) 的转换,你没有考虑你的十六进制代码太短的可能性。

R = hex.length == 1 ? "0" + hex : hex;

请看:

r = 0.05;
delta = 0.05;
function changecol() {
if (r < 0.05 || r > 0.95) delta = delta * (-1);
r += delta;
hex = (Math.floor(r * 255)).toString(16);
R = hex.length == 1 ? "0" + hex : hex;
col = '#' + R + R + R;
document.getElementById('test').style.backgroundColor = col;
setTimeout("changecol()", 300);
}
document.addEventListener('DOMContentLoaded', changecol, false);
<div id="test">
ADFASDF
</div>
<div id="test1">
ADFASDF
</div>

另一张海报已经提到了您在编码小值方面的错误,因此例如#0C0C0C被编码为#CCC实际上表示#CCCCCC

除此之外,您的代码还有一些不良做法。

首先:使用局部变量! 有些可能是"需要的"全局变量,但col根本没有理由成为全局变量。那只是环境污染。

下一篇:将函数传递给 setTimeout,而不是必须解析的字符串。

setTimeout(changecol, 50);

它更快,更干净,可以封装,可以缩小,...只有好处。

然后,通过一个小技巧,您可以重写颜色编码以使其更好:

var c = 255 * r;
var col = "#" + (0x1000000 | c << 16 | c << 8 | c).toString(16).substr(1);

0x1000000是填充的,因此值始终超过 6 位,后跟三个颜色通道(R、G、B)。

该值被转换为十六进制值,在填充的前面有一个 1,然后由 substr(1) 删除。因此,我们总是有 6 个十六进制数字,每个颜色通道 2 个,不用担心前导零之类的。

位运算还剥离了c可能拥有的所有小数位。

并且不要使用身体负荷来启动这件事。如果你真的必须这样做,请使用jQuery($(changecol))或搜索DOMContentLoaded-implementation,...或者只是将脚本放在正文的末尾,并知道因此在所有 HTML 被解析并构建 dom 后执行。


但是,我们可以做得更好。我们可以使这个东西成为时间的函数,而不是增加和减少值,并给它一个 5% 的填充,这样你就不会越过边界,......

var startTime = Date.now();
var speed = 255 / 1000;   //255 shades of gray / 1000ms
//this means every 2 seconds this animation completes 
//a full cycle from black to white to black again.
//or you slow the speed down to `2*255 / 30000` for example
//1cycle per 30seconds
function changecol(){
//first we calculate the passed time since the start (in ms) 
//and then we multiply it by the speed by wich the value should change
//the amount of color-steps per ms
var t = (Date.now() - startTime) * speed;
//now we get the fraction of 256 wich defines the shade
/*
var c = Math.floor(t % 256);
*/
//since we're dealing with powers of 2 we can use a bit-mask to extract just 
//the last 8 bit of the value and do basically the same, including the Math.floor()
var c = t & 0xFF;
//every other interval we have to change the direction, so that we fade back from white to black
//and don't start over from 0 to 255 (black to white)
/*
var interval = Math.floor(t / 256);  //get the interval
if(interval % 2 === 1)               //check wether it's odd
c = 255 - c;  //revert the direction
*/
//but again we can do easyer by simply checking the 9th bit. 
//if it's a 1, this is an odd interval. the 9th bit equals 0x100 (256).
if(t & 0x100) //mask only the 9th bit. returns either 256 (wich is true) or 0 (false)
c = 0xFF - c;  //revert the direction
var col = "#" + (0x1000000 | c << 16 | c << 8 | c).toString(16).substr(1);
test.style.color = col;
//using animation-frames to stay in sync with the rest of animation in the browser.
requestAnimationFrame(changecol);
}
changecol();  //start this thing, don't use body-onload

你可以做这样的事情,这样你就不必尝试找出正确的十六进制代码。

var whatColor = true;
fadeColor = function() {
var color = whatColor ? 'white' : 'black';
$('svg circle').css({fill: color});
whatColor = !whatColor;
setTimeout(function() {
fadeColor();
}, 1000);

}
fadeColor();
svg circle {
-webkit-transition: fill 1s;
transition: fill 1s;
fill: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg class="mySVG" height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3"  />
</svg>

最新更新