自学内容网 自学内容网

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

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

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

一、什么是骨架图

骨架图

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

二、骨架图的作用

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

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

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

1. 利用 opacity 奠定视觉基础

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

2. animateTo 驱动动态变化

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对象
{
  "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
  }

  • 整体骨架视图
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%')
  }
}
  • 具体渲染骨架组件
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的变化范围,从而打造出各种独具特色且舒适的骨架图加载效果。让我们一起用这些技术,为用户带来更加精彩的应用体验吧🎉!


原文地址:https://blog.csdn.net/m0_66825548/article/details/145268232

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!