鸿蒙开发中的骨架图:提升用户体验的关键一环

大家好,我是小 z,今天要给大家分享一个提升用户体验的超实用技巧 ------ 骨架图🎯

文章目录

在当今快节奏的数字化时代,用户对于应用程序的体验要求越来越高。一个响应迅速、视觉流畅的应用,往往能在众多竞品中脱颖而出🌟。在鸿蒙开发中,骨架图作为优化用户体验的重要手段,正逐渐受到开发者们的广泛关注。

一、什么是骨架图

骨架图,简单来说,是一种在数据尚未加载完成时,展示页面大致结构的占位图形。它就像一个精心搭建的建筑蓝图🏗️,以简洁的线条和几何形状勾勒出页面的主要元素,如标题栏、列表项、图片区域等。当用户打开应用,首先映入眼帘的不再是一片空白,而是一个大致的页面框架,为后续填充具体内容提供了基础。

二、骨架图的作用

  • 减少用户等待焦虑😟:在网络环境不佳或数据量较大的情况下,数据加载可能需要一定时间。若没有骨架图,用户面对的将是长时间的空白屏幕,这极易引发用户的焦虑情绪,甚至导致用户放弃使用应用。而骨架图的出现,让用户明确知道页面正在加载,并且对即将呈现的内容有了初步预期,从而有效缓解等待过程中的焦虑。
  • 提升视觉连贯性🎨:从空白页面到突然加载出完整内容,这种突兀的转变可能会给用户带来视觉上的不适感。骨架图则能在加载过程中保持页面的视觉连贯性,通过逐渐过渡到真实内容,为用户提供一种流畅、自然的视觉体验。

三、鸿蒙开发中实现骨架图的方法

在鸿蒙开发中,opacity(透明度)属性与animateTo函数的精妙搭配,为构建引人入胜的骨架图效果提供了有力支持,极大地优化了用户体验。

1. 利用 opacity 奠定视觉基础

在给定的代码里,TravelSkeletonView组件通过@State columnOpacity: number = 1定义了透明度状态变量columnOpacity ,初始值设为 1。这意味着在页面加载的初始阶段,骨架图以完全不透明的状态呈现,用户能够清晰看到页面的大致结构,比如各个占位元素所代表的区域布局。这种清晰的初始展示,为后续的动态变化提供了稳定的视觉基准,就像是搭建舞台,先把布景布置好🎭。

2. animateTo 驱动动态变化

typescript 复制代码
startAnimation(): void{
    animateTo(CommonConstants.SKELETON_ANIMATION, () => {
      this.columnOpacity = 0.5
    })
  }
  • animateTo函数在整个骨架图动态展示中扮演着核心驱动角色。它接收两个关键参数,第一个参数CommonConstants.SKELETON_ANIMATION 详细定义了动画的各项特性。其中,持续时间设定为 400 毫秒,这决定了整个动画从开始到结束的时长;节奏设为 0.6,影响动画的速度变化;缓动曲线选择Curve.EaseInOut,使得动画在开始和结束时都较为平滑,避免突兀;延迟时间 200 毫秒,让动画在组件出现 200 毫秒后才启动,给予用户一定的适应时间;迭代次数设为无限次,且播放模式为PlayMode.Alternate(交替播放),这意味着动画会不断循环,且每次播放方向相反。想象一下,这就像是舞台上的灯光在慢慢闪烁,吸引着观众的注意力✨。
  • 第二个参数是一个回调函数,当动画执行时,会将columnOpacity的值从初始的 1 改变为 0.5。结合opacity属性来看,在build方法中,Column组件通过.opacity(this.columnOpacity)将自身的透明度与columnOpacity绑定。所以,随着animateTo函数驱动columnOpacity值的改变,整个骨架图组件的透明度会逐渐降低。这一过程模拟出数据加载时,骨架图逐渐被真实内容替代的视觉效果,给用户一种数据正在逐步填充的直观感受,仿佛舞台上的演员在慢慢走上台,替换掉了之前的占位道具🎭。

3. 二者协同触发与展示

在build方法里,.opacity(this.columnOpacity).onAppear(() => { this.startAnimation() })这部分代码至关重要。.opacity(this.columnOpacity)确保了骨架图组件的透明度实时跟随columnOpacity值变化。而.onAppear(() => { this.startAnimation() })则表明,当骨架图组件被渲染到屏幕上时,startAnimation方法会立即被触发。也就是说,组件一出现,animateTo函数驱动的动画就开始改变骨架图的透明度,从而向用户展示数据加载的动态过程。这种动态变化不仅增强了页面的视觉层次感,还巧妙地暗示了用户数据加载的进程,让用户在等待数据的过程中,有更丰富的视觉反馈,而不是面对单调的空白等待。

四、完整代码

  • 使用的color和AnimateParam对象
typescript 复制代码
{
  "color": [
    {
      "name": "color_1",
      "value": "#ff0000"
    },
    {
      "name": "skeleton_color_deep",
      "value": "#1A000000"
    },
    {
      "name": "skeleton_color_medium",
      "value": "#FFF2F3F4"
    },
    {
      "name": "skeleton_color_light",
      "value": "#FFECECEC"
    },
    {
      "name": "skeleton_color",
      "value": "#ECECEC"
    }
  ]
}

