如何在 JS 上的累积过程中使用 CSS 转换? (代码突然不起作用)

How to use CSS transition during accumulation on JS? (Code suddenly doesn't work)

我一直使用此代码来移动特定元素,并将 value 的 CSS 属性 (例如:left)与 [=16 相加=] 同时。我在 2~3 天前检查过这段代码是否有效,但今天突然,transition 由于未知原因不再起作用。

这是代码:

'use strict';
class Assemble {
    constructor(elem, acc, name, unit) {
        this.elem = document.querySelector(elem);
        this.acc = acc;
        this.name= name;
        this.unit = unit;
    }
    calc() {
        let displayOn = this.elem.style.display;
        this.elem.style.display = 'none';
        let currentProp = window.getComputedStyle(this.elem, null).getPropertyValue(this.name),
            S2N = parseInt(currentProp, 10);
        this.elem.style.display = displayOn;
        this.stock(S2N);
    }
    stock(value) {
        let digit = (value * 1) + this.acc;
        this.elem.style.left = digit + this.unit;
        console.log(this.elem.style.left);
    }
}
let proAssemble = new Assemble('.button', 10, 'left', '%');
setInterval(() => {
    proAssemble.calc();
}, 1500)
* {
    margin: 0;
    padding: 0;
}
.button {
    position: absolute;
    width: 200px;
    left: 10%;
    background-color: purple;
    transition: left 1000ms;
}
<div class="button">
    test
</div>

算法很简单:

  1. 关闭 element's display 以获得其电流 CSS 属性。 (必须)

  2. 将长度单位 (%) 分离为 CSS 属性 值。

  3. 开启 element's display.

  4. 将数字 this.acc 添加到 CSS 属性 值。

  5. 分配给element

为了以防万一,我已经检查了 CSS and Javascript 两个的最新补丁说明,但我不知道哪个导致它不起作用。

任何提示都可以很好地解决这个问题。

谢谢。

这可能是由于内部优化。发生的事情是浏览器正在对样式进行批处理并将它们全部添加到一起,即使您单独添加它们也是如此。这意味着 left 没有被转换。如果您尝试将 any 属性 与 display: none 一起转换为其他内容,也会发生同样的情况。

如果浏览器以前没有进行此优化,他们现在正在进行,这就是您的代码被破坏的原因。

对此有一个简单的解决方案,即 运行 stock 在 setTimeout 的回调中,如下所示:setTimeout(() => this.stock(S2N), 0);。这通过在以后安排更改来选择退出优化(即使它是 0ms,它仍然被添加到队列中并且不会立即执行,因此选择退出)。您可以在下面的代码片段中看到这一点。这是最简单的修复方法。

另一种可能的解决方案是自己计算百分比,而不是设置 display: none 来获取百分比值。据我所知,这种行为没有明确定义,将来可能会改变。您可以这样计算您的左值的百分比:

Math.floor(window.innerWidth / parseInt(currentProp, 10))

您可以在 Assemble 对象实例化期间执行此操作,或者在 class 中创建某种逻辑来检测 unit 是否为百分比。

最后一句警告:转换 left 效率很低。尝试转换 transform: translateX(value) 属性。渲染性能要好很多。

'use strict';
class Assemble {
    constructor(elem, acc, name, unit) {
        this.elem = document.querySelector(elem);
        this.acc = acc;
        this.name= name;
        this.unit = unit;
    }
    calc() {
        let displayOn = this.elem.style.display;
        this.elem.style.display = 'none';
        let currentProp = window.getComputedStyle(this.elem, null).getPropertyValue(this.name),
            S2N = parseInt(currentProp, 10);
        this.elem.style.display = displayOn;
        if (S2N < 100) {
            setTimeout(() => this.stock(S2N), 0);
        }
    }
    stock(value) {
        let digit = (value * 1) + this.acc;
        this.elem.style.left = digit + this.unit;
        this.elem.addEventListener('transitionend', this.calc.bind(this), {once: true})
        console.log(this.elem.style.left);
    }
}
let proAssemble = new Assemble('.button', 10, 'left', '%');
proAssemble.calc();
* {
    margin: 0;
    padding: 0;
}
.button {
    position: absolute;
    width: 200px;
    left: 0;
    background-color: purple;
    transition: left 1000ms;
}
<div class="button">
    test
</div>

我找到了应用@EmanueleFeliziani 的答案的解决方案。但它有点难看。

  1. 将代码隐藏到布尔标志以进行阻塞,直到完成。

  2. setTimeout()'s timer 设置为至少 100。除非,当 CTRL + MWing.

  3. 时,转换将不再起作用

之后就顺畅了

再次感谢您回答@EmanueleFeliziani

最后一件事,我尝试使用 Math.floor 自己计算百分比,但浏览器抛出 infinity。 o.o

'use strict';
class Assemble {
    constructor(elem, acc, name, unit) {
        this.elem = document.querySelector(elem);
        this.acc = acc;
        this.name= name;
        this.unit = unit;
        this.trigger = true;
        setInterval(() => {
            this.calc();
        }, 1000);
    }

    calc() {
        if (this.trigger) {
            this.trigger = false;
            let displayOn = this.elem.style.display;
            this.elem.style.display = 'none';
            let currentProp = window.getComputedStyle(this.elem, null).getPropertyValue(this.name),
            S2N = parseInt(currentProp, 10);
            this.elem.style.display = displayOn;
            setTimeout(() => this.stock(S2N), 100);
        }
    }
    stock(value) {
        if(!this.trigger) {
            console.log('test');
            let digit = (value * 1) + this.acc;
            this.elem.style.left = digit + this.unit;
            this.trigger = true;
        }
    }
}
let proAssemble = new Assemble('.button', 10, 'left', '%');
* {
    margin: 0;
    padding: 0;
}
.button {
    position: absolute;
    width: 200px;
    left: 0%;
    background-color: purple;
    transition: left 500ms;
}
<div class="button">
    test
</div>