1 概述
屬性動畫,是最為基礎的動畫,其功能強大、使用場景多,應用范圍較廣。常用于如下場景中:
- 一、頁面布局發(fā)生變化。例如添加、刪除部分組件元素。
- 二、頁面元素的可見性和位置發(fā)生變化。例如顯示或者隱藏部分元素,或者將部分元素從一端移動到另外一端。
- 三、頁面中圖形圖片元素動起來。例如使頁面中的靜態(tài)圖片動起來。
簡單來說,屬性動畫是組件的通用屬性發(fā)生改變時而產生的屬性漸變效果。如下圖所示,其原理是,當組件的通用屬性發(fā)生改變時,組件狀態(tài)由初始狀態(tài)逐漸變?yōu)榻Y束狀態(tài)的過程中,會創(chuàng)建多個連續(xù)的中間狀態(tài),逐幀播放后,就會形成屬性漸變效果,從而形成動畫。
屬性動畫的使用方式也非常簡單,只需要給組件(包括基礎組件和容器組件)添加animation屬性,并設置好參數(shù),如下代碼所示:
Image($r('app.media.image1'))
.animation({
duration: 1000,
tempo: 1.0,
delay: 0,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: 1
})
2 創(chuàng)建屬性動畫頁面
如下圖所示,在該下拉刷新動畫場景中,一共有6個屬性動畫。頭部中的五個圖標的移動放大動畫中,每個圖標都是單獨的一個動畫,其共同組合成一個刷新等待動畫。最后是下方組件上移的一個移動動畫。為方便理解,圖中下方的內容將以圖片來代替實際應用的功能頁面。
圖2-1 :示例動畫
該6個屬性動畫創(chuàng)建方式類似,以五個圖標放大移動動畫的為例來講解如何創(chuàng)建屬性動畫。
首先,創(chuàng)建一個頭部刷新組件RefreshAnimHeader,在其中自定義一個圖標組件AttrAnimIcons,用Image組件將資源圖標引入,并設置好樣式,如下所示:
@Component
export default struct RefreshAnimHeader {
...
@Builder AttrAnimIcons(iconItem) {
Image(iconItem.imgRes)
.width(this.iconWidth)
.position({ x: iconItem.posX })
.objectFit(ImageFit.Contain)
.animation({
duration: 2000,
tempo: 3.0,
delay: iconItem.delay,
curve: Curve.Linear,
playMode: PlayMode.Alternate,
iterations: -1
})
}
...
}
然后在build方法中使用Row容器組件,將自定義的圖標組件引入,并設置好樣式,同時定義組件狀態(tài)iconWidth,添加onApper事件,修改iconWidth的值,使其從30變?yōu)?00,觸發(fā)UI狀態(tài)更新。
@Component
export default struct RefreshAnimHeader {
...
@State iconWidth: number = 30;
private onStateCheck() {
if (this.state === RefreshState.REFRESHING) {
this.iconWidth = 100;
} else {
this.iconWidth = 30;
}
}
build() {
Row() {
ForEach(CommonConstants.REFRESH_HEADER_FEATURE, (iconItem) = > {
this.AttrAnimIcons(iconItem)
}, item = > item.toString())
}
.width("100%")
.height("100%")
.onAppear(() = > {
this.onStateCheck();
})
}
}
運行代碼,即可看到五個圖標的移動放大動畫效果。
1、animation屬性作用域。animation自身也是組件的一個屬性,其作用域為animation之前。即產生屬性動畫的屬性須在animation之前聲明,其后聲明的將不會產生屬性動畫。以示例中的五個圖標動畫為例,我們期望產生動畫的屬性為Image組件的width屬性,故該屬性width需在animation屬性之前聲明。如果將該屬性width在animation之后聲明,則不會產生動畫效果。
2、產生屬性動畫的屬性變化時需觸發(fā)UI狀態(tài)更新。在本示例中,產生動畫的屬性width,其值是通過變量iconWidth從30變?yōu)?00,故該變量iconWidth的改變需觸發(fā)UI狀態(tài)更新。
3、產生屬性動畫的屬性本身需滿足一定的要求,并非任何屬性都可以產生屬性動畫。目前支持的屬性包括width、height、position、opacity、backgroundColor、scale、rotate、translate等
3 屬性動畫參數(shù)調整
屬性動畫中animation的參數(shù)如下:
屬性名稱 | 屬性類型 | 默認值 | 描述 |
---|---|---|---|
duration | number | 1000 | 動畫時長,單位為毫秒,默認時長為1000毫秒。 |
tempo | number | 1.0 | 動畫的播放速度,值越大動畫播放越快,值越小播放越慢,為0時無動畫效果。 |
curve | Curve | Curve.Linear | 動畫變化曲線,默認曲線為線性。 |
delay | number | 0 | 延時播放時間,單位為毫秒,默認不延時播放。 |
iterations | number | 1 | 播放次數(shù),默認一次,設置為-1時表示無限次播放。 |
playMode | PlayMode | PlayMode.Normal | 設置動畫播放模式,默認播放完成后重頭開始播放。 |
onFinish | function | - | 動畫播放結束時回調該函數(shù)。 |
其中變化曲線curve枚舉值為:
名稱 | 描述 |
---|---|
Linear | 表示動畫從頭到尾的速度都是相同的。 |
Ease | 表示動畫以低速開始,然后加快,在結束前變慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。 |
EaseIn | 表示動畫以低速開始,CubicBezier(0.42, 0.0, 1.0, 1.0)。 |
EaseOut | 表示動畫以低速結束,CubicBezier(0.0, 0.0, 0.58, 1.0)。 |
EaseInOut | 表示動畫以低速開始和結束,CubicBezier(0.42, 0.0, 0.58, 1.0)。 |
FastOutSlowIn | 標準曲線,cubic-bezier(0.4, 0.0, 0.2, 1.0)。 |
LinearOutSlowIn | 減速曲線,cubic-bezier(0.0, 0.0, 0.2, 1.0)。 |
FastOutLinearIn | 加速曲線,cubic-bezier(0.4, 0.0, 1.0, 1.0)。 |
ExtremeDeceleration | 急緩曲線,cubic-bezier(0.0, 0.0, 0.0, 1.0)。 |
Sharp | 銳利曲線,cubic-bezier(0.33, 0.0, 0.67, 1.0)。 |
Rhythm | 節(jié)奏曲線,cubic-bezier(0.7, 0.0, 0.2, 1.0)。 |
Smooth | 平滑曲線,cubic-bezier(0.4, 0.0, 0.4, 1.0)。 |
Friction | 阻尼曲線,CubicBezier(0.2, 0.0, 0.2, 1.0)。 |
播放模式playMode枚舉值為:
名稱 | 描述 |
---|---|
Normal | 動畫按正常播放。 |
Reverse | 動畫反向播放。 |
Alternate | 動畫在奇數(shù)次(1、3、5...)正向播放,在偶數(shù)次(2、4、6...)反向播放。 |
AlternateReverse | 動畫在奇數(shù)次(1、3、5...)反向播放,在偶數(shù)次(2、4、6...)正向播放。 |
本文以參數(shù)delay和onFinish為例來演示和講解屬性動畫的參數(shù)調整。其他參數(shù)的效果可自行嘗試。
延時播放時間delay的設置
在單個的組件元素的屬性動畫中,一般不設置參數(shù)delay的值。而在需要設置時,往往是需要在動畫開始前做一些準備工作,具體依場景而定,本文在此不討論。
在由多個組件元素的屬性動畫組合的動畫中,例如示例動畫中的五個圖標的屬性動畫組合而成的刷新等待動畫,通過設置參數(shù)delay的值,可以使各個組件元素之間按照一定的秩序依次播放,形成跌宕起伏、鱗次櫛比的動畫效果。在此場景中,該值的大小又與duration相關聯(lián)。
該如何設置各個圖標的參數(shù)delay的值呢?
在設置delay值之前,我們先理解一個概念:延時間距。其意思是兩個圖標組件的延時參數(shù)delay的差值,即:delay2-delay1=延時間距。要想實現(xiàn)五個圖標之間以良好的秩序先后移動放大,各個圖標之間的延時間距是一樣的,例如延時間距為100ms時,此五個圖標的延時delay可以分別設置為100ms、200ms、300ms、400ms、500ms。
在實際開發(fā)場景中,我們該如何確定延時間距呢?
在此有個經(jīng)驗可以參考:在延時間距不超過動畫時長duration時,總延時間距越接近duration,秩序性越好。其中,總延時間距為延時間距與圖標數(shù)量的乘積,即:延時間距*圖標數(shù)量=總延時間距。
故此,我們在設置參數(shù)delay時,需要確定動畫時長duration的值。該值默認為1000ms,具體時長可依據(jù)具體的業(yè)務需要來決定。
在本示例動畫中,圖標動畫時長duration為2000ms,故延時間距為2000ms/5=400ms,五個圖標的延時參數(shù)delay可分別設置為400ms、800ms、1200ms、1600ms、2000ms。其效果如示例圖所示,圖標先后秩序明顯,視覺效果良好。
onFinish回調函數(shù)的使用
參數(shù)onFinish與參數(shù)iterations有關。當參數(shù)iterations播放結束時,會調用onFinish函數(shù)來進行后續(xù)的業(yè)務處理。例如提示動畫播放結束。
Image(iconItem.imgRes)
.width(this.iconWidth)
.position({ x: iconItem.posX })
.objectFit(ImageFit.Contain)
.animation({
duration: 2000,
tempo: 3.0,
delay: iconItem.delay,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: 1,
onFinish: () = > {
prompt.showToast({ message:"動畫播放結束!!!" })
}
})
當iterations設置為-1時,表示無限次播放,則onFinish回調函數(shù)不會被調用。
4 關閉屬性動畫頁面
此處需要將關閉屬性動畫區(qū)別開來:
- 屬性動畫關閉,是指動畫播放結束,但是動畫組件依然存在并顯示在頁面上。
- 關閉屬性動畫頁面,是指將動畫的組件刪除或者隱藏起來。
在本示例動畫中,指將頭部刷新組件RefreshAnimHeader隱藏起來。該如何實現(xiàn)呢?
首先,在組件RefreshAnimHeader中添加變量state,并用@Consume監(jiān)聽其值的變化,同時添加條件渲染邏輯,根據(jù)state的值來判斷是否需要關閉。當state變?yōu)镮DLE狀態(tài)時,表示需要關閉屬性動畫頁面。
@Component
export default struct RefreshAnimHeader {
@Consume(RefreshConstants.REFRESH_STATE_TAG) @Watch('onStateCheck') state: RefreshState;
build() {
Row() {
if (this.state !== RefreshState.IDLE) { // start or stop animation when idle state.
ForEach(CommonConstants.REFRESH_HEADER_FEATURE, (iconItem) = > {
this.AttrAnimIcons(iconItem)
}, item = > item.toString()}
}
}
.width(CommonConstants.FULL_LENGTH)
.height(CommonConstants.FULL_LENGTH)
.onAppear(() = > {
this.onStateCheck();
})
}
}
其次,在本示例中,通過下方圖片的上移屬性動畫來關閉刷新組件RefreshAnimHeader。在組件RefreshComponent中,通過@Consume與組件RefreshAnimHeader的@Consume進行間接綁定,修改state變量的值為IDLE狀態(tài)即可關閉屬性動畫頁面。
@Component
export default struct RefreshComponent {
@Consume(RefreshConstants.REFRESH_STATE_TAG) @Watch('onStateChanged') state: RefreshState;
build() {
List({ scroller: this.listController }) {
ListItem() {
...
}
}
.animation({
curve: Curve.Smooth,
duration: RefreshConstants.REFRESH_HEADER_ANIM_DURATION,
playMode: PlayMode.Normal,
onFinish: () = > {
if (this.headerOffset === -RefreshConstants.REFRESH_HEADER_HEIGHT) {
this.state = RefreshState.IDLE;
}
}
})
}
審核編輯 黃宇
-
鴻蒙
+關注
關注
57文章
2345瀏覽量
42822 -
OpenHarmony
+關注
關注
25文章
3716瀏覽量
16273
發(fā)布評論請先 登錄
相關推薦
評論