1 概述
屬性動(dòng)畫(huà),是最為基礎(chǔ)的動(dòng)畫(huà),其功能強(qiáng)大、使用場(chǎng)景多,應(yīng)用范圍較廣。常用于如下場(chǎng)景中:
- 一、頁(yè)面布局發(fā)生變化。例如添加、刪除部分組件元素。
- 二、頁(yè)面元素的可見(jiàn)性和位置發(fā)生變化。例如顯示或者隱藏部分元素,或者將部分元素從一端移動(dòng)到另外一端。
- 三、頁(yè)面中圖形圖片元素動(dòng)起來(lái)。例如使頁(yè)面中的靜態(tài)圖片動(dòng)起來(lái)。
簡(jiǎn)單來(lái)說(shuō),屬性動(dòng)畫(huà)是組件的通用屬性發(fā)生改變時(shí)而產(chǎn)生的屬性漸變效果。如下圖所示,其原理是,當(dāng)組件的通用屬性發(fā)生改變時(shí),組件狀態(tài)由初始狀態(tài)逐漸變?yōu)榻Y(jié)束狀態(tài)的過(guò)程中,會(huì)創(chuàng)建多個(gè)連續(xù)的中間狀態(tài),逐幀播放后,就會(huì)形成屬性漸變效果,從而形成動(dòng)畫(huà)。
屬性動(dòng)畫(huà)的使用方式也非常簡(jiǎn)單,只需要給組件(包括基礎(chǔ)組件和容器組件)添加animation屬性,并設(shè)置好參數(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)建屬性動(dòng)畫(huà)頁(yè)面
如下圖所示,在該下拉刷新動(dòng)畫(huà)場(chǎng)景中,一共有6個(gè)屬性動(dòng)畫(huà)。頭部中的五個(gè)圖標(biāo)的移動(dòng)放大動(dòng)畫(huà)中,每個(gè)圖標(biāo)都是單獨(dú)的一個(gè)動(dòng)畫(huà),其共同組合成一個(gè)刷新等待動(dòng)畫(huà)。最后是下方組件上移的一個(gè)移動(dòng)動(dòng)畫(huà)。為方便理解,圖中下方的內(nèi)容將以圖片來(lái)代替實(shí)際應(yīng)用的功能頁(yè)面。
圖2-1 :示例動(dòng)畫(huà)
該6個(gè)屬性動(dòng)畫(huà)創(chuàng)建方式類(lèi)似,以五個(gè)圖標(biāo)放大移動(dòng)動(dòng)畫(huà)的為例來(lái)講解如何創(chuàng)建屬性動(dòng)畫(huà)。
首先,創(chuàng)建一個(gè)頭部刷新組件RefreshAnimHeader,在其中自定義一個(gè)圖標(biāo)組件AttrAnimIcons,用Image組件將資源圖標(biāo)引入,并設(shè)置好樣式,如下所示:
@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容器組件,將自定義的圖標(biāo)組件引入,并設(shè)置好樣式,同時(shí)定義組件狀態(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();
})
}
}
運(yùn)行代碼,即可看到五個(gè)圖標(biāo)的移動(dòng)放大動(dòng)畫(huà)效果。
1、animation屬性作用域。animation自身也是組件的一個(gè)屬性,其作用域?yàn)閍nimation之前。即產(chǎn)生屬性動(dòng)畫(huà)的屬性須在animation之前聲明,其后聲明的將不會(huì)產(chǎn)生屬性動(dòng)畫(huà)。以示例中的五個(gè)圖標(biāo)動(dòng)畫(huà)為例,我們期望產(chǎn)生動(dòng)畫(huà)的屬性為Image組件的width屬性,故該屬性width需在animation屬性之前聲明。如果將該屬性width在animation之后聲明,則不會(huì)產(chǎn)生動(dòng)畫(huà)效果。
2、產(chǎn)生屬性動(dòng)畫(huà)的屬性變化時(shí)需觸發(fā)UI狀態(tài)更新。在本示例中,產(chǎn)生動(dòng)畫(huà)的屬性width,其值是通過(guò)變量iconWidth從30變?yōu)?00,故該變量iconWidth的改變需觸發(fā)UI狀態(tài)更新。
3、產(chǎn)生屬性動(dòng)畫(huà)的屬性本身需滿足一定的要求,并非任何屬性都可以產(chǎn)生屬性動(dòng)畫(huà)。目前支持的屬性包括width、height、position、opacity、backgroundColor、scale、rotate、translate等
3 屬性動(dòng)畫(huà)參數(shù)調(diào)整
屬性動(dòng)畫(huà)中animation的參數(shù)如下:
屬性名稱(chēng) | 屬性類(lèi)型 | 默認(rèn)值 | 描述 |
---|---|---|---|
duration | number | 1000 | 動(dòng)畫(huà)時(shí)長(zhǎng),單位為毫秒,默認(rèn)時(shí)長(zhǎng)為1000毫秒。 |
tempo | number | 1.0 | 動(dòng)畫(huà)的播放速度,值越大動(dòng)畫(huà)播放越快,值越小播放越慢,為0時(shí)無(wú)動(dòng)畫(huà)效果。 |
curve | Curve | Curve.Linear | 動(dòng)畫(huà)變化曲線,默認(rèn)曲線為線性。 |
delay | number | 0 | 延時(shí)播放時(shí)間,單位為毫秒,默認(rèn)不延時(shí)播放。 |
iterations | number | 1 | 播放次數(shù),默認(rèn)一次,設(shè)置為-1時(shí)表示無(wú)限次播放。 |
playMode | PlayMode | PlayMode.Normal | 設(shè)置動(dòng)畫(huà)播放模式,默認(rèn)播放完成后重頭開(kāi)始播放。 |
onFinish | function | - | 動(dòng)畫(huà)播放結(jié)束時(shí)回調(diào)該函數(shù)。 |
其中變化曲線curve枚舉值為:
名稱(chēng) | 描述 |
---|---|
Linear | 表示動(dòng)畫(huà)從頭到尾的速度都是相同的。 |
Ease | 表示動(dòng)畫(huà)以低速開(kāi)始,然后加快,在結(jié)束前變慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。 |
EaseIn | 表示動(dòng)畫(huà)以低速開(kāi)始,CubicBezier(0.42, 0.0, 1.0, 1.0)。 |
EaseOut | 表示動(dòng)畫(huà)以低速結(jié)束,CubicBezier(0.0, 0.0, 0.58, 1.0)。 |
EaseInOut | 表示動(dòng)畫(huà)以低速開(kāi)始和結(jié)束,CubicBezier(0.42, 0.0, 0.58, 1.0)。 |
FastOutSlowIn | 標(biāo)準(zhǔn)曲線,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枚舉值為:
名稱(chēng) | 描述 |
---|---|
Normal | 動(dòng)畫(huà)按正常播放。 |
Reverse | 動(dòng)畫(huà)反向播放。 |
Alternate | 動(dòng)畫(huà)在奇數(shù)次(1、3、5...)正向播放,在偶數(shù)次(2、4、6...)反向播放。 |
AlternateReverse | 動(dòng)畫(huà)在奇數(shù)次(1、3、5...)反向播放,在偶數(shù)次(2、4、6...)正向播放。 |
本文以參數(shù)delay和onFinish為例來(lái)演示和講解屬性動(dòng)畫(huà)的參數(shù)調(diào)整。其他參數(shù)的效果可自行嘗試。
延時(shí)播放時(shí)間delay的設(shè)置
在單個(gè)的組件元素的屬性動(dòng)畫(huà)中,一般不設(shè)置參數(shù)delay的值。而在需要設(shè)置時(shí),往往是需要在動(dòng)畫(huà)開(kāi)始前做一些準(zhǔn)備工作,具體依場(chǎng)景而定,本文在此不討論。
在由多個(gè)組件元素的屬性動(dòng)畫(huà)組合的動(dòng)畫(huà)中,例如示例動(dòng)畫(huà)中的五個(gè)圖標(biāo)的屬性動(dòng)畫(huà)組合而成的刷新等待動(dòng)畫(huà),通過(guò)設(shè)置參數(shù)delay的值,可以使各個(gè)組件元素之間按照一定的秩序依次播放,形成跌宕起伏、鱗次櫛比的動(dòng)畫(huà)效果。在此場(chǎng)景中,該值的大小又與duration相關(guān)聯(lián)。
該如何設(shè)置各個(gè)圖標(biāo)的參數(shù)delay的值呢?
在設(shè)置delay值之前,我們先理解一個(gè)概念:延時(shí)間距。其意思是兩個(gè)圖標(biāo)組件的延時(shí)參數(shù)delay的差值,即:delay2-delay1=延時(shí)間距。要想實(shí)現(xiàn)五個(gè)圖標(biāo)之間以良好的秩序先后移動(dòng)放大,各個(gè)圖標(biāo)之間的延時(shí)間距是一樣的,例如延時(shí)間距為100ms時(shí),此五個(gè)圖標(biāo)的延時(shí)delay可以分別設(shè)置為100ms、200ms、300ms、400ms、500ms。
在實(shí)際開(kāi)發(fā)場(chǎng)景中,我們?cè)撊绾未_定延時(shí)間距呢?
在此有個(gè)經(jīng)驗(yàn)可以參考:在延時(shí)間距不超過(guò)動(dòng)畫(huà)時(shí)長(zhǎng)duration時(shí),總延時(shí)間距越接近duration,秩序性越好。其中,總延時(shí)間距為延時(shí)間距與圖標(biāo)數(shù)量的乘積,即:延時(shí)間距*圖標(biāo)數(shù)量=總延時(shí)間距。
故此,我們?cè)谠O(shè)置參數(shù)delay時(shí),需要確定動(dòng)畫(huà)時(shí)長(zhǎng)duration的值。該值默認(rèn)為1000ms,具體時(shí)長(zhǎng)可依據(jù)具體的業(yè)務(wù)需要來(lái)決定。
在本示例動(dòng)畫(huà)中,圖標(biāo)動(dòng)畫(huà)時(shí)長(zhǎng)duration為2000ms,故延時(shí)間距為2000ms/5=400ms,五個(gè)圖標(biāo)的延時(shí)參數(shù)delay可分別設(shè)置為400ms、800ms、1200ms、1600ms、2000ms。其效果如示例圖所示,圖標(biāo)先后秩序明顯,視覺(jué)效果良好。
onFinish回調(diào)函數(shù)的使用
參數(shù)onFinish與參數(shù)iterations有關(guān)。當(dāng)參數(shù)iterations播放結(jié)束時(shí),會(huì)調(diào)用onFinish函數(shù)來(lái)進(jìn)行后續(xù)的業(yè)務(wù)處理。例如提示動(dòng)畫(huà)播放結(jié)束。
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:"動(dòng)畫(huà)播放結(jié)束!?。? })
}
})
當(dāng)iterations設(shè)置為-1時(shí),表示無(wú)限次播放,則onFinish回調(diào)函數(shù)不會(huì)被調(diào)用。
4 關(guān)閉屬性動(dòng)畫(huà)頁(yè)面
此處需要將關(guān)閉屬性動(dòng)畫(huà)區(qū)別開(kāi)來(lái):
- 屬性動(dòng)畫(huà)關(guān)閉,是指動(dòng)畫(huà)播放結(jié)束,但是動(dòng)畫(huà)組件依然存在并顯示在頁(yè)面上。
- 關(guān)閉屬性動(dòng)畫(huà)頁(yè)面,是指將動(dòng)畫(huà)的組件刪除或者隱藏起來(lái)。
在本示例動(dòng)畫(huà)中,指將頭部刷新組件RefreshAnimHeader隱藏起來(lái)。該如何實(shí)現(xiàn)呢?
首先,在組件RefreshAnimHeader中添加變量state,并用@Consume監(jiān)聽(tīng)其值的變化,同時(shí)添加條件渲染邏輯,根據(jù)state的值來(lái)判斷是否需要關(guān)閉。當(dāng)state變?yōu)镮DLE狀態(tài)時(shí),表示需要關(guān)閉屬性動(dòng)畫(huà)頁(yè)面。
@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();
})
}
}
其次,在本示例中,通過(guò)下方圖片的上移屬性動(dòng)畫(huà)來(lái)關(guān)閉刷新組件RefreshAnimHeader。在組件RefreshComponent中,通過(guò)@Consume與組件RefreshAnimHeader的@Consume進(jìn)行間接綁定,修改state變量的值為IDLE狀態(tài)即可關(guān)閉屬性動(dòng)畫(huà)頁(yè)面。
@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;
}
}
})
}
審核編輯 黃宇
-
鴻蒙
+關(guān)注
關(guān)注
57文章
2388瀏覽量
42964 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3744瀏覽量
16473
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論