js 動畫二

這一篇是接著上一篇 翻譯。原文點擊。
上一篇主要講了animate的基本原理。這一篇主要將幾種常見的delta。

進度函數delta

動畫是隨著時間變化而變化的,在javascript動畫中,這個變化的原則是由delta來決定的。
不同的delta使動畫的變化速度,加速度和其他的一些參數完全不同。
數學公式經常會被用到。對于一些已經離開學校多年的web從業者可能對于這些公式比較陌生。但是,在這里我將應用一些非常流行的函數,來看看他們怎么工作。
下面這里的動畫例子依次提供不同的delta。

線性 delta

function linear(progress){
  return progress;
}

Paste_Image.png

水平線是progress(時間的進度),垂直線是delta(progress) 也就是動畫移動的進度。
我們已經看出來,線性的delta使動畫勻速進行。

<div onclick="move(this.children[0], linear)" class=''example_path">
  <div class="exmple_block"></div>
</div>

n次冪函數

delta函數是progress的n次冪,比較特殊的情況,比如 n = 2n = 3。

例如:

function quad(progress){
  return Math.pow(progress, 2);
}
Paste_Image.png

Circ: a piece of circle

例如

function circ(progress){
  return 1 - Math.sin(Math.acos(progess))
}
Paste_Image.png

Back: the bow function

這個函數像一個弓:首先我們先拉弓,然后發射。

function back(progress, x) {
  return Math.pow(progress, 2) * ((x + 1) * progress - x)
}
Paste_Image.png

Bounce

想象一下,我們放開一個球自由落體,然后反彈幾次最后停止。
這個bounce函數正好和這個效果相反,開始時候'bounce'直到它到達這個目標點。
如下:

function bounce(progress) {
  for(var a = 0, b = 1, result; 1; a += b, b /= 2) {
    if (progress >= (7 - 4 * a) / 11) {
      return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2)
    }
  }
}

還有其他的bounce函數。

Elastic

這個函數依靠x這個額外的參數,這個參數定義了初始的范圍。

function elastic(progress, x) {
  return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*x/3*progress)
}

Paste_Image.png

反函數(easeIn, easeOut, easeInOut)

一個javascript框架通常提供一系列的delta,這正向的使用叫做easeIn
有的時候,我們要求這個動畫和時間的進度相反的,這個叫做easeOut
并且應用通過"time-reversing"delta。

easeOut

deltaEaseOut = 1 - delta(1 - progress)
例如:

function bounce(progress) {
  for(var a = 0, b = 1, result; 1; a += b, b /= 2) {
    if (progress >= (7 - 4 * a) / 11) {
      return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);
    }
  }
}

function makeEaseOut(delta) {  
  return function(progress) {
    return 1 - delta(1 - progress)
  }
}

var bounceEaseOut = makeEaseOut(bounce)
Paste_Image.png
easeInOut

另一個選項是delta影響開始的時候和結束的時候,叫做 easeInOut

if (progress <= 0.5) { // the first half of the animation)
  return delta(2 * progress) / 2
} else { // the second half
  return (2 - delta(2 * (1 - progress))) / 2
}

function makeEaseInOut(delta) {  
  return function(progress) {
    if (progress < .5)
      return delta(2*progress) / 2
    else
      return (2 - delta(2*(1-progress))) / 2
  }
}

bounceEaseInOut = makeEaseInOut(bounce)

Paste_Image.png

step執行函數

幾乎所有的東西,你都可以animate,除了移動,還有change opaciy, width, height, color...等等其他任何你可以想想的。

例如改變背景色:

function highlight(elem) {
  var from = [255,0,0], to = [255,255,255]
  animate({
    delay: 10,
    duration: 1000,
    delta: linear,
    step: function(delta) {
      elem.style.backgroundColor = 'rgb(' +
        Math.max(Math.min(parseInt((delta * (to[0]-from[0])) + from[0], 10), 255), 0) + ',' +
        Math.max(Math.min(parseInt((delta * (to[1]-from[1])) + from[1], 10), 255), 0) + ',' +
        Math.max(Math.min(parseInt((delta * (to[2]-from[2])) + from[2], 10), 255), 0) + ')'
    }
  })  
}

