【问题标题】:How can I restart a CSS transition as soon as it ends using standard JavaScript?使用标准 JavaScript 结束后,如何重新启动 CSS 过渡?
【发布时间】:2020-03-28 23:37:15
【问题描述】:

我已经构建了一种密码生成器,它应该在倒计时到期时显示一个新密码。不幸的是,我只设法弄清楚如何运行我的代码一次。倒计时由一个简单的 CSS 过渡组成,我想保留它,因为它比我尝试使用 JavaScript 反复更新宽度的其他尝试平滑得多。

var dictionary = {
	"adverbs": [
  	"always",
  	"usually",
    "probably"
  ],
  "adjectives": [
  	"useful",
  	"popular",
   	"accurate"
  ],
  "nouns": [
  	"computer",
    "software",
    "terminal"
  ]
};

function capitalizeFirst(string) {
	return string.charAt(0).toUpperCase() + string.slice(1);
}

function randomIndex(object) {
	return object[Math.floor(Math.random() * object.length)];
}

function generatePassword() {
	var category = ["adverbs", "adjectives", "nouns"];
	var password = [];
  
  for (i = 0; i < category.length; i++) {
  	password.push(capitalizeFirst(randomIndex(dictionary[category[i]])));
  }
  
  password.push(Math.floor(Math.random() * 8999) + 1000);
  return password.join("");
}

function updatePassword() {
	document.getElementById("countdown-fill").style.width = 100 + '%';
	document.getElementById("text-field").value = generatePassword();
	document.getElementById("countdown-fill").style.width = 0 + '%';
}

setInterval(updatePassword, 5000);
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');

body {
  margin: 0;
  width: 100%;
  height: 100%;
  background-color: #f8f8f8;
}

.container {
  max-width: 400px;
  margin: 0 auto;
}

#text-field {
  font-size: 15px;
  font-weight: 400;
  font-family: 'Nunito', sans-serif;
  margin-top: 100px;
  margin-bottom: 10px;
  width: 100%;
  height: 30px;
  padding: 10px;
  box-sizing: border-box;
  border: 1px solid  #e5e5e5;
  background-color: #ffffff;
}

#countdown-background {
  width: 100%;
  height: 10px;
  box-sizing: border-box;
  border: 1px solid  #e5e5e5;
  background-color: #ffffff;
}
#countdown-fill {
  width: 100%;
  height: 100%;
  transition: width 5s;
  transition-timing-function: linear;
  background-color: #1e87f0;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Password Generator</title>
  </head>
  <body>
    <div class="container">
      <input id="text-field" type="text" spellcheck="false">
      <div id="countdown-background">
        <div id="countdown-fill"></div>
      </div>
    </div>
  </body>
</html>

目前,我的代码有两个明显的问题:

  1. 由于setInterval,转换变得延迟。如果我只是单独调用updatePassword,情况并非如此。
  2. CSS 过渡只动画一次。我想在每次调用updatePassword 时重置动画。

我遇到了一些 jQuery 解决方案来解决我的问题,但我对这些解决方案不是很感兴趣,因为我想尽可能地依赖标准 JavaScript。不过,我可以使用关键帧等替代 CSS 工具,它们似乎运行良好:

#countdown-fill {
  width: 100%;
  height: 100%;
  animation: refresh 5s infinite;
  background-color: #1e87f0;
}

@keyframes refresh {
    from {
        width: 100%;
    }
    to {
        width: 0;
    }
}

不过,我确实担心同步问题,因为动画与 updatePassword 没有任何关联。

问题:有没有办法让updatePassword在我每次调用该函数时重置动画,并消除初始延迟?

JSFiddle: https://jsfiddle.net/MajesticPixel/fxkng013/

【问题讨论】:

    标签: javascript css


    【解决方案1】:

    我修改了你的JSFiddle,下面是解释。

    #countdown-fill {
      width: 100%;
      height: 100%;
      transform-origin: left;
      background-color: #1e87f0;
    }
    .reset {
      transition: transform 5s linear;
      transform: scaleX(0);
    }
    

    诀窍是将转换绑定到一个类,当您想重置它时,只需删除该类(将转换重置为初始状态)并再次添加它(重新启动它)。

    但是有一些陷阱:最重要的是即时删除和添加类将由浏览器优化,它只会合并操作,根本不会发生任何转换。诀窍是将调用包装在嵌套的 rAF 调用中,这将强制浏览器执行、渲染,然后再次执行。

    window.requestAnimationFrame(function() {
      document.getElementById("countdown-fill").classList.remove('reset');   
      window.requestAnimationFrame(function() {
        document.getElementById("countdown-fill").classList.add('reset');
      });
    });
    

    第二个与过渡有关:优化浏览器渲染,避免像宽度或高度这样的过渡属性,并尝试限制变换和不透明度。我已将您的宽度过渡更改为变换过渡:效果相同,性能更高。

    【讨论】:

    • 谢谢 NevNein!这正是我所追求的:)
    【解决方案2】:

    我赞同 NevNein 发布的内容,并且还想补充一点,如果您想将转换与 updatePassword 耦合,以便它们具有链接关系而不仅仅是匹配超时,您应该将 setInterval(updatePassword, 5000) 替换为:

    updatePassword();
    document.getElementById('countdown-fill').addEventListener("transitionend", updatePassword)
    

    倒计时和密码更改现在将以您在 CSS 中设置的任何速度运行。

    【讨论】:

    • 嘿,劳伦斯。我试图听从你的建议,但我无法让它发挥作用。你的意思是我应该在#countdown-fill 的样式中指定transition-duration: 5s; 吗?让我知道我在哪里搞砸了,因为你的解决方案听起来很有趣。谢谢。
    • 我应该添加的,您还需要自己调用updatePassword() 来启动链。我自己在 JSFiddle 上对此进行了测试,效果很好。保留 Nev 工作版本中的过渡持续时间。
    猜你喜欢
    • 2016-02-23
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多