一般,咱們使用的Web應用大多數都只是簡單地從一個視圖切換到另外一個視圖,致使用戶體驗不直觀,可是咱們能夠經過技術的手段把這方面的交互行爲作得更得體:javascript
在建立UI時,添加合理的UI過渡動效,避免跳轉和瞬間移動。若是將生活中的一些天然運動用到UI動效中來,將會給你的用戶帶來眼前一亮的感受。畢竟,全部與你互動的東西都源自於生活中天然的運動。css
接下來,咱們將一塊兒探討你可能熟悉的某一類有意義的加強用戶體驗的UI動效。這種技術有一個專業術語,即FLIP(First, Last, Invert, Play)。FLIP技術能夠以一種高性能的方式來動態的改變DOM元素的位置和尺寸,而不須要管它的佈局是如何計算或渲染的(好比,height、width、float、絕對定位、Flexbox和Grid等)。在改變的過程當中將賦予必定的動效,從而達到咱們所須要的目的,讓UI動效更爲合理,相應加強用戶的體驗。html
在移動應用程序中從一個視圖移動到另外一個視圖時,焦點元素一般會在視圖之間從一個位置移動到另外一個位置。好比下面的這些場景:java
若是要讓動效更流暢,就要儘可能的讓樣式計算的數量儘可能的少,並且儘量的快。關鍵是在動畫中儘可能的只使用transform和opacity。只不過,只使用transform和opacity是使人沮喪的。但幸運地是,FLIP可讓咱們如何只使用transform來模擬佈局變化。web
FLIP是一種記憶設備和技術,最先是由@Paul Lewis提出的,FLIP是First、Last、Invert和Play四個單詞首字母的縮寫。這裏簡單的陳述一下:api
First,指的是在任何事情發生以前(過渡以前),記錄當前元素的位置和尺寸。可使用getBoundingClientRect()這個API來處理,好比:ide
const first = el.getBoundingClientRect()佈局
這句代碼用於獲取元素的邊界 getBoundingClientRect()將會返回一個DOMRect對象,以下圖所示:性能
Last:執行一段代碼,讓元素髮生相應的變化,並記錄元素在最後狀態的位置和尺寸,好比:動畫
// 經過給元素添加一個類名,設置元素最後狀態的位置和大小
//(在.totes-at-the-end中添加相應的樣式規則),佈局發生了變化
el.classList.add('totes-at-the-end')
// 記錄元素最後狀態的位置和尺寸大小
const last = el.getBoundingClientRect()
複製代碼
Invert:計算元素第一個位置(first)和最後一個位置(last)之間的位置變化(若是須要,還能夠計算兩個狀態之間的尺寸大小的變化),而後使用這些數字作必定的計算,讓元素進行移動(經過transform來改變元素的位置和尺寸),從而建立它位於第一個位置(初始位置)的一個錯覺:
const deltaX = first.left - last.left const deltaY = first.top - last.top
const deltaW = first.width / last.width const deltaH = first.height / last.height
複製代碼
Play:將元素反轉(僞裝在first位置),咱們能夠把transform設置爲none,將其移動到last位置,讓元素有動畫效果:
elm.animate([ { transformOrigin: 'top left', transform: ` translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH}) ` }, { transformOrigin: 'top left', transform: 'none' }], { duration: 300, easing: 'ease-in-out', fill: 'both' } );
複製代碼
注意:在Play中使用了Web Animations API。若是從未接觸過相關的API,點擊這裏瞭解
把全部代碼合在一塊兒,以下:
// 獲取當前元素的邊界
const first = el.getBoundingClientRect()
// 經過給元素添加一個類名,設置元素最後狀態的位置和大小 (在.totes-at-the-end中添加相應的樣式規則) // 佈局發生了變化
el.classList.add('totes-at-the-end') // 記錄元素最後狀態的位置和尺寸大小
const last = el.getBoundingClientRect()
const deltaX = first.left - last.left const deltaY = first.top - last.top
const deltaW = first.width / last.width const deltaH = first.height / last.height elm.animate([ { transformOrigin: 'top left', transform: ` translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH}) ` }, { transformOrigin: 'top left', transform: 'none' }], { duration: 300, easing: 'ease-in-out', fill: 'both' } );
複製代碼
爲了便於你們更好的理解FLIP技術製做的動效原理,借用下圖向你們展現,或許更易於理解:
加上最後一個過程Play,實現的動畫效果以下圖所示:
特別聲明:上面兩張圖來自於@davidkpiano在2017年的CSS Dev Conf分享的《FLIP ping out about animated layouts》的話題中
在App應用中,視圖的過渡和元素不一樣狀態的過渡效果是一個很是常見的效果,其最基本的原則就是元素初始狀態和最終狀態不一樣。在Android中,這相似於共享元素轉換,只不過元素在DOM中不會像在Android中那樣從一個視圖「回收」到另外一個視圖。
接下來咱們來看一個FLIP的過渡效果:
簡單的說明一下其計算過程:
// 首先獲取點被點擊圖片的位置和尺寸
let firstRect = itemImage.getBoundingClientRect();
// 而後獲取詳情的位置和尺寸
let lastRect = detailItem.getBoundingClientRect(); // 詳情到圖片的動效
detailItem.animate( [ { transform: ` translateX(${firstRect.left - lastRect.left}px) translateY(${firstRect.top - lastRect.top}px) scale(${firstRect.width / lastRect.width}) ` }, { transform: ` translateX(0) translateY(0) scale(1) ` } ], { duration: 600, easing: 'cubic-bezier(0.2, 0, 0.2, 1)' } );
// 詳情的位置和尺寸
let itemImageRect = itemImage.getBoundingClientRect();
// 原圖片的位置和尺寸
let detailItemRect = detailItem.getBoundingClientRect();
// 詳情回到圖片的動效
itemImage.animate( [ { zIndex: 2, transform: ` translateX(${detailItemRect.left - itemImageRect.left}px) translateY(${detailItemRect.top - itemImageRect.top}px) scale(${detailItemRect.width / itemImageRect.width}) ` }, { zIndex: 2, transform: ` translateX(0) translateY(0) scale(1) ` } ], { duration: 600, easing: 'cubic-bezier(0.2, 0, 0.2, 1)' } );
複製代碼
但願本文看了對你們的動畫製做有幫助!