例如利用bounce模仿text type:

function animateText(textArea) {
  var text = textArea.value
  var to = text.length, from = 0
  
  animate({
    delay: 20,
    duration: 5000,
    delta: bounce,
    step: function(delta) {
      var result = (to-from) * delta + from
      textArea.value = text.substr(0, Math.ceil(result))
    }
  })
}

例如模仿足球掉落:

var img = document.getElementById('ball')
var field = document.getElementById('field')
img.onclick = function() {
  var from = 0
  var to = field.clientHeight - img.clientHeight
  animate({
    delay: 20,
    duration: 1000,
delta: makeEaseOut(bounce), 
    step: function(delta) {
      img.style.top = to*delta + 'px'
    }
  })
}

例如模仿足球向左向下掉落:

img.onclick = function() {

  var height  = document.getElementById('field').clientHeight - img.clientHeight
  var width  = 100
  
  animate({
    delay: 20,
    duration: 1000,
    delta: makeEaseOut(bounce), 
    step: function(delta) {
      img.style.top = height*delta + 'px'
    }
  })
  
animate({
    delay: 20,
    duration: 1000, 
    delta: makeEaseOut(quad),
    step: function(delta) {
      img.style.left = width*delta  + "px"
    }
  })
}

css 變化(transitions)

現在大部分現代的瀏覽器支持css transition, 一種通過css來實現的動畫。
這種動畫很簡單,設置css transition-duration屬性和transition-property屬性。
例如:

.animated {
  transition-property: background-color;
  transition-duration: 2s;
}

具體例子:

<style>
.animated {
  transition: background-color 2s;
  -webkit-transition: background-color 2s;
  -o-transition: background-color 2s;
  -moz-transition: 2s;
}
</style>
<div class="animated" onclick="this.style.backgroundColor='red'">
  <span style="font-size:150%">Click to animate (no IE, no FF<4)</span>
</div>

因為css transition還是一個草案,所以需要加上前綴。
通常沒有瀏覽器支持沒有前綴的tansition屬性。

css transition的一些限制:

  1. css transition 可以設置 delta, 但是只有有限的種類。
  2. 只有css的屬性可以操縱,但是好處是多個屬性可以一起改變。

優化的一些建議

  1. 多個timer導致cpu過載。解決方案一次redraw(也就是一個timer中)完成多個 step 動作。
  2. 由于元素在document中的位置越深,計算量越大。解決方案如下:
  • 把該元素提取出來,加到body中,然后position:absolute。
  • 操作它
  • 插回去

總結

動畫都是通過setInterval來完成的。通常delay在10ms ~ 50ms,在每一個delay中,更新元素的某些屬性。
其實這個不局限在元素上,也可以是別的東西。
通常的animate框架接受的參數:
1.delay - 幀之間的間隔
2.duration - 動畫的時間
3.delta - 時間進度和動畫進度的對應關系
4.step - 真正的動畫操作

請記住:如果你感覺動畫卡頓,把多個動畫和到一個timer中,或者把元素提取到body 上作為絕對元素來操作。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 實現鼠標移動改變圖片的透明度 所用知識:透明度屬性:opacitysetInterval()方法:設置時間函數 實...
    大海孤了島閱讀 241評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,595評論 25 708
  • 只愿一人無拘無憂 無懼無愁 只愿一生長安長樂 不思離愁
    姚小玘閱讀 1,325評論 0 0
  • duiwe對象的基本特征之一就是封裝---對外部世界隱藏其內部細節.封裝往往伴隨著委托. 比如你問你的主管是否有時...
    rxdxxxx閱讀 480評論 0 0
  • 很開心今天比平日早起了兩小時。 給自己放假把這兩小時用來看綜藝然而卻一發不可收拾收拾。 好容易關掉綜藝打開PPT打...
    錦一閱讀 224評論 0 0