塞爾曲線在計算機圖形學中被大量使用,通常可以產生平滑的曲線。如果您曾經使用過Photoshop,則可能會發現名為“錨點”的工具,您可以在其中放置錨點并用它們繪制一些曲線,這些也是貝塞爾曲線。如果您使用了基于矢量的圖形SVG,這些也會使用貝塞爾曲線。讓我們看看它是如何工作的。
給定n + 1 個點(P0,…,Pn)稱為控制點,這些點定義的貝塞爾曲線定義為:
eq. 1
其中B(t)稱為Bernstein多項式:
eq. 2
你會注意到這個伯恩斯坦多項式看起來很像牛頓二項式公式中的第k項,也就是:
eq. 3
事實上,伯恩斯坦多項式就是(t + (1 - t))^n=1的展開式中的第k項。這就是為什么如果你把所有的Bi加到n,你會得到1。
二次貝塞爾曲線就是我們所說的有三個控制點的貝塞爾曲線,P(t)的階數是2。讓我們計算給定3個控制點的貝塞爾曲線,并探索一些我們可能會發現的特性!請記住,公式1適用于n+1個點,所以在我們的例子中n=2。
eq. 4
注意,P(t)返回的不是一個數字,而是曲線上的一個點。現在我們只需要選擇三個控制點,然后在[0,1]范圍內對曲線求值。我們可以在Python中很容易地做到這一點。
import numpy as np
import matplotlib.pyplot as plt
P0, P1, P2=np.array([
[0, 0],
[2, 4],
[5, 3]
])
# define bezier curve
P=lambda t: (1 - t)**2 * P0 + 2 * t * (1 - t) * P1 + t**2 * P2
# evaluate the curve on [0, 1] sliced in 50 points
points=np.array([P(t) for t in np.linspace(0, 1, 50)])
# get x and y coordinates of points separately
x, y=points[:,0], points[:,1]
# plot
plt.plot(x, y, 'b-')
plt.plot(*P0, 'r.')
plt.plot(*P1, 'r.')
plt.plot(*P2, 'r.')
plt.show()
您會注意到曲線從第一個控制點處開始,到最后一個控制點處結束。這個結果對任意數量的點都成立。由于t的取值范圍是從0到1,我們可以通過求P(t)在t=0和t=1時的值來證明這一點。使用eq.1:
eq. 5
eq. 6
因為是從P0到P2,P1完全決定了曲線的形狀。移動P1你可能會注意到:
貝塞爾曲線總是包含在控制點形成的多邊形中。這個多邊形因此被稱為控制多邊形,或者貝塞爾多邊形。這個屬性也適用于任意數量的控制點,這使得它們在使用軟件時的操作非常直觀。
控制多邊形還具有以下特性:包含曲線的面積最小,稱為凸包。
貝塞爾曲線的一個有趣應用是繪制一條通過一組預定義點的平滑曲線。之所以有趣,是因為P(t)的公式產生點,并且不是y=f(x)的形式,因此一個x可以具有多個y。。例如,我們可以這樣畫:
下面是如何使用公式1為任意數量的控制點編寫通用版本的Bezier曲線。
import numpy as np
import matplotlib.pyplot as plt
from math import factorial
def comb(n, k):
return factorial(n) // (factorial(k) * factorial(n - k))
def get_bezier_curve(points):
n=len(points) - 1
return lambda t: sum(
comb(n, i) * t**i * (1 - t)**(n - i) * points[i]
for i in range(n + 1)
)
def evaluate_bezier(points, total):
bezier=get_bezier_curve(points)
new_points=np.array([bezier(t) for t in np.linspace(0, 1, total)])
return new_points[:,0], new_points[:,1]
points=np.array([
[0, 0],
[-1, 3],
[4, 3],
[6, 0],
[7, 2.5]
])
x, y=points[:,0], points[:,1]
bx, by=evaluate_bezier(points, 50)
plt.plot(bx, by, 'b-')
plt.plot(x, y, 'r.')
plt.show()
運行該Python程序,顯示如下圖形:
了這篇文章,你就不用在其他地方學習基本的動效設計規則了。
當元素的位置和狀態發生改變的時候,動效的速度應該足夠慢,維持足夠長的時間,讓用戶能夠注意到變化,但是同時,又不能慢到需要用戶去等待。
大量的研究表明,動效的最佳持續時長是200毫秒到500毫秒之間,這個研究數字是基于人腦的認知方式和信息消化速度得出來的。任何低于100毫秒的動效對于人的眼睛而言,幾乎都是瞬間,很難被識別出來,而超過1秒的動效會讓人有遲滯感。
△ 界面中動效持續時長
在手機這樣的移動端設備上,按照 Material Design 的建議,動效時長應該控制在200~300毫秒之間。在平板電腦上,這個時長應該延長大概30%,也就是說,時長應該在400~450毫秒之間。
原因很簡單,屏幕尺寸越大,元素在發生位移的時候,跨越的距離越長,速度一定的情況下,時長自然越長。相應的,在可穿戴設備的小屏幕上,這個時長應該縮短30%,在150~200毫秒之間。
△ 移動端設備的屏幕尺寸影響動畫的持續時長
網頁動效的處理方式也不一樣,由于我們習慣在瀏覽器中直接打開網頁,考慮到瀏覽器性能和大家的使用習慣,用戶對于瀏覽器中動效變化速率的預期還是比較快的。相比于移動端中的動效速度,網頁中的速度會快上一倍,換句話說,就是動效的持續時長應該在150~200毫秒之間。如果持續時間太長,用戶會忍不住覺得網頁卡住了。
不過,如果你的網頁中所用的動效并非功能性的,而是裝飾用的,或者用來吸引用戶的注意力,那么請忘記這個規則,在這種情況下,動效可以更長。
更大的屏幕=更慢的動效?絕不是如此!
請務必記住,無論是在什么平臺上,動效的持續時長絕不是單純取決于屏幕尺寸和運動距離,還取決于平臺特征、元素大小、功能設定等等。較小的元素或者較小的變化,相應的動效應該更快一點。因此,大而復雜的元素動效持續時間更長,看起來也更舒服一點。
大小相同的元素,在移動的時候,移動距離最短的元素,是最先停止下來的。
與較大的元素相比,較小的元素運動速度應該更慢,因為相同的移動距離,對于小元素而言,位移距離和自身大小比例倍數更大,相對偏移更遠。
△ 動效的持續時長還和元素大小、運動距離有關
動效的運動規律要符合物理規律,當元素運動到邊界,發生碰撞的時候,碰撞的「能量」最終是要均勻分攤下來的,而彈跳的特效在多數情況下是不適合的,僅在特殊情況下適合使用。
△ 避免使用彈跳特效,它會分散用戶的注意力
元素的運動過程應該是清晰的,盡量不要在運動中使用模糊的效果(是的,說的就是 AE 的模糊動效愛好者們),模糊的動效不適合在 UI界面中使用。
△ 不要在動效中使用模糊效果
列表項(新聞列表、郵件列表等)所使用的動效,在實際運動的過程中,項和項之間應該有輕微的延遲,元素之間的延遲應該控制在20~25毫秒之間,如果持續時間再長,可能會給人一種遲滯的觀感。
△ 列表項之間的延遲應該在20~25毫秒
緩動指的是物體在物理規則下,漸進加速或減速的現象。在動效中加入緩動的效果能夠讓運動顯得更加自然,這是運動的基本原則之一。對于緩動,迪士尼的兩位關鍵性的動畫大師 Ollie Johnston 和 Frank Thomas 在他們的著作《The Illusion of Life: Disney Animation》中有過非常詳盡的描述。
為了不讓動效看起來機械或者人工痕跡太明顯,元素的運動應該有漸進加速和漸進減速的特征,就像物理世界當中其他的物體這樣。
勻速直線運動
不受任何物理力量的影響,勻速直線運動看起來是非常不自然的,尤其是對于人眼而言。
所有用來設計動畫的應用都會使用坐標軸和曲線來闡述動效的運動特征,我將嘗試闡述它們的含義,以及如何使用。坐標軸的 X軸是實現,而 Y軸則表示的唯一,換句話來說,如同我們在初中物理中所學到的,坐標軸上的線條描述的是速度和加速度的特征。
下面所示的直線,表示速度是均勻的,也就是勻速直線運動,物體在相同時間內運動的距離是不變的。
△ 勻速直線運動的座標圖
均勻的變化通常只會用在色彩的改變或者透明的改變上,一般來說,我們也可以讓背景元素均勻運動,而前景元素保持不變,來呈現它的狀態,就像上圖一樣。
緩動加速曲線
通過曲線我們可以看到,物體開始時候的初速度比較低,運動緩慢,隨后速度逐漸增加,這意味著物體在加速運動。
△ 加速曲線
當物體加速飛出屏幕的時候,可以使用這種加速曲線,比如界面中被用戶使用滑動手勢甩出去的卡片。但是請記住,只有當運動對象需要完全離開界面的時候才會這么使用,如果它還需要再回來的話,則不行。
△ 以加速運動將卡片扔出屏幕
動畫曲線有助于正確傳達訊息,甚至表達情緒和感覺。在下面的案例當中,我們可以看每個元素的運動位移是完全一樣的,所消耗的總時長也是一樣的,但是運動的速率變化是不同的,這一點也體現在曲線上,所表現出來的情緒也不同。
當然,通過調整曲線,你能夠讓物體的運動軌跡盡可能接近現實世界。
緩動減速曲線
當元素從屏幕外運動到屏幕內的時候,動效應該遵循這類曲線的運動特征。從全速進入屏幕開始,速度降低,直到完全停止。
△ 減速曲線
減速曲線可以適用于多種不同的 UI控件和元素,包括從屏幕外進入屏幕內的的卡片、條目等。
△ 減速曲線案例
在這種曲線之下,物體從靜止開始加速,到達速度最高點之后開始減速直到靜止。這種類型的元素在 UI界面中最為常見,每當你不知道要在動效中使用哪種運動方式的時候,可以試試標準曲線。
△ 標準曲線
根據 Material Design 的規范,最好使用不那么對稱的增速和減速的過程,讓動效看起來更加真實。同時大家會更加在意運動的結果,曲線的末端,也就是運動結束的過程最好進行適當的強調。
換句話說就是減速過程持續的時長最好超過開始加速的時長,用戶將會更加清楚地觀察到運動的最終結果,從而更好地明白運動的終止狀態。
△ 對稱和非對稱運動的差異
當元素從屏幕的一個位置移動到另外一個位置的時候,最好使用這種標準的緩動曲線,這個過程中,盡量不要讓動畫效果引人注意,不要使用戲劇化的效果。
△ 卡片元素從屏幕上運動的時候,不對稱的緩動曲線
當元素從屏幕上消失的時候,采用了相同的不對稱緩動曲線,用戶同樣可以通過滑動回到之前的位置。這個環節使用了抽屜式導航控件。
△ 抽屜式導航隨著緩動曲線從屏幕上隱藏
從這些案例當中,可以看出許多動效的初學者對于運動規則的了解還不足。比如:下面的這個動效,元素隨著減速曲線出現,然后使用標準緩動曲線消失。根據 Material Design 的標準,新出現的元素持續的時間應該更長,因為需要吸引更多的注意力。
△ 導航菜單的出現和消失
貝賽爾曲線函數 cubic-bezier是用來描述曲線的,它的名字前面之所以帶有 Cubic 是因為每個貝賽爾曲線的描述都是基于四個不同的參數來確定的。曲線的起點(0,0)和終點(1,1)在坐標軸上的位置是已經事先確定的。
為了簡化你對于貝賽爾曲線的理解,我們推薦兩個網站,分別是 easings.net 和 cubic-bezier.com ,前者包含了最常見的曲線的列表,你可以將他們復制到你的原型工具中,第二個網站為你提供了不同曲線的參數,你可以直接在其中查看各種對象的移動方式。
△ 不同類型的 cubic-bezier的曲線和參數
就像芭蕾舞的舞蹈編排一樣,動畫效果也是需要編排的,它的主要目的是讓元素從一個狀態切換到下一個狀態,自然過渡,讓用戶的注意力自然地被引導過去。
編排有兩種不同的方式:一種是均等交互,另一種是從屬交互。
均等交互意味著所有的元素和對象都遵循一個特定的編排的規則。
在這個實例當中,所有的卡片都遵循著一個方向來引導用戶的注意力,自上到下地次第加載。相反,沒有按照這樣清晰的規則來加載,用戶的注意力會被分散,元素的外觀排布也會顯得比較糟糕。
△ 用戶的注意力應該沿著一個流向來引導
至于表格式的布局,它相對就復雜一點。在這里,用戶的視線流向應該是清晰的對角線方向,因此,逐個區塊次第出現是一個糟糕的設計。這樣的逐個顯示,一方面耗時太長,另一方面會讓用戶覺得元素的加載方式是鋸齒狀的,這種方式并不合理。
△ 沿著對角線加載
從屬交互指的是使用一個中心對象作為主體,來吸引用戶的注意力,而其他的元素從屬于它來逐步呈現。這樣的動畫設計能夠創造更強的秩序感,讓主要的內容更容易引起用戶的注意。
在其他的設計當中,用戶很難搞清楚哪個才是主要的,因為注意力被分散了。因此,如果要設置多個動畫元素,應該定義清楚誰為主,誰是中心,并且盡量按照從屬關系來次第呈現不同的子元素。
如果只有一個中心對象,那么其他的對象的運動方式都要受它制約,否則用戶分不清楚主次。
根據 Material Design 的規定,當元素不成比例地變幻尺寸的時候,它應該沿著弧線運動,而不是直線運動,這樣有助于讓它看起來更加自然。所謂「不成比例」地變化指的是元素的長和寬的變化并不是按照相同比例來縮放或者變化的,換句話來說,變化的速度也不一樣。(比如:方形變成矩形)
△ 不成比例地改變對象外觀的時候,運動軌跡應該是弧線的
相反,如果元素是按照比例改變大小的時候,應該沿著直線移動,這樣不僅操作更加方便,而且更符合均勻變化的特征。看一下真實的案例,你會發現直線的運動軌跡會更加合理。
△ 成比例變化大小的時候,應該沿著直線運動
當元素不成比例放大的時候,運動軌跡是弧線,而這種弧線運動軌跡有兩種不同的呈現一種,一種軌跡是初始方向為垂直方向而運動結束時瞬間運動方向是水平的(Horizontal out),另外一種初始方向是水平方向而運動結束時瞬間運動方向是垂直的(Vertical out)。
那么怎么選取這個方向呢?
很簡單,元素運動曲線的方向,應該是要向界面的主要運動方向的主軸靠攏重合。舉個例子,在下面的動效當中,整個界面的滾動方向是上下滾動,主軸是縱向的。因此,當卡片點擊之后被展開的時候,會先向右水平移動,并最終以垂直運動結束,運動的最終方向,切線是垂直的,也就和垂直方向的主軸重合了。
△ 元素按照弧線展開的時候,最終方向應該和主軸重合
如果幾個不同的元素的運動軌跡相交,那么他們不能彼此穿越。如果每個元素都必須通過某個交點,抵達另外一個位置,那么應該次第減速,依次通過這個點,給彼此留出足夠的空間。另外一種選擇,是元素不相交,而是像實體一樣在靠近的時候,彼此推開。
為什么?
因為我們通常假定界面中所有的元素都位于同一個平面當中。
在運動過程中,元素不應彼此穿越,而應該互相留出空間。
但是這一點也不是一成不變的。在比較擁擠的界面當中,某個元素可以「越過」其他的元素,它同樣沒有穿過其他的元素消失,而是單純的移動。這一點從某種意義上也是延續自我們日常的物理規律,只不過我們會默認界面中的元素在這個情況下擁有了高度這樣的屬性。
△ 元素可以越過其他的元素運動
我們總結了這么多動效運動的規則和原則,從某種意義上還是延續自我們對于物理世界的認知,摩擦力和加速度在虛擬界面中以另外的方式續存著。模仿現實世界的界面讓我們對于界面的秩序有更清晰的認知,允許我們更輕松的了解和訪問界面的內容。
如果動效按照正確的方式來設計,它應該是不顯著,且不會分散用戶注意力的。如果不是這樣,那么你需要讓動效更微妙一點,實在不行甚至需要將它移除。動效不應該成為影響用戶操控界面的障礙,或者轉移注意力的存在。
當然,即使是遵循這么多規律,動效的設計依然是一門藝術,而非單純的科學,多做測試多摸索總是有必要的。
原文作者:Taras Skytskyi
原文鏈接:https://uxdesign.cc/the-ultimate-guide-to-proper-use-of-animation-in-ux-10bd98614fa9
譯文作者:陳子木
譯文鏈接:https://www.uisdc.com/ultimate-guide-to-ui-animation
本文由 @陳子木 授權發布于人人都是產品經理,未經作者許可,禁止轉載
題圖作者提供
塞爾曲線是自然幾何形狀的數學近似。我們使用它們來表示一條曲線,該曲線具有盡可能少的信息并具有很高的靈活性。
與更抽象的數學概念不同,貝塞爾曲線是為工業設計而創建的。它們是圖形軟件行業中流行的工具。
它們依賴于插值(我在上一篇文章中提過),結合了多個步驟以創建平滑曲線。為了更好地了解貝塞爾曲線的工作原理,讓我們從其最簡單的形式開始:二次貝塞爾曲線。
取三點,這是二次貝塞爾曲線起作用的最低要求:
為了在它們之間繪制一條曲線,我們首先使用0到1范圍內的值,在由三個點組成的兩個線段的每個頂點的兩個頂點上逐步進行插值。這使我們在改變線段值時沿著線段移動兩個點的t從0到1。
func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float):
var q0=p0.linear_interpolate(p1, t)
var q1=p1.linear_interpolate(p2, t)
然后q0,我們進行插值并q1獲得r沿曲線移動的單個點。
var r=q0.linear_interpolate(q1, t)
return r
這種類型的曲線稱為二次貝塞爾曲線。
(圖片來源:維基百科)
在前面的示例的基礎上,我們可以通過在四個點之間進行插值來獲得更多控制。
我們首先使用的功能與四個參數取四點作為輸入, p0,p1,p2和p3:
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
我們對每個點應用線性插值以將其減少到三個:
var q0=p0.linear_interpolate(p1, t)
var q1=p1.linear_interpolate(p2, t)
var q2=p2.linear_interpolate(p3, t)
然后,我們將三點簡化為兩點:
var r0=q0.linear_interpolate(q1, t)
var r1=q1.linear_interpolate(q2, t)
并給一個:
var s=r0.linear_interpolate(r1, t)
return s
這是全部功能:
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
var q0=p0.linear_interpolate(p1, t)
var q1=p1.linear_interpolate(p2, t)
var q2=p2.linear_interpolate(p3, t)
var r0=q0.linear_interpolate(q1, t)
var r1=q1.linear_interpolate(q2, t)
var s=r0.linear_interpolate(r1, t)
return s
結果將是在所有四個點之間插值的平滑曲線:
(圖片來源:維基百科)
==注意==
三次貝塞爾曲線插值在3D中的效果相同,只是使用Vector3 代替Vector2。
以立方貝塞爾曲線為基礎,我們可以更改兩個點的工作方式以自由控制曲線的形狀。而是具有p0,p1,p2 和p3,我們將它們存儲為:
這樣,我們有兩個點和兩個控制點,它們是相對于各個點的相對向量。如果您以前使用過圖形或動畫軟件,則可能看起來很熟悉:
這就是圖形軟件如何向用戶顯示Bezier曲線,以及它們在Godot中的工作方式和外觀。
有兩個包含曲線的對象:Curve3D和Curve2D(分別用于3D和2D)。
它們可以包含多個點,從而可以使用更長的路徑。也可以將它們設置為節點:Path和Path2D(也分別用于3D和2D):
但是,使用它們可能并不十分明顯,因此以下是Bezier曲線最常見用例的描述。
僅評估它們可能是一種選擇,但是在大多數情況下,它不是很有用。 貝塞爾曲線的最大缺點是,如果以恒定速度從t=0到t=1遍歷它們,則實際插補將不會以恒定速度移動。 速度也是點p0,p1,p2和p3之間距離的插值,并且沒有數學上簡單的方法來以恒定速度遍歷曲線。
讓我們用下面的偽代碼做一個簡單的例子:
var t=0.0
func _process(delta):
t +=delta
position=_cubic_bezier(p0, p1, p2, p3, t)
如您所見,即使t以恒定速度增加,圓的速度(以每秒像素為單位)也會變化。這使得貝塞爾曲線難以在開箱即用的情況下使用。
繪制貝塞爾曲線(或基于曲線的對象)是一種非常常見的用例,但這也不容易。在幾乎任何情況下,貝塞爾曲線都需要轉換為某種線段。但是,這通常很困難,而又不創建大量的代碼。
原因是曲線的某些部分(特別是拐角)可能需要大量的點,而其他部分可能不需要:
此外,如果兩個控制點都是0, 0(請記住它們是相對矢量),則貝塞爾曲線將只是一條直線(因此繪制大量的點將是浪費的)。
在繪制貝塞爾曲線之前,需要進行細分。這通常通過遞歸或分而治之的功能來完成,該功能可以分割曲線,直到曲率量小于某個閾值為止。
的曲線類通過提供這種 Curve2D.tessellate()函數(其接收可選stages的遞歸和角度tolerance參數)。這樣,基于曲線繪制對象就更容易了。
曲線的最后一個常見用例是遍歷它們。由于前面提到的有關恒速的內容,這也很困難。
為了使此操作更容易,需要將曲線烘焙到等距的點。這樣,它們可以通過常規插值進行近似(可以通過三次選項進一步改進)。為此,只需將Curve.interpolate_baked()方法與Curve2D.get_baked_length()一起使用 。第一次調用它們中的任何一個都會在內部烘焙曲線。
然后,可以使用以下偽代碼完成恒速遍歷:
var t=0.0
func _process(delta):
t +=delta
position=curve.interpolate_baked(t * curve.get_baked_length(), true)
然后,輸出將以恒定速度移動:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。