輕松上手-骨架屏后動畫顯示

作者:狼哥
團(tuán)隊(duì):堅(jiān)果派
團(tuán)隊(duì)介紹:堅(jiān)果派由堅(jiān)果等人創(chuàng)建,團(tuán)隊(duì)擁有12個華為HDE帶領(lǐng)熱愛HarmonyOS/OpenHarmony的開發(fā)者,以及若干其他領(lǐng)域的三十余位萬粉博主運(yùn)營。專注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服務(wù)、倉頡。團(tuán)隊(duì)成員聚集在北京,上海,南京,深圳,廣州,寧夏等地,目前已開發(fā)鴻蒙原生應(yīng)用,三方庫60+,歡迎交流。

介紹

在移動應(yīng)用開發(fā)中,采用骨架屏(Skeleton Screen)作為加載策略,具有顯著的優(yōu)勢。首先,骨架屏能夠即時反饋給用戶頁面正在加載中,有效緩解了因網(wǎng)絡(luò)延遲或數(shù)據(jù)處理造成的“白屏”現(xiàn)象,提升了用戶體驗(yàn)的流暢度與期待感。它以一種輕量級、占位符的形式預(yù)先展示頁面結(jié)構(gòu),讓用戶對即將呈現(xiàn)的內(nèi)容有所預(yù)期,減少了等待時的焦慮感。

其次,骨架屏設(shè)計(jì)簡潔且高度自定義,能夠根據(jù)應(yīng)用風(fēng)格和頁面布局靈活調(diào)整,保持界面的一致性和美觀性。這種視覺上的連續(xù)性不僅增強(qiáng)了應(yīng)用的品牌形象,也提升了用戶對產(chǎn)品的信任度和好感度。

再者,從性能優(yōu)化的角度來看,骨架屏相比完整的頁面內(nèi)容加載更快,因?yàn)樗鼉H包含基礎(chǔ)的框架結(jié)構(gòu)和動畫效果,減少了初始加載的數(shù)據(jù)量,有助于提升應(yīng)用的加載速度和響應(yīng)能力。這對于提升用戶留存率和轉(zhuǎn)化率具有重要意義。

綜上所述,骨架屏作為現(xiàn)代移動應(yīng)用設(shè)計(jì)中的一種高效加載策略,以其即時反饋、美化等待體驗(yàn)、提升性能及增強(qiáng)品牌一致性等優(yōu)勢,成為提升用戶體驗(yàn)不可或缺的一環(huán)。

效果預(yù)覽

1723867551658.gif

image.png

image.png

image.png

image.png

image.png

image.png

image.png

知識點(diǎn)

1. 顯式動畫 (animateTo)
2. 組件內(nèi)轉(zhuǎn)場 (transition)
3. if/else:條件渲染
4. ForEach:循環(huán)渲染

工程目錄

├──entry/src/main/ets                        // 代碼區(qū)
│  ├──components
│  │  └──TextAreaBuilder.ets                 // 骨架屏占位組件 
│  ├──entryability
│  │  └──EntryAbility.ets 
│  ├──pages
│  │  └──Index.ets                           // 首頁
│  └──view
│     ├──GridSkeleton.ets                    // Grid骨架圖
│     ├──GridView.ets                        // Grid布局圖
│     ├──ListSkeleton.ets                    // List骨架圖
│     └──ListView.ets                        // List布局圖
│     ├──SwiperSkeleton.ets                  // Swiper骨架圖
│     └──SwiperView.ets                      // Swiper布局圖
└──entry/src/main/resources                  // 應(yīng)用資源目錄

具體實(shí)現(xiàn)

在HarmonyOS(鴻蒙系統(tǒng))中實(shí)現(xiàn)骨架屏(Skeleton Screen)通常用于提升應(yīng)用的加載體驗(yàn),特別是在網(wǎng)絡(luò)請求數(shù)據(jù)尚未返回時顯示一個大致的頁面框架,讓用戶知道頁面正在加載中。下面介紹如何使用`if/else`渲染、`foreach`渲染、顯式動畫以及組件內(nèi)轉(zhuǎn)場等技術(shù)在HarmonyOS中實(shí)現(xiàn)骨架屏。

**骨架屏基礎(chǔ)設(shè)計(jì)**

設(shè)計(jì)你的骨架屏布局。骨架屏通常包括簡單的線條、圓形等,以模擬最終加載完成后的頁面結(jié)構(gòu)??梢允褂迷赥extAreaBuilder.ets里封裝的`textArea` Builder來創(chuàng)建。
1. 骨架屏占位組件
@Builder
export function textArea(
  width: number | Resource | string = '100%',
  height: number | Resource | string = '100%',
  borderRadius: Length | BorderRadiuses | LocalizedBorderRadiuses = 0,
  padding: Length | Padding | LocalizedPadding = 0,
  margin: Length | Padding | LocalizedPadding = 0) {
  Row()
    .width(width)
    .height(height)
    .backgroundColor('#FFF2F3F4')
    .borderRadius(borderRadius)
    .padding(padding)
    .margin(margin)
}
2. 首頁
    首頁由Swiper視圖、Grid視圖、List視圖組成,各自布局和邏輯在各自上實(shí)現(xiàn)。
