ArkUI為組件提供了通用的屬性動畫和轉場動畫能力的同時,還為一些組件提供了默認的動畫效果。例如,List的滑動動效,Button的點擊動效,是組件自帶的默認動畫效果。在組件默認動畫效果的基礎上,開發(fā)者還可以通過屬性動畫和轉場動畫對容器組件內的子組件動效進行定制。
使用組件默認動畫
組件默認動效具備以下功能:
- 提示用戶當前狀態(tài),例如用戶點擊Button組件時,Button組件默認變灰,用戶即確定完成選中操作。
- 提升界面精致程度和生動性。
- 減少開發(fā)者工作量,例如列表滑動組件自帶滑動動效,開發(fā)者直接調用即可。
更多效果,可以參考組件說明。
示例代碼和效果如下。
@Entry
@Component
struct ComponentDemo {
build() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(true)
.shape(CheckBoxShape.CIRCLE)
.size({ width: 50, height: 50 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
ts
打造組件定制化動效
部分組件支持通過屬性動畫和轉場動畫自定義組件子Item的動效,實現定制化動畫效果。例如,Scroll組件中可對各個子組件在滑動時的動畫效果進行定制。
- 在滑動或者點擊操作時通過改變各個Scroll子組件的仿射屬性來實現各種效果。
- 如果要在滑動過程中定制動效,可在滑動回調onScroll中監(jiān)控滑動距離,并計算每個組件的仿射屬性。也可以自己定義手勢,通過手勢監(jiān)控位置,手動調用ScrollTo改變滑動位置。
- 在滑動回調onScrollStop或手勢結束回調中對滑動的最終位置進行微調。
定制Scroll組件滑動動效示例代碼和效果如下。
import curves from '@ohos.curves';
import window from '@ohos.window';
import display from '@ohos.display';
import mediaquery from '@ohos.mediaquery';
import UIAbility from '@ohos.app.ability.UIAbility';
export default class GlobalContext extends AppStorage{
static mainWin: window.Window|undefined = undefined;
static mainWindowSize:window.Size|undefined = undefined;
}
/**
* 窗口、屏幕相關信息管理類
*/
export class WindowManager {
private static instance: WindowManager|null = null;
private displayInfo: display.Display|null = null;
private orientationListener = mediaquery.matchMediaSync('(orientation: landscape)');
constructor() {
this.orientationListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) = > { this.onPortrait(mediaQueryResult) })
this.loadDisplayInfo()
}
/**
* 設置主window窗口
* @param win 當前app窗口
*/
setMainWin(win: window.Window) {
if (win == null) {
return
}
GlobalContext.mainWin = win;
win.on("windowSizeChange", (data: window.Size) = > {
if (GlobalContext.mainWindowSize == undefined || GlobalContext.mainWindowSize == null) {
GlobalContext.mainWindowSize = data;
} else {
if (GlobalContext.mainWindowSize.width == data.width && GlobalContext.mainWindowSize.height == data.height) {
return
}
GlobalContext.mainWindowSize = data;
}
let winWidth = this.getMainWindowWidth();
AppStorage.setOrCreate< number >('mainWinWidth', winWidth)
let winHeight = this.getMainWindowHeight();
AppStorage.setOrCreate< number >('mainWinHeight', winHeight)
let context:UIAbility = new UIAbility()
context.context.eventHub.emit("windowSizeChange", winWidth, winHeight)
})
}
static getInstance(): WindowManager {
if (WindowManager.instance == null) {
WindowManager.instance = new WindowManager();
}
return WindowManager.instance
}
private onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
if (mediaQueryResult.matches == AppStorage.get< boolean >('isLandscape')) {
return
}
AppStorage.setOrCreate< boolean >('isLandscape', mediaQueryResult.matches)
this.loadDisplayInfo()
}
/**
* 切換屏幕方向
* @param ori 常量枚舉值:window.Orientation
*/
changeOrientation(ori: window.Orientation) {
if (GlobalContext.mainWin != null) {
GlobalContext.mainWin.setPreferredOrientation(ori)
}
}
private loadDisplayInfo() {
this.displayInfo = display.getDefaultDisplaySync()
AppStorage.setOrCreate< number >('displayWidth', this.getDisplayWidth())
AppStorage.setOrCreate< number >('displayHeight', this.getDisplayHeight())
}
/**
* 獲取main窗口寬度,單位vp
*/
getMainWindowWidth(): number {
return GlobalContext.mainWindowSize != null ? px2vp(GlobalContext.mainWindowSize.width) : 0
}
/**
* 獲取main窗口高度,單位vp
*/
getMainWindowHeight(): number {
return GlobalContext.mainWindowSize != null ? px2vp(GlobalContext.mainWindowSize.height) : 0
}
/**
* 獲取屏幕寬度,單位vp
*/
getDisplayWidth(): number {
return this.displayInfo != null ? px2vp(this.displayInfo.width) : 0
}
/**
* 獲取屏幕高度,單位vp
*/
getDisplayHeight(): number {
return this.displayInfo != null ? px2vp(this.displayInfo.height) : 0
}
/**
* 釋放資源
*/
release() {
if (this.orientationListener) {
this.orientationListener.off('change', (mediaQueryResult: mediaquery.MediaQueryResult) = > { this.onPortrait(mediaQueryResult)})
}
if (GlobalContext.mainWin != null) {
GlobalContext.mainWin.off('windowSizeChange')
}
WindowManager.instance = null;
}
}
/**
* 封裝任務卡片信息數據類
*/
export class TaskData {
bgColor: Color | string | Resource = Color.White;
index: number = 0;
taskInfo: string = 'music';
constructor(bgColor: Color | string | Resource, index: number, taskInfo: string) {
this.bgColor = bgColor;
this.index = index;
this.taskInfo = taskInfo;
}
}
export const taskDataArr: Array< TaskData > =
[
new TaskData('#317AF7', 0, 'music'),
new TaskData('#D94838', 1, 'mall'),
new TaskData('#DB6B42 ', 2, 'photos'),
new TaskData('#5BA854', 3, 'setting'),
new TaskData('#317AF7', 4, 'call'),
new TaskData('#D94838', 5, 'music'),
new TaskData('#DB6B42', 6, 'mall'),
new TaskData('#5BA854', 7, 'photos'),
new TaskData('#D94838', 8, 'setting'),
new TaskData('#DB6B42', 9, 'call'),
new TaskData('#5BA854', 10, 'music')
];
@Entry
@Component
export struct TaskSwitchMainPage {
displayWidth: number = WindowManager.getInstance().getDisplayWidth();
scroller: Scroller = new Scroller();
cardSpace: number = 0; // 卡片間距
cardWidth: number = this.displayWidth / 2 - this.cardSpace / 2; // 卡片寬度
cardHeight: number = 400; // 卡片高度
cardPosition: Array< number > = []; // 卡片初始位置
clickIndex: boolean = false;
@State taskViewOffsetX: number = 0;
@State cardOffset: number = this.displayWidth / 4;
lastCardOffset: number = this.cardOffset;
startTime: number|undefined=undefined
// 每個卡片初始位置
aboutToAppear() {
for (let i = 0; i < taskDataArr.length; i++) {
this.cardPosition[i] = i * (this.cardWidth + this.cardSpace);
}
}
// 每個卡片位置
getProgress(index: number): number {
let progress = (this.cardOffset + this.cardPosition[index] - this.taskViewOffsetX + this.cardWidth / 2) / this.displayWidth;
return progress
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
// 背景
Column()
.width('100%')
.height('100%')
.backgroundColor(0xF0F0F0)
// 滑動組件
Scroll(this.scroller) {
Row({ space: this.cardSpace }) {
ForEach(taskDataArr, (item:TaskData, index) = > {
Column()
.width(this.cardWidth)
.height(this.cardHeight)
.backgroundColor(item.bgColor)
.borderStyle(BorderStyle.Solid)
.borderWidth(1)
.borderColor(0xAFEEEE)
.borderRadius(15)
// 計算子組件的仿射屬性
.scale((this.getProgress(index) >= 0.4 && this.getProgress(index) <= 0.6) ?
{
x: 1.1 - Math.abs(0.5 - this.getProgress(index)),
y: 1.1 - Math.abs(0.5 - this.getProgress(index))
} :
{ x: 1, y: 1 })
.animation({ curve: Curve.Smooth })
// 滑動動畫
.translate({ x: this.cardOffset })
.animation({ curve: curves.springMotion() })
.zIndex((this.getProgress(index) >= 0.4 && this.getProgress(index) <= 0.6) ? 2 : 1)
}, (item:TaskData) = > item.toString())
}
.width((this.cardWidth + this.cardSpace) * (taskDataArr.length + 1))
.height('100%')
}
.gesture(
GestureGroup(GestureMode.Parallel,
PanGesture({ direction: PanDirection.Horizontal, distance: 5 })
.onActionStart((event: GestureEvent|undefined) = > {
if(event){
this.startTime = event.timestamp;
}
})
.onActionUpdate((event: GestureEvent|undefined) = > {
if(event){
this.cardOffset = this.lastCardOffset + event.offsetX;
}
})
.onActionEnd((event: GestureEvent|undefined) = > {
if(event){
let time = 0
if(this.startTime){
time = event.timestamp - this.startTime;
}
let speed = event.offsetX / (time / 1000000000);
let moveX = Math.pow(speed, 2) / 7000 * (speed > 0 ? 1 : -1);
this.cardOffset += moveX;
// 左滑大于最右側位置
let cardOffsetMax = -(taskDataArr.length - 1) * (this.displayWidth / 2);
if (this.cardOffset < cardOffsetMax) {
this.cardOffset = cardOffsetMax;
}
// 右滑大于最左側位置
if (this.cardOffset > this.displayWidth / 4) {
this.cardOffset = this.displayWidth / 4;
}
// 左右滑動距離不滿足/滿足切換關系時,補位/退回
let remainMargin = this.cardOffset % (this.displayWidth / 2);
if (remainMargin < 0) {
remainMargin = this.cardOffset % (this.displayWidth / 2) + this.displayWidth / 2;
}
if (remainMargin <= this.displayWidth / 4) {
this.cardOffset += this.displayWidth / 4 - remainMargin;
} else {
this.cardOffset -= this.displayWidth / 4 - (this.displayWidth / 2 - remainMargin);
}
// 記錄本次滑動偏移量
this.lastCardOffset = this.cardOffset;
}
})
), GestureMask.IgnoreInternal)
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
// 滑動到首尾位置
Button('Move to first/last')
.backgroundColor(0x888888)
.margin({ bottom: 30 })
.onClick(() = > {
this.clickIndex = !this.clickIndex;
if (this.clickIndex) {
this.cardOffset = this.displayWidth / 4;
} else {
this.cardOffset = this.displayWidth / 4 - (taskDataArr.length - 1) * this.displayWidth / 2;
}
this.lastCardOffset = this.cardOffset;
})
}
.width('100%')
.height('100%')
}
}
ts
審核編輯 黃宇
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
組件
+關注
關注
1文章
532瀏覽量
18423 -
鴻蒙
+關注
關注
60文章
2620瀏覽量
44052 -
OpenHarmony
+關注
關注
29文章
3854瀏覽量
18607
發(fā)布評論請先 登錄
相關推薦
熱點推薦
OpenHarmony實戰(zhàn)開發(fā)-如何實現動畫幀
你們的 『點贊和評論』,才是我創(chuàng)造的動力。
關注小編,同時可以期待后續(xù)文章ing?,不定期分享原創(chuàng)知識。
更多鴻蒙最新技術知識點,請關注作者博客:鴻蒙實戰(zhàn)經驗分享:鴻蒙基礎入門開發(fā)寶典! (qq.com)**
發(fā)表于 05-06 14:11
OpenHarmony實戰(zhàn)開發(fā)-如何實現窗口開發(fā)概述
操作系統(tǒng)而言,窗口模塊提供了不同應用界面的組織管理邏輯。
窗口模塊的用途
在OpenHarmony中,窗口模塊主要負責以下職責:
提供應用和系統(tǒng)界面的窗口對象。 應用開發(fā)者通過窗口加載UI界面,實現界面
發(fā)表于 05-06 14:29
HarmonyOS Lottie組件,讓動畫繪制更簡單
了豐富的API,讓開發(fā)者能輕松控制動畫,大大提高了開發(fā)效率。
二、Lottie實戰(zhàn)
通過上文對Lottie的介紹,相信很多小伙伴已經感受到了Lottie
發(fā)表于 02-22 14:55
OpenHarmony標準設備應用開發(fā)筆記匯總
如何在標準設備上運行一個最簡單的 OpenHarmony 程序。2、如何在OpenHarmony中實現音樂的播放本章是 OpenHarmony 標準設備應用
發(fā)表于 03-28 14:19
OpenHarmony標準設備應用開發(fā)(二)——布局、動畫與音樂
節(jié)的基礎上,學到更多 ArkUI 組件和布局在 OpenHarmony 中的應用,以及如何在自己的應用中實現顯示動畫的效果。代碼鏈接:https://gitee.com/
發(fā)表于 04-07 17:09
OpenHarmony有氧拳擊之應用端開發(fā)
變化的動畫,便很適合使用屬性動畫來實現。屬性動畫是指組件的通用屬性發(fā)生變化時,會根據開始狀態(tài)和通用屬性改變后的狀態(tài)作為
發(fā)表于 10-09 15:19
HarmonyOS/OpenHarmony應用開發(fā)-屬性動畫
組件的某些通用屬性變化時,可以通過屬性動畫實現漸變過渡效果,提升用戶體驗。支持的屬性包括width、height、backgroundColor、opacity、scale、rotate
發(fā)表于 01-03 10:51
OpenHarmony應用開發(fā)—ArkUI組件集合
頁面
接口參考:@ohos.curves, @ohos.router
顯示動畫
用到全局組件TitleBar,IntroductionTitle實現頁面
接口參考:animateTo
屬性
發(fā)表于 09-22 14:56
基于openharmony適配移植的多種動畫效果實現教程
項目介紹 項目名稱: OhosLoadingAnimation 所屬系列:openharmony的第三方組件適配移植 功能:實現多種動畫效果 項目移植狀態(tài):主功能完成 調用差異
發(fā)表于 04-02 10:55
?0次下載
openharmony第三方組件適配移植的動畫庫實現
項目介紹 項目名稱:CanAnimation 所屬系列:openharmony的第三方組件適配移植 功能:使用openharmony屬性動畫寫的一個庫,可組建
發(fā)表于 04-02 11:30
?3次下載
openharmony第三方組件適配移植的SVGA動畫渲染庫
項目介紹 項目名稱:SVGAPlayer-Ohos 所屬系列:openharmony的第三方組件適配移植 功能:SVGAPlayer-Ohos 是一個輕量的動畫渲染庫。你可以使用工具從 Adobe
發(fā)表于 04-02 11:47
?15次下載
2022 OpenHarmony組件大賽,共建開源組件
原標題:共建開源組件生態(tài) 2022 OpenHarmony組件大賽等你來 2022年4月15日,2022 OpenHarmony組件大賽(下

OpenHarmony輕量系統(tǒng)書籍推薦《OpenHarmony輕量設備開發(fā)理論與實戰(zhàn)》
最近大家問的智能家居套件方面有沒有可以參考的資料,這里給大家統(tǒng)一回復一下 推薦大家可以看這本書 《OpenHarmony輕量設備開發(fā)理論與實戰(zhàn)》 本書系統(tǒng)地講授OpenHarmony
評論