static readonly SKELETON_ANIMATION: AnimateParam = {
    duration: 400,
    tempo: 0.6,
    curve: Curve.EaseInOut,
    delay: 200,
    iterations: -1,
    playMode: PlayMode.Alternate
  }
  • 整体骨架视图
typescript 复制代码
import { BreakpointConstants, BreakpointType, CommonConstants } from "utils";
import { NearbySpotLoadingSkeleton } from "./NearbySpotLoadingSkeleton";

const NEARBY_VISIBLE_LENGTH = 10


@Component
export struct TravelSkeletonView {
  nearbySpots: Array<Number> = new Array(NEARBY_VISIBLE_LENGTH).fill(1).map((v: number, index: number) => index + 1);
  @State columnOpacity: number = 1

  @StorageLink('currentHeightBreakpoint') currentHeightBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
  @StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;

  startAnimation(): void{
    animateTo(CommonConstants.SKELETON_ANIMATION, () => {
      this.columnOpacity = 0.5
    })
  }

  build() {
    Column() {
      Row() {
        Row() {
        }
        .alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.Center)
        .backgroundColor($r('app.color.skeleton_color'))
        .height(20)
        .width('20%')
        .padding(5)

        Blank()

        Row() {
        }
        .width('15%')
        .height(20)
        .backgroundColor($r('app.color.skeleton_color'))
      }
      .alignItems(VerticalAlign.Center)
      .width('100%')

      List() {
        ForEach(this.nearbySpots, (item:number) => {
          ListItem() {
            NearbySpotLoadingSkeleton()
          }
          .margin({ left: 5, right: 5 })
        })
      }
      .nestedScroll({
        scrollForward: NestedScrollMode.PARENT_FIRST,
        scrollBackward: NestedScrollMode.SELF_FIRST
      })
      .lanes(new BreakpointType({ sm: 1, md: 1, lg: 2 }).getValue(this.currentWidthBreakpoint))
      .scrollBar(BarState.Off)
      .width('100%')
      .layoutWeight(1)
      .listDirection(Axis.Vertical)
    }
    .opacity(this.columnOpacity)
    .onAppear(() => {
      this.startAnimation()
    })
    .alignItems(HorizontalAlign.Start)
    .width('100%')
  }
}
  • 具体渲染骨架组件
typescript 复制代码
import Constants from "../constants/Constants"

@Component
export struct NearbySpotLoadingSkeleton{

  build() {
    Row() {
      Column() {
        Row()
          .width('100%')
          .height(60)
          .backgroundColor($r('app.color.skeleton_color_deep'))

        Row()
          .height(20)
          .width("40%")
          .margin({ top: 5})
          .backgroundColor($r('app.color.skeleton_color_medium'))

      }
      .width('40%')
      .padding(8)
      .alignItems(HorizontalAlign.Center)

      Column() {
        Row()
          .height(20)
          .width("30%")
          .backgroundColor($r('app.color.skeleton_color_medium'))

        Row()
          .height(20)
          .width('80%')
          .padding({ top: 5 })
          .backgroundColor($r('app.color.skeleton_color_light'))

      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.End)

    }
    .borderRadius(Constants.BORDER_RADIUS_MD)
    .backgroundColor(Color.White)
    .height(100)
    .width('100%')
    .padding(5)
    .margin({ top: 5, bottom: 5, left: 10, right: 10})
  }
}

在实际开发中,nearbySpots: Array = new Array(NEARBY_VISIBLE_LENGTH).fill(1).map((v: number, index: number) => index + 1);确定了要渲染的骨架数量。开发者完全可以依据应用的整体风格以及用户体验需求,灵活调整animateTo函数中的动画参数,如时长、缓动曲线、关键帧等,同时搭配opacity的变化范围,从而打造出各种独具特色且舒适的骨架图加载效果。让我们一起用这些技术,为用户带来更加精彩的应用体验吧🎉!

相关推荐
用户545748341778 分钟前
HarmonyOS Next应用开发实战——登录页面实现(part1)
harmonyos
用户5457483417710 分钟前
HarmonyOS Next应用开发实战——底部弹框组件的实现(part1)
harmonyos
用户5457483417712 分钟前
HarmonyOS Next应用开发实战——底部弹框组件的实现(part2)
harmonyos
用户5457483417713 分钟前
HarmonyOS Next应用开发实战——多功能页面组件构建(part1)
harmonyos
用户5457483417713 分钟前
HarmonyOS Next应用开发实战——多功能页面组件构建(part2)
harmonyos
东锋1.322 分钟前
DeepSeek V3可用的15种精美知识卡片提示词
人工智能·信息可视化
星释23 分钟前
鸿蒙Flutter实战:18-组合而非替换,现有插件快速鸿蒙化
flutter·华为·harmonyos
90后的晨仔1 小时前
鸿蒙开发中的常见关键字简单总结
harmonyos
二流小码农1 小时前
鸿蒙开发:使用Ellipse绘制椭圆
android·ios·harmonyos
别说我什么都不会2 小时前
OpenHarmony解读之设备认证:sts协议-客户端发起start请求
物联网·嵌入式·harmonyos