Translate

2020年5月30日 星期六

CSS 動畫

第一次看到 CSS 動畫,覺得為之驚豔,這樣做動畫比用 jquery 自己刻快多了,效果又好,然後就因為工作在忙的關係,又停止了學習的腳步。

首先先來一個簡單的範例 :

這裡比較特別的是 cubic-bezier 可以利用這個網頁來計算 cubic-bezier, 四個參數其實是貝滋曲線的兩個控制點座標, 起點和終點為 0,0 和 1,1 , 套在 svg 上就是 <path d="M 0 0 C 參數1 參數2 參數3 參數4 1 1"/>


<style>
.a05301
{
  position:absolute;
  width:1em;
  height:1em;
  background-color:#f00;
  animation-name:a05301_a1; /* 名稱 */
  animation-duration:3000ms; /* 動畫時間 */
  animation-delay:0ms; /* 延遲 */
  animation-iteration-count:infinite;
  /*
      播放次數
      infinite  代表無限次播放
  */
  animation-timing-function:ease;
  /*
      預設 ease
      linear, ease-in, ease-out, ease-in-out
      step-start, step-end, steps(int,start/end), 
      cubic-bezier(n,n,n,n)
  */
  animation-direction:alternate;
  /*
      播放方向
      normal 預設
      reverse 反向
      alternate 來回
      alternate-reverse 反向來回
  */
  animation-fill-mode:none;
  /*
      播放後
      none 回到前面
      backwards 回到前面
      forwards 停在後面
      both 前面或後面
  */
  animation-play-state:paused;
  /*
      running 播放中 
      paused  暫停
  */
}
.a05301_d1
{
    animation-timing-function:ease;
}
.a05301_d2
{
    animation-timing-function:linear;
}
.a05301_d3
{
    animation-timing-function:ease-in;
}
.a05301_d4
{
    animation-timing-function:ease-out;
}
.a05301_d5
{
    animation-timing-function:ease-in-out;
}
.a05301_d6
{
    animation-timing-function:cubic-bezier(0.6, -0.6, 0.1, 1.6);
}
.a05301_d7
{
    animation-timing-function:steps(3,start);
}
@keyframes a05301_a1
{
  0% 
  {
    left:8em;
  }
  2% 
  {
    left:8em;
  }
  50%
  {
    left:50%;
  }
  100%
  {
    left:95%;
  }
}  
#a05301_id1 input:checked ~ .a05301
{
  animation-play-state:running;
}

</style>


<div id="a05301_id1" style="line-height:1em">
<label for="a05301_id2">播放</label> 
<input id="a05301_id2" type="checkbox" checked=""><br><br>

<div class="a05301 a05301_d1"></div> ease        <hr>
<div class="a05301 a05301_d2"></div> linear      <hr>
<div class="a05301 a05301_d3"></div> ease-in     <hr>
<div class="a05301 a05301_d4"></div> ease-out    <hr>
<div class="a05301 a05301_d5"></div> ease-in-out <hr>
<div class="a05301 a05301_d6"></div> cubic-bezier<hr>
<div class="a05301 a05301_d7"></div> steps       <hr>
</div>  

<br>  



ease
linear
ease-in
ease-out
ease-in-out
cubic-bezier
steps


Event

CSS 的屬性 animation-name 被修改後,動畫會重新開始,如果只給同一個名字是沒有用的,所以這裡的解決方法是先設 animation-name="" 然後設個 timeout 後再修改為要播放的動畫名稱

Ani2 的按鈕是利用新增 CSS 來取代本來的腳本,在 chrome 和 firefox 上,取代後會立即生效,但是在 safari 和 iphone/ipad 上卻會等動畫結束重新開始後才會套用新的腳本, Ani3 是取代後直接呼叫套用的 function 讓動畫重新開始。

animationstart 開始播放
animationend 播放結束
animationiteration 動畫開始重複
animationcancel 取消



