逐幀動(dòng)畫是常見的一種動(dòng)畫呈現(xiàn)形式,本例就為大家介紹如何通過 translate(),setInterval(),clearAllInterval() 等方法實(shí)現(xiàn)逐幀動(dòng)畫。
效果呈現(xiàn)
本例最終效果如上圖:
點(diǎn)擊“run”按鈕,火柴人開始走動(dòng)。
點(diǎn)擊“stop”按鈕,火柴人停止走動(dòng)。
運(yùn)行環(huán)境
本例基于以下環(huán)境開發(fā),開發(fā)者也可以基于其他適配的版本進(jìn)行開發(fā):
IDE:DevEco Studio 3.1 Release
SDK:Ohos_sdk_public 3.2.12.5(API Version 9 Release)
實(shí)現(xiàn)思路
本例的實(shí)現(xiàn)有兩個(gè)關(guān)鍵點(diǎn): ①將連續(xù)走動(dòng)的火柴人拆分為多幀靜態(tài)圖像,在固定的時(shí)間間隔內(nèi)逐幀將圖像移動(dòng)到動(dòng)畫窗口,間隔時(shí)間要小于肉眼可察覺的時(shí)間。循環(huán)上述動(dòng)作,就可以實(shí)現(xiàn)火柴人的走動(dòng)動(dòng)畫。
火柴人靜態(tài)圖像如下:
?
將背景圖片以固定速度相對(duì)于火柴人走動(dòng)方向反方向移動(dòng),從而實(shí)現(xiàn)火柴人向前走動(dòng)的效果。
背景圖如下:
本例使用 translate() 控制火柴人的移動(dòng),用 backgroundImagePosition() 控制背景圖的移動(dòng)。 另外,通過 setInterval() 設(shè)置火柴人移動(dòng)的時(shí)間間隔,通過 clearAllInterval() 清除移動(dòng)。
開發(fā)步驟
①搭建 UI 框架
使用兩個(gè) Row 組件分別呈現(xiàn)背景圖和火柴人,第二個(gè) Row 組件作為第一個(gè) Row 組件的子組件,父 Row 組件的背景設(shè)置為背景圖,子 Row 組件中添加 Image 組件用來呈現(xiàn)火柴人單幀圖像。
@Entry @Component exportdefaultstructframeAnimation{ build(){ Column(){ //父Row組件 Row(){ //子Row組件 Row(){ //通過Image組件顯示火柴人圖像 Image($r("app.media.man")).height(60).width(545.16) }.width(100) .justifyContent(FlexAlign.Start) .alignItems(VerticalAlign.Top) //截取顯示與背景同等大小的區(qū)域,控制單個(gè)火柴人顯示在畫面中 .clip(true) } //添加背景圖像 .backgroundImage($r("app.media.background")) //保持寬高比進(jìn)行縮小或者放大,使得圖片兩邊都大于或等于顯示邊界。 .backgroundImageSize(ImageSize.Cover) .width('100%') .height(130) .justifyContent(FlexAlign.Center) .alignItems(VerticalAlign.Bottom) Row(){ //添加跑動(dòng)按鈕 Button('run') .margin({right:10}) .type(ButtonType.Normal) .width(75) .borderRadius(5) //添加停止按鈕 Button('stop') .type(ButtonType.Normal) .borderRadius(5) .width(75) .backgroundColor('#ff0000') }.margin({top:30,bottom:10}) }.width('100%').width('100%').padding({top:30}) } }
②添加火柴人和背景圖片的移動(dòng)邏輯
通過狀態(tài)變量設(shè)定火柴人和背景圖片的位置,位置變化時(shí)可以實(shí)時(shí)刷新 UI 界面。
//火柴人位置變量 @StatemanPostion:{ x:number, y:number }={x:0,y:0} //背景圖位置變量 @StatetreePosition:{ x:number, y:number }={x:0,y:0}給火柴人和背景圖片添加位置屬性。
Row(){ Row(){ Image($r("app.media.man")) .height(60) .width(545.16) //通過translate實(shí)現(xiàn)火柴人的位移。綁定manPosition,用來改變火柴人位置。 .translate(this.manPostion) } ... } .backgroundImage($r("app.media.background")) .backgroundImageSize(ImageSize.Cover) //通過backgroundImagePosition實(shí)現(xiàn)背景圖片的位移。綁定treePosition,用來改變背景圖片的位置。 .backgroundImagePosition(this.treePosition) ...
③為’‘run’'按鈕和"stop"按鈕綁定控制邏輯
構(gòu)建火柴人和背景圖片移動(dòng)的方法,用來設(shè)定火柴人和背景圖片每次移動(dòng)的距離。 這里要注意火柴人每次移動(dòng)的距離等于兩個(gè)火柴人之間的間隔距離(像素值)。
//火柴人移動(dòng)方法 manWalk(){ if(this.manPostion.x<=?-517.902)?{ ????this.manPostion.x?=?0 ??}?else?{ ????//?每次移動(dòng)的距離為火柴人靜態(tài)圖像之間的間隔距離 ????this.manPostion.x?-=?129.69 ??} } //?背景移動(dòng)方法 treesMove()?{ ??if?(this.treePosition.x?<=?-1215)?{ ????this.treePosition.x?=?0 ??}?else?{ ????this.treePosition.x?-=?20 ??} }
創(chuàng)建 doAnimation() 方法調(diào)用上述兩個(gè)方法,以便在后續(xù)的定時(shí)器中使用。
doAnimation(){ this.manWalk() this.treesMove() }
通過 setInterval 為“run”按鈕綁定走動(dòng)邏輯。
Button('run') .margin({right:10}) .type(ButtonType.Normal) .width(75) .borderRadius(5) .onClick(()=>{ this.clearAllInterval() //創(chuàng)建定時(shí)器,調(diào)用doAnimation方法,啟動(dòng)動(dòng)畫 lettimer=setInterval(this.doAnimation.bind(this),100) this.timerList.push(timer) })
通過 clearAllInterval 為“stop”按鈕綁定停止邏輯。
Button('stop') .type(ButtonType.Normal) .borderRadius(5) .width(75) .backgroundColor('#ff0000') .onClick(()=>{ //清理定時(shí)器,停止動(dòng)畫 this.clearAllInterval() })
完整代碼
本例完整代碼如下:
@Entry @Component exportdefaultstructframeAnimation{ //火柴人位置變量 @StatemanPostion:{ x:number, y:number }={x:0,y:0} //背景圖位置變量 @StatetreePosition:{ x:number, y:number }={x:0,y:0} //定時(shí)器列表,當(dāng)列表清空時(shí),動(dòng)畫停止 privatetimerList:number[]=[] //火柴人移動(dòng)方法 manWalk(){ if(this.manPostion.x<=?-517.902)?{ ??????this.manPostion.x?=?0 ????}?else?{ ??????this.manPostion.x?-=?129.69 ????} ??} ??//?背景移動(dòng)方法 ??treesMove()?{ ????if?(this.treePosition.x?<=?-1215)?{ ??????this.treePosition.x?=?0 ????}?else?{ ??????this.treePosition.x?-=?20 ????} ??} ??//?銷毀所有定時(shí)器 ??clearAllInterval()?{ ????this.timerList.forEach((timer:?number)?=>{ clearInterval(timer) }) this.timerList=[] } doAnimation(){ this.manWalk() this.treesMove() } build(){ Column(){ //父Row組件 Row(){ //子Row組件 Row(){ //通過Image組件顯示火柴人圖像 Image($r("app.media.man")) .height(60) .width(545.16) //通過translate實(shí)現(xiàn)火柴人的位移。綁定manPosition變量,用來改變火柴人位置。 .translate(this.manPostion) } .width(100) .justifyContent(FlexAlign.Start) .alignItems(VerticalAlign.Top) //截取顯示與背景同等大小的區(qū)域,控制單個(gè)火柴人顯示在畫面中 .clip(true) } //添加背景圖像 .backgroundImage($r("app.media.background")) //保持寬高比進(jìn)行縮小或者放大,使得圖片兩邊都大于或等于顯示邊界。 .backgroundImageSize(ImageSize.Cover) //通過backgroundImagePosition實(shí)現(xiàn)背景圖片的位移。綁定treePosition,用來改變背景圖片的位置。 .backgroundImagePosition(this.treePosition) .width('100%') .height(130) .justifyContent(FlexAlign.Center) .alignItems(VerticalAlign.Bottom) Row(){ //添加跑動(dòng)按鈕 Button('run') .margin({right:10}) .type(ButtonType.Normal) .width(75) .borderRadius(5) .onClick(()=>{ this.clearAllInterval() lettimer=setInterval(this.doAnimation.bind(this),100) this.timerList.push(timer) }) //添加停止按鈕 Button('stop') .type(ButtonType.Normal) .borderRadius(5) .width(75) .backgroundColor('#ff0000') .onClick(()=>{ this.clearAllInterval() }) }.margin({top:30,bottom:10}) }.width('100%').width('100%').padding({top:30}) } }
審核編輯:劉清
-
OpenHarmony
+關(guān)注
關(guān)注
25文章
3713瀏覽量
16254
原文標(biāo)題:OpenHarmony上實(shí)現(xiàn)逐幀動(dòng)畫
文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論