分層動畫實現元素的曲線運動

在前端實現元素的動畫須要用到transition或者animation搭配transform作位移的效果,但這隻能實現元素沿着直線運動。若是想要讓元素沿着曲線運動,則須要用到本文介紹的「分層動畫」。javascript

分層動畫

你們還記得高中物理的「平拋運動」嗎?運動軌跡爲一條曲線,它能夠當作是水平方向上的勻速運動和垂直方向上的自由落體的組合。更進一步,運動軌跡爲曲線必須在水平與垂直兩個方向上有不一樣的以時間爲自變量的路徑函數f(t)。css

以此類推到前端,過渡與動畫中的屬性time-function就是以時間爲自變量的路徑函數f(t),只要讓元素在水平和垂直方向上有不一樣的time-function,就可實現曲線運動的效果。接着咱們將使用分層動畫法,讓元素同時沿着不一樣方向運動。html

分層動畫法,就是將運動曲線分解爲水平與垂直的運動,元素自己只執行一個方向的運動,其外層容器執行另外一方向上的運動前端

HTML結構以下:java

<div class="container">
  <div class="dot"></div>
</div>

讓container作水平運動,dot作垂直運動,注意二者使用的緩動函數要是不一樣的,CSS代碼以下:git

.container {
  /* ... 定位等 */
  animation: horizontal 2s infinite ease-in;
}

.dot {
  /* ... 定位等 */
  animation: vertical 2s infinite ease;
}

@keyframes horizontal {
  100% {
    transform: translateX(150px);
  }
}

@keyframes vertical {
  50% {
    animation-timing-function: ease-in;
    transform: translateY(-100px);
  }
  100% {
    transform: translateY(0);
  }
}

實現的效果就是簡單的拋物線,以下動圖:
圖片描述github

上例中用到的緩動函數ease和ease-in比較簡單,除了簡單的緩動函數以外,還能夠用貝塞爾曲線函數實現更復雜的曲線運動。算法

前端工程師須要知道的貝塞爾曲線

大三圖形學的時候接觸過貝塞爾曲線,後來因爲沒有實際應用場景,勉強只能模糊的記得公式的樣子。現在,貝塞爾曲線在前端領域中也佔有一席之地,有了應用場景,學習起來會更有目的性也能理解得更深入。前端工程師

貝塞爾曲線的繪製方法

首先,咱們須要知道貝塞爾曲線是如何繪製。網上繪製貝塞爾曲線的資料不少,這裏推薦參考文獻[1]中的掃盲文,介紹的是德卡斯特里奧(de Casteljau)算法,通俗易懂,步驟詳實。函數

例如,參照下圖,ABC三個點做爲控制點繪製曲線的大體步驟是:
肯定三個輔助點DEF,使其始終知足以下比例關係:

AD:AB = BE:BC = DF:DE

而後將AD:AB的值從0取到1,全部的F所組成的曲線就是ABC三個控制點的二階貝塞爾曲線。

動圖比較直觀一點:
動圖

前端領域中的貝塞爾曲線

前端領域中動畫或過渡的緩動函數就是三階貝塞爾曲線,即四個控制點的貝塞爾曲線,以下圖所示。
若是以爲比較抽象,能夠用這個網址 http://myst729.github.io/bezi... ,畫四個點觀察繪製過程。
圖片描述

圖中有四個控制點,左下方和右上方兩個點的位置固定,其座標爲(0,0)(1,1)。另兩個點可移動,座標分別爲紅點記爲(x1, y1),綠點記爲(x2, y2)。緩動函數中貝塞爾函數是cubic-bezier(x1, y1, x2, y2),四個參數就是中間兩個控制點的座標。

案例分析

分層動畫與貝塞爾曲線聯合可以實現一些酷炫的效果,只是我暫時沒有找到一個能夠精細量化曲線動畫的方法,只能粗略肯定貝塞爾函數並在細微處調整。

以參考文獻[2]中的動畫爲例,以下圖的「what we want」
what we want

例子中的是對稱循環的動畫,咱們先分析完整動畫的一半。
首先看水平方向,位移是translateX(200px),小黑球水平移動的速率是先快後慢,因此緩動函數應該是以下圖的貝塞爾曲線:
圖片描述

再來看垂直方向,位移是translateY(-200px),小黑球垂直移動的速率是先緩慢出發,忽然快速前進超過終點再緩慢往回,因此緩動函數應該是以下圖的貝塞爾曲線:

圖片描述

總體的CSS代碼以下:

.container.special {
  animation: xAxis 2s infinite cubic-bezier(.02,.01,.26,1);
}

.dot {
  animation: yAxis 2s infinite cubic-bezier(.29,.27,.08,1.58);
}

@keyframes xAxis {
  50% {
    transform: translateX(150px);
  }
}

@keyframes yAxis {
  50% {
    transform: translateY(-150px);
  }
}

效果以下圖:
圖片描述

能夠看出,前一半的動畫(左下到右上的過程)與預期實現基本一致。接下來就是處理後一半,後一半的動畫就是前一半的鏡象對稱,也就是前一半的水平方向運動與後一半的垂直方向運動相同,前一半的垂直方向運動與後一半的水平方向運動相同。

.container.special {
  animation: xAxis 2s infinite cubic-bezier(.02,.01,.26,1);
}

.dot {
  animation: yAxis 2s infinite cubic-bezier(.29,.27,.08,1.58);
}

@keyframes xAxis {
  50% {
    animation-timing-function: cubic-bezier(.29,.27,.08,1.58);    /* 後一半用前一半垂直向上的緩動函數 */
    transform: translateX(150px);
  }
}

@keyframes yAxis {
  50% {
    animation-timing-function: cubic-bezier(.02,.01,.26,1);    /* 後一半用前一半水平方向上的緩動函數 */
    transform: translateY(-150px);
  }
}

效果以下圖:
圖片描述

參考文檔

  1. 貝塞爾曲線掃盲貼 [http://www.html-js.com/articl...]
  2. CSS分層動畫可讓元素沿弧形路徑運動 [http://jinlong.github.io/2016...]
  3. 貝塞爾曲線 [http://cubic-bezier.com]
相關文章
相關標籤/搜索