<style>
.a05302
{
  position:absolute;
  width:1em;
  height:1em;
  background-color:#f00;
  animation-name:"";
  animation-duration:3000ms;
  animation-iteration-count:2;
  animation-timing-function:ease;
  animation-direction:normal;
  animation-fill-mode:both;
  animation-play-state:paused;
}
@keyframes a05302_a1
{
  0% 
  {
    left:10%;
  }
  50%
  {
    left:50%;
  }
  100%
  {
    left:90%;
  }
}  
@keyframes a05302_a2
{
  0% 
  {
    left:10%;
  }
  50%
  {
    transform:translate(0,3em);
    left:50%;
  }
  100%
  {
    left:90%;
  }
}
#a05302_play:checked ~ .a05302 
{
  animation-play-state:running;
}
#a05302_div button
{
  font-size:1em;height:1.7em;
}

</style>
<style id="a05302_newcss">
</style>

  <div id="a05302_div">
  播放 <input id="a05302_play" type="checkbox" checked=""><br>
  <div id="a05302_id1" class="a05302"></div>
  <br><br><br>
  <button id="a05302_change">Change</button>
  <button id="a05302_restart">Restart</button>
  <button id="a05302_ani2">Ani2</button>
  <button id="a05302_ani3">Ani3</button>

  <br>
  <br>
  <div id="a05302_id2" style="border:1px solid #000;padding:1em;"></div>
  </div>

<script>

(function()
{
  let o = $("#a05302_id1");
  let txt = $("#a05302_id2");
  let n=0;
  let a=["a05302_a1","a05302_a2"];
  let an=0;

  o.css("animation-name",a[an]); 

  function setani(o,name)
  {
    o.css("animation-name",""); 

    setTimeout(function()
    {
      o.css("animation-name",name); 
    },10);

  }

  o.on("animationstart",function()
  {
    if (++n>=2) {txt.html("");n=0;}
    txt.html(txt.html()+"開始.");
  });
  o.on("animationend",function()
  {
    txt.html(txt.html()+"結束.");
    setani(o,a[an]);

  });
  o.on("animationiteration",function()
  {
    txt.html(txt.html()+"重複.");
  });
  o.on("animationcancel",function()
  {
    txt.html(txt.html()+"取消.");
  });

  $("#a05302_change").on("click",function()
  { 
    an=(an+1)&1;
    setani(o,a[an]);
  });

  $("#a05302_restart").on("click",function()
  { 
    setani(o,a[an]);
  });

  $("#a05302_ani2").on("click",function()
  { 
    let cc = document.getElementById('a05302_newcss').sheet;
    try{cc.deleteRule(0);}catch(e){}
    cc.insertRule("@keyframes a05302_a2{"
      +"0%{left:10%;}50%{"
      +"transform:translate(0,3em);"
      +"left:50%;}100%{left:90%;}}", 0);    
  });
  $("#a05302_ani3").on("click",function()
  { 
    let cc = document.getElementById('a05302_newcss').sheet;
    try{cc.deleteRule(0);}catch(e){}
    cc.insertRule("@keyframes a05302_a2{"
      +"0%{left:10%;}50%{"
      +"transform:translate(0,-3em);"
      +"left:50%;}100%{left:90%;}}", 0);   

    setTimeout(function(){setani(o,a[an])},10);
  });


})();

</script>

播放





開始.

利用 CSS 動畫來實做一個指針時鐘,幾乎不需要寫程式,


<style>
.a05303
{
  position:absolute;
  width:8em;
  height:8em;
  animation-name:a05303_a1;
  animation-iteration-count:infinite;
  animation-timing-function:linear;
  animation-direction:normal;
  animation-play-state:running;
}
.a05303_h
{
  animation-duration:12000ms;
} 
.a05303_m
{
  animation-duration:1000ms;
} 
@keyframes a05303_a1
{
  0% 
  {
    transform:rotate(0deg);
  }
  100%
  {
    transform:rotate(360deg);
  }
}  
.a05303_d1
{
  position:absolute;
  left:calc( 50% - 2px );
  top:10%;
  width:4px;
  height:40%;
  background-color:#0000ff;
}
.a05303_d2
{
  position:absolute;
  left:calc( 50% - 2px );
  top:20%;
  width:4px;
  height:30%;
  background-color:#ff0000;
}
.a05303_circle
{
  border:1px solid #000;
  width:8em;
  height:8em;
  border-radius:4em;
}
</style>

<div class="a05303 a05303_m">
<div class="a05303_d1"></div>
</div>
<div class="a05303 a05303_h">
<div class="a05303_d2"></div>
</div>
<div class="a05303_circle"></div>

搭配 clip-path 可以設計多邊形變形的動畫


