動畫的原理是在一個時間段內,多次改變UI外觀,由于人眼會產生視覺暫留,所以最終看到的就是一個“連續”的動畫。UI的一次改變稱為一個動畫幀,對應一次屏幕刷新,而決定動畫流暢度的一個重要指標就是幀率FPS(Frame Per Second),即每秒的動畫幀數,幀率越高則動畫就會越流暢。
ArkUI中,產生動畫的方式是改變屬性值且指定動畫參數。動畫參數包含了如動畫時長、變化規律(即曲線)等參數。當屬性值發生變化后,按照動畫參數,從原來的狀態過渡到新的狀態,即形成一個動畫。
1、動畫分類
2、常見動畫的使用
通過改變元素的寬高、位置、布局等觸發動畫
官方文檔-動畫
// 顯式動畫 https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-explicit-animation-0000001281480722
animateTo(value: AnimateParam, event: () => void): void
// 屬性動畫 https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-animatorproperty-0000001333321185
animation(value: AnimateParam)
// 轉場動畫-必須和animateTo配合使用 https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-page-transition-animation-0000001281201178
transition(value: TransitionOptions)
transition({ type: TransitionType.All, scale: { x: 0, y: 0 } }) // 全部使用一種動畫
transition({ type: TransitionType.Insert, translate: { x: 200, y: -200 }, opacity: 0 }) // 進入/插入動畫
transition({ type: TransitionType.Delete, rotate: { x: 0, y: 0, z: 1, angle: 360 } }) // 移出/刪除動畫
3、矩陣動畫的使用
這一塊我們重點關注幾個常用的屬性
3.1 translate(拖拽動畫實現的主要屬性)
translate({x?: number, y?: number, z?: number}): Object
當x,y坐標為0時,則意味著,每個元素按照各自的位置進行排列(例如:grid、list、Stack等)。
因此,我們可以根據元素的下標index,通過一些算法來改變坐標的位置,從而實現拖拽動畫,具體見代碼
DOM的實現
// isStartDrag 為是否開始拖拽,當開始拖拽時,我們動態改變矩陣中元素的坐標
Grid() {ForEach(this.selected, (item: dataListType) => {GridItem() {Text(item.text).blockTextStyle(this.blockWidth)}.translate({x: this.isStartDrag ? this.geyCoodXY(index).x : 0,y: this.isStartDrag? this.geyCoodXY(index).y : 0}).animation({duration: DURATION, // 動畫時長curve: Curve.Linear, // 動畫曲線iterations: 1, // 播放次數playMode: PlayMode.Normal // 動畫模式})})
}
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(16)
.rowsGap(16)
.editMode(true) //設置Grid是否進入編輯模式,進入編輯模式可以拖拽Grid組件內部GridItem
.supportAnimation(true) // 開啟動畫
坐標改變的算法
geyCoodXY(index) {const gridCol = this.getGridCol()let x = 0let y = 0if(this.insterIndex != -1) {if(index >= this.insterIndex) {// 判斷是否需要換行// 需要取余如果等于0,則需要換行,需要進行下移和左移if(parseInt(((index) % gridCol).toString()) === gridCol - 1) {// 判斷是否為當前列的最后一個if(this.options.type === 'object') {x = x - this.blockWidth * (gridCol - 1) - 16 * (gridCol - 2) - 19y = y + this.blockHeight - 16.5} else {x = x - this.blockWidth * (gridCol - 1) - 16 * (gridCol - 2) - 13y = y + this.blockHeight + 18}} else {// 默認右移x = x + 16 + this.blockWidth + 1}}if(!this.isStartDrag) {x = 0y = 0}}return {x: x,y: y}}/*** 獲取Grid高度計算是否需要+1* 場景1:當前數組(data)長度小于列(colNum)的長度* 場景2:當前數組的長度等于拖拽前的長度 && 對數組長度%列長度區域不為0* */getGridNum(data) {let len = data.lengthlet num = 0if(len < this.colNum) {num = 1}if(parseInt((len % this.colNum).toString()) !== 0 && this.editGridDataLength === len) {num = 1}return num}/*** 獲取當前布局列數* 默認:文本COL_TEXT:4* 默認:圖文COL_IMAGE_TEXT:3* */getGridCol() {return this.options.type === 'object' ? COL_IMAGE_TEXT : COL_TEXT}
3.2 scale
縮放函數,配合transform
進行使用
scale({x?: number, y?: number, z?: number, centerX?: number, centerY?: number}): Object
3.3 rotate
旋轉函數
rotate({x?: number, y?: number, z?: number, angle?: number, centerX?: Length, centerY?: Length}): Object
3.4 transformPoint
坐標映射,可以將當前的變換效果作用到一個坐標點上。
源碼地址:MeshObjectEdit
4、?注意點
4.1 Grid布局中的Item使用屬性動畫時,只能使用自帶的curve,無法自定義
4.2 底層渲染問題
?在開發拖拽動畫時,發現png的圖片在拖拽結束后,會出現圖片閃動的不流暢問題,改為svg圖片解決。因此通過大量的對比驗證,確認為鴻蒙底層竄然問題。