@Entry
@Component
struct Index {
    build() {
    Column({ space: 20 }) {
      SwiperView()
      GridView()
      ListView()
    }
    .height('100%')
    .width('100%')
  }
}
3. Swiper實(shí)現(xiàn)
Swiper骨架圖
import { textArea } from '../components/TextAreaBuilder'
@Component
export struct SwiperSkeleton {
  build() {
    textArea('90%', px2vp(460), 16, 0, {top: 11})
  }
}
Swiper實(shí)現(xiàn)與布局
  @State isLoadingSwiperData: boolean = true;
  aboutToAppear(): void {
    // 模擬異步回調(diào)
    setTimeout(() => {
      animateTo({ duration: 1000 }, () => {
        this.isLoadingSwiperData = false;
      })
    }, 4000)
  }
  Swiper() {
    ForEach(this.bannerList, (item: BannerClass) => {
      if (this.isLoadingSwiperData) {
        SwiperSkeleton()
      } else {
        Image($r(item.imageSrc))
          .objectFit(ImageFit.Contain)
          .width('100%')
          .borderRadius(16)
          .transition({ type: TransitionType.Insert, translate: { x: 700, y: 0 } })
      }
    }, (item: BannerClass) => item.id.toString())
  }
4. Grid實(shí)現(xiàn)
Grid骨架圖
import { textArea } from '../components/TextAreaBuilder'
@Component
export struct GridSkeleton {
  build() {
    Column({space: 15}) {
      textArea('100%', '60%', { topLeft: 16, topRight: 16 })
      Column({space: 15}) {
        textArea('60%', '15%')
        textArea('90%', '25%')
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)
      .padding({left: 15})
    }
    .height('100%')
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }
}
Grid實(shí)現(xiàn)與布局
  @State isLoadingGridData: boolean = true;
  aboutToAppear(): void {
    // 模擬異步回調(diào)
    setTimeout(() => {
      animateTo({ duration: 3000 }, () => {
        this.isLoadingGridData = false;
      })
    }, 6000)
  }
  Grid() {
    ForEach(this.enablementList, (item: ArticleClass) => {
      GridItem() {
        if (this.isLoadingGridData) {
          GridSkeleton()
        } else {
          EnablementItem({ enablementItem: item })
            .transition({ type: TransitionType.Insert, translate: { x: -700, y: 0 } })
        }

      }
      .width(160)
    }, (item: ArticleClass) => item.id.toString())
  }
5. List實(shí)現(xiàn)
List骨架圖
import { textArea } from '../components/TextAreaBuilder'
@Component
export struct ListSkeleton {
  build() {
    Row({space: 10}) {
      Column({space: 10}) {
        textArea('60%', '20%')
        textArea('90%', '70%')
      }
      .width('70%')
      .alignItems(HorizontalAlign.Start)
      textArea('30%', '100%', 16)
    }
    .height(88)
    .margin({ bottom: 10 })
    .justifyContent(FlexAlign.SpaceBetween)
  }
}
List實(shí)現(xiàn)與布局
  @State isLoadingListData: boolean = true;
  aboutToAppear(): void {
    // 模擬異步回調(diào)
    setTimeout(() => {
      animateTo({ duration: 3000 }, () => {
        this.isLoadingListData = false;
      })
    }, 8000)
  }
List({ space: 12 }) {
    ForEach(this.tutorialList, (item: ArticleClass) => {
      ListItem() {
        if (this.isLoadingListData) {
          ListSkeleton()
        } else {
          TutorialItem({ tutorialItem: item })
            .transition({ type: TransitionType.Insert, translate: { x: 0, y: 500 } })
        }
      }
    }, (item: ArticleClass) => item.id.toString())
  }

總結(jié)

    通過此案例,可以學(xué)習(xí)到顯式動畫知識點(diǎn),全局animateTo顯式動畫接口來指定由于閉包代碼導(dǎo)致的狀態(tài)變化插入過渡動效。同屬性動畫,布局類改變寬高的動畫,內(nèi)容都是直接到終點(diǎn)狀態(tài)。組件內(nèi)轉(zhuǎn)場主要通過transition屬性配置轉(zhuǎn)場參數(shù),在組件插入和刪除時顯示過渡動效,主要用于容器組件中的子組件插入和刪除時,提升用戶體驗(yàn)。if/else條件渲染,可根據(jù)應(yīng)用的不同狀態(tài),使用if、else和else if渲染對應(yīng)狀態(tài)下的UI內(nèi)容。ForEach循環(huán)渲染,F(xiàn)orEach接口基于數(shù)組類型數(shù)據(jù)來進(jìn)行循環(huán)渲染,需要與容器組件配合使用,且接口返回的組件應(yīng)當(dāng)是允許包含在ForEach父容器組件中的子組件。例如,ListItem組件要求ForEach的父容器組件必須為List組件??傊?,以后在很多異步返回?cái)?shù)據(jù)前,都可以先顯示骨架屏,讓用戶知道頁面正在加載中,在HarmonyOS中實(shí)現(xiàn)骨架屏需要結(jié)合布局設(shè)計(jì)、數(shù)據(jù)綁定、條件渲染、動畫以及可能的組件內(nèi)狀態(tài)管理。

約束與限制

1.本示例僅支持標(biāo)準(zhǔn)系統(tǒng)上運(yùn)行,支持設(shè)備:華為手機(jī)。

2.HarmonyOS系統(tǒng):HarmonyOS NEXT Developer Beta1及以上。

3.DevEco Studio版本:DevEco Studio NEXT Developer Beta1及以上。

4.HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta1 SDK及以上。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容