JavaScript实现图片opacity递增仅执行一次的原因排查
问题复现
预期效果
每10毫秒将id为img的图片opacity属性提升0.01,直至透明度为1,初始实现代码如下:
var t; var frontimage=document.getElementById('img'); frontimage.style.top=200+'px'; frontimage.style.opacity=0; frontimage.style.left=200+'px'; frontimage.style.width=500+'px'; frontimage.style.height=300+'px'; function right(){ if(frontimage.style.opacity>=0 && frontimage.style.opacity<1){ frontimage.style.opacity=frontimage.style.opacity+0.01; console.log(frontimage.style.opacity); }else{ window.clearInterval(t); } } t= window.setInterval(right,10);
实际异常表现
- 递增逻辑仅生效1次,控制台固定输出
0.01,透明度不再继续升高 - if判断块始终可以进入,
console.log随定时器周期正常触发 - 把逻辑改为从1开始每次递减0.01到0时,代码完全正常运行,递减版本代码如下:
var frontimage=document.getElementById('img'); frontimage.style.top=200+'px'; frontimage.style.opacity=1; frontimage.style.left=200+'px'; frontimage.style.width=500+'px'; frontimage.style.height=300+'px'; function right(){ if(frontimage.style.opacity>0 && frontimage.style.opacity<=1){ frontimage.style.opacity=frontimage.style.opacity-0.01; console.log(frontimage.style.opacity); }else{ window.clearInterval(t); } } t= window.setInterval(right,10);
根本原因
问题核心来自两个JS和DOM交互的固有规则,和浮点数精度无关:
- DOM style对象的属性值永远是字符串类型:哪怕你给
element.style.opacity赋值数字类型,浏览器内部也会把它转成符合CSS规范的字符串存储,读取时拿到的一定是字符串。 - JS运算符的重载规则差异:
- 加法运算符
+存在字符串拼接重载:只要任意一侧操作数是字符串,就会直接拼接内容,不会做数值加法 - 减法
-、乘法*、除法/没有字符串重载:执行时会强制把两侧操作数转为数值类型再做算术运算
- 加法运算符
顺着规则走一遍递增逻辑的执行流程就能定位问题:
- 初始给
opacity赋值数字0,浏览器自动转成字符串"0"存储 - 第一次定时器触发:
- if判断时字符串
"0"和数字比较,被隐式转为数字0,条件成立 - 执行
"0" + 0.01时触发字符串拼接,得到结果"00.01" - 把
"00.01"赋值给opacity,浏览器可以解析这个格式(多了前导0不影响数值识别),自动转成合法CSS值"0.01"存储,所以第一次控制台输出0.01
- if判断时字符串
- 第二次及后续定时器触发:
- if判断时字符串
"0.01"被隐式转为数字0.01,始终满足<1的条件,所以if块每次都能进入,console.log正常触发 - 执行
"0.01" + 0.01时再次触发字符串拼接,得到结果"0.010.01"——这个值带两个小数点,是完全非法的CSS数值格式 - 浏览器遇到非法CSS属性值会直接丢弃赋值操作,
opacity会一直保留上一次的合法值"0.01",所以透明度永远不会继续升高
- if判断时字符串
再看递减逻辑为什么正常:
- 每次执行减法时,不管读取到的
opacity是啥字符串,都会被强制转成数字再计算,永远不会出现字符串拼接出非法值的情况 - 计算得到的数字赋值给
opacity后,浏览器再转成合法字符串存储,全程值都是合法的,所以可以正常递减到0
修复方案
做加法前手动把读取到的opacity转为数字即可,比如用一元加号转类型:
frontimage.style.opacity = +frontimage.style.opacity + 0.01;
内容的提问来源于stack exchange,提问作者Imash Eshan
相关产品推荐
相关产品推荐