<style>
.a05304_bk
{
  position:relative;
  width:13em;
  height:13em;
  background:#fff;
}
.a05304_path
{
  position:absolute;
  width:13em;
  height:13em;
  background:#ffee88;
  animation-name:a05303;
  animation-iteration-count:1;
  animation-timing-function:ease;
  animation-direction:normal;
  animation-play-state:running;
  animation-duration:2000ms;
  animation-fill-mode:both;
}
.a05304_ball
{
  position:absolute;
  left:12em;
  top:6em;
  width:1em;
  height:1em;
  border-radius:0.5em;
  background:#f00;
  animation:a05304_a 3000ms 0s 1 linear running;
/*
  animation:a05304_a 3000ms 0s infinite linear running;
*/
}
@keyframes a05304_a
{
0%{left:12.00em;top:6.00em;}
33%{left:3.00em;top:11.20em;}
66%{left:3.00em;top:0.80em;}
100%{left:12.00em;top:6.00em;}
}
@keyframes a05304_a1
{
0%
{clip-path:polygon(96% 50% ,27% 90% ,27% 10% ,96% 50% ,96% 50% ,96% 50% ,96% 50% ,96% 50% )
}
100%
{clip-path:polygon(96% 50% ,50% 96% ,4% 50% ,50% 4% ,96% 50% ,96% 50% ,96% 50% ,96% 50% )}
}
#a05304 button
{
  font-size:1em;
}
</style>
<style id="a05304_css"></style>

<div id="a05304">

<div class="a05304_bk">
<div class="a05304_path"></div>
<div class="a05304_ball"></div>
</div><br>

<button class="a05304_b" nn="3">三角形</button>
<button class="a05304_b" nn="4">四邊形</button>
<button class="a05304_b" nn="5">五邊形</button>
<button class="a05304_b" nn="6">六邊形</button>
<button class="a05304_b" nn="7">七邊形</button>

</div>

<script>
$(function()
{
  function cn(n)
  { 
    let i;
    let j=100/n;
    let d=3.1415926*2/n;
    let hh="@keyframes a05304_b{";
    let pp="polygon(";
    let x,y;

    for (i=0;i<=n;i++)
    {
      x=(6+6*Math.cos(i*d));
      y=(6+6*Math.sin(i*d));
      hh+=(i*j)+"%{left:"+x+"em;top:"+y+"em;}";
      pp+=((x+0.5)*100/13).toFixed(0)+"% "
         +((y+0.5)*100/13).toFixed(0)+"% ,";
    }

    for (;i<=7;i++)
    {
      pp+=((x+0.5)*100/13).toFixed(0)+"% "
         +((y+0.5)*100/13).toFixed(0)+"% ,";
    }

    hh+="}";
    pp=pp.substr(0,pp.length-1)+")";

    return [hh,pp];
  } 
  function setani(o,name)
  {
    o.css("animation-name",""); 

    setTimeout(function()
    {
      o.css("animation-name",name); 
    },10);
  }

  let bakn=3;
  let bakpath="";

  function setn(n)
  {
    let css=document.getElementById("a05304_css").sheet;
    try{css.deleteRule(0);}catch(e){}
    try{css.deleteRule(0);}catch(e){}

    let r=cn(n);
    let r1=cn(bakn);

    css.insertRule(r[0]);

    css.insertRule("@keyframes a05304_a1{"
      +"0%{"
      +"clip-path:"+r1[1]+";transform:rotate(0deg);"
      +"-webkit-clip-path:"+r1[1]
      +"}"
      +"100%{"
      +"clip-path:"+r[1]+";transform:rotate(360deg);"
      +"-webkit-clip-path:"+r[1]
      +"}}");

    setani($(".a05304_path"),"a05304_a1");

    bakn=n;
    bakpath=r[1];
  }
  $(".a05304_path").on("animationend",function()
  {
    $(".a05304_path").css("clip-path",bakpath);
    $(".a05304_path").css("-webkit-clip-path",bakpath);
  });
  $(".a05304_ball").on("animationend",function()
  {
    setani($(".a05304_ball"),"a05304_b");
  });
  $(".a05304_b").on("click",(e)=>
  {
    setn( parseInt($(e.target).attr("nn")) );
  });

  setn(3);


});
</script>



沒有留言:

張貼留言