先介绍CSS3动画如何使用?两步即可搞定。

  1. 定义动画规则
@keyframe animationName{  
     timeStamp{  
         attr1:value1;  
         attr2:value2;  
         ...
         attrn,valuen;  
     }  
 }
复制代码

说明:

  • animationName表示动画名称;
  • timeStamp表示时间点,可取值from,to或0-100%,from表示0,to表示100%;
  • 每个时间点可以指定多个样式属性attr与对应值value。
  1. 元素应用动画
elemSelector{  
     animation: animationName animationDuration;   
 }
复制代码

说明:

  • elemSelector表示元素选择器
  • 执行一个动画至少指定动画名称animationName和动画时长animationDuration
  • animation属性是以下属性的简写形式,每个属性的含义说明如下表,

属性名称

含义

animation-name

@keyframe动画的名称

animation-duration

动画的时长,需指定单位如s或ms,默认值为0

animation-timing-function

动画的速度曲线,默认值为ease,其他取值有ease-in、ease-out、ease-in-out、linear、step-start、step-end、贝塞尔曲线函数cubic-bezier、步进函数steps

animation-delay

动画延时多久开始,需指定指定单位如s或ms,默认为0,,取正值表示延时,负值表示超前

animation-iteration-count

动画播放次数,默认为1

animation-direction

动画是否在下一周期逆向播放,默认是normal,其他取值有reverse、alternate、alternate-reverse

animation-play-state

动画的播放状态,是运行还是暂停,默认是running,其他取值有paused

animation-fill-mode

动画执行前、后是否应用目标状态,默认是none,其他取值有forwards、backwards、both

重点来了,一个倒计时效果是如何实现的,先看效果。由于是压缩生成的gif,所以看起来会很快。



下面对主要代码进行说明,

  • HTML部分
<!--用于呈现数字-->
  <div id="number"></div>
  <!--用于重新倒计时-->
  <button class="button">再来一次</button>
   <!--用于最后的声音播放。-->
  <audio ></audio>
复制代码
  • CSS部分
/*初始化数字div样式*/
  #number {
      position: absolute;
      top: 50%;
      left: 50%;
  
      text-align: center;
  
      -webkit-transform: translate3d(-50%, -50%, 0);
      -moz-transform: translate3d(-50%, -50%, 0);
      transform: translate3d(-50%, -50%, 0);
  }
  
  /*绑定动画*/
  .number-anim {
      -webkit-animation: animation_in 10s linear;
      -moz-animation: animation_in 10s linear;
      /*动画时长是10s,且动画的速度是线性的*/
      animation: animation_in 10s linear;
  }
  
  /*定义动画规则*/
  @keyframes animation_in {
      0 {}
      10% {
          /*由于动画时长是10s,此时是1s时刻,字体大小变为100px,z方向距离主屏幕为300px*/
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      11% {
          /*随后字体大小变为300px,z方向距离主屏幕为0,后面每到整秒数都会重复进行*/
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      20% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      21% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      30% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      31% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      40% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      41% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      50% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      51% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      60% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      61% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      70% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      71% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      80% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      81% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      90% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
      91% {
          font-size: 300px;
          -webkit-transform: translate3d(-50%, -50%, 0);
          -moz-transform: translate3d(-50%, -50%, 0);
          transform: translate3d(-50%, -50%, 0);
      }
      100% {
          font-size: 100px;
          -webkit-transform: translate3d(-50%, -50%, -300px);
          -moz-transform: translate3d(-50%, -50%, -300px);
          transform: translate3d(-50%, -50%, -300px);
      }
   }
复制代码
  • JS部分
window.onload = function() {
  
  //第一步:定义全局变量
  num = 10;//数字内容
  isCounting = true;//是否正在计数
  timer = null;//定时器
  numberDiv = document.querySelector("#number");
  audio = document.querySelector("audio");
  button = document.querySelector(".button");

  //第二步:初始化
  init();

  //第三步:开始倒计时
  timer = setInterval(count, 1000);
    
}
复制代码

初始化函数声明如下,

function init() {
      numberDiv.innerHTML = num;
      numberDiv.style.color =  getRandomColor();
      numberDiv.style.fontSize = "300px";
      numberDiv.className = "number-anim";
    
      button.addEventListener("click", function() {
          if (isCounting) {
              return;
          }
          isCounting = true;
          num = 10;
  
          numberDiv.innerHTML = num;
          numberDiv.style.color = getRandomColor();   
          numberDiv.style.fontSize = "300px";
          
          //先解绑,再使用setTimeout使浏览器重新渲染页面,重新绑定
          //setTimeout只是一种方式,只要能使浏览器重新渲染即可      
          numberDiv.className = null;
          setTimeout(function() {
              numberDiv.className = "number-anim";
  
              timer = setInterval(count, 1000);
          }, 30); 
      }, false);
  }
复制代码

倒计数函数声明如下,

function count() {
  
      numberDiv.innerHTML = --num;
      numberDiv.style.color = getRandomColor();
    
      if (num == 0) {
          clearInterval(timer);
  
          audio.src="./audio/readygo.mp3";
          audio.play();
  
          numberDiv.innerHTML = "Ready Go!";
          numberDiv.style.color = getRandomColor();
          numberDiv.style.fontSize = "100px";
  
          numberDiv.style.opacity = 0.8;
          numberDiv.style.filter = "alpha(opacity=80)";
          CompatibleFunc(numberDiv, "Transition", "opacity 1s");
  
          isCounting = false;
      }
  }
复制代码

这里有一个地方需要特别注意,避免以后踩坑。

关于动画样式的解绑与绑定。

动画样式animation一执行结束,就不再起作用了,要使其重新生效,需要重新绑定。我试了4种方式。

  • 第一种是直接解绑,然后再绑定。
numberDiv.className = null;
  numberDiv.className = "number-anim";
复制代码

结果是不能使动画重新生效。

  • 第二种是使用classlist属性进行解绑定。
numberDiv.classList.toggle("number-anim");
  numberDiv.classList.toggle("number-anim");
复制代码

classList返回类名列表对象,调用toggle方法,若类名存在则删除,返回false,若类名不存在则添加,返回true,所以要调用两次,第一次删除类名,第二次添加类名。但是结果依然是不能使动画重新生效。

  • 第三种是开启定时器。
    先解绑,再利用定时器使浏览器重新渲染页面,重新绑定。setTimeout只是一种方式,只要能使浏览器重新渲染即可,最终动画重新生效。
numberDiv.className = null;
  setTimeout(function() {
      numberDiv.className = "number-anim";
  }, 30); 
复制代码

在mozilla官方文档介绍了另一种重新渲染方式,

https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Animations/Tips
复制代码

应用在这里的话,写法是,

numberDiv.className = null;
  window.requestAnimationFrame(function(){
      window.requestAnimationFrame(function(){
          numberDiv.className = "number-anim";
      });
  });
复制代码
  • 第四种是借助其他重新渲染途径,如颜色、内容、大小的变化。
numberDiv.className = null;

  numberDiv.innerHTML = num;
  numberDiv.style.color = getRandomColor();   
  numberDiv.style.fontSize = "300px";
  
  numberDiv.className = "number-anim";
复制代码

结果IE和Microsoft Edge 浏览器是支持的,但是FireFox和Chrome不支持, 但这是不是也说明了Chrome和FireFox已经对浏览器渲染做了优化?

简而言之,要使动画重新生效,需要触发浏览器重新渲染。
好了,附上源码链接。

https:///muzhidong/frontend-demo/tree/master/countdown
复制代码