当前位置: 首页 > article >正文

鸿蒙NEXT自定义组件:太极Loading

【引言】(完整代码在最后面)

本文将介绍如何在鸿蒙NEXT中创建一个自定义的“太极Loading”组件,为你的应用增添独特的视觉效果。

【环境准备】

电脑系统:windows 10

开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

工程版本:API 12

真机:mate60 pro

语言:ArkTS、ArkUI

【项目分析】

1. 组件结构

我们将创建一个名为 TaiChiLoadingProgress 的自定义组件,它将模拟太极图的旋转效果,作为加载动画展示给用户。组件的基本结构如下:

@Component
struct TaiChiLoadingProgress {
  @Prop taiChiWidth: number = 400
  @Prop @Watch('animationCurveChanged') animationCurve: Curve = Curve.Linear
  @State angle: number = 0
  @State cellWidth: number = 0
  ...
}

2. 绘制太极图案

使用鸿蒙NEXT提供的UI组件,如 Rect 和 Circle,构建太极图的黑白两部分。关键在于利用 rotate 方法实现太极图的旋转效果。

build() {
  Stack() {
    Stack() {
      // 黑色半圆背景
      Stack() {
        Rect().width(`${this.cellWidth}px`).height(`${this.cellWidth / 2}px`).backgroundColor(Color.Black)
      }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).rotate({ angle: -90 }).align(Alignment.Top)
      // 大黑球 上
      Stack() {
        Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.Black)
        Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.White)
      }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Top)
      // 大白球 下
      Stack() {
        Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.White)
        Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.Black)
      }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Bottom)
    }
    .width(`${this.cellWidth}px`)
    .height(`${this.cellWidth}px`)
    .borderWidth(1)
    .borderColor(Color.Black)
    .borderRadius('50%')
    .backgroundColor(Color.White)
    .clip(true)
    .rotate({
      angle: this.angle
    })
    .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {
      if (isVisible && currentRatio >= 1.0) {
        this.startAnim()
      }
      if (!isVisible && currentRatio <= 0.0) {
        this.endAnim()
      }
    })
  }
  .width(`${this.taiChiWidth}px`)
  .height(`${this.taiChiWidth}px`)
}

3. 动画实现

通过 animateTo 方法设置太极图的旋转动画,可以自定义动画曲线以实现不同的动画效果。

startAnim() {
  animateTo({
    duration: 2000,
    iterations: -1,
    curve: this.animationCurve
  }, () => {
    this.angle = 360 * 2
  })
}

endAnim() {
  animateTo({
    duration: 0
  }, () => {
    this.angle = 0
  })
}

【完整代码】

@Component
struct TaiChiLoadingProgress {
  @Prop taiChiWidth: number = 400
  @Prop @Watch('animationCurveChanged') animationCurve: Curve = Curve.Linear
  @State angle: number = 0
  @State cellWidth: number = 0

  animationCurveChanged() {
    this.endAnim()
    this.startAnim()
  }

  startAnim() {
    animateTo({
      duration: 2000,
      iterations: -1,
      curve: this.animationCurve
    }, () => {
      this.angle = 360 * 2
    })
  }

  endAnim() {
    animateTo({
      duration: 0
    }, () => {
      this.angle = 0
    })
  }

  aboutToAppear(): void {
    this.cellWidth = this.taiChiWidth / 2
  }

  build() {
    Stack() {
      Stack() {
        //黑色 半圆 背景
        Stack() {
          Rect().width(`${this.cellWidth}px`).height(`${this.cellWidth / 2}px`).backgroundColor(Color.Black)
        }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).rotate({ angle: -90 }).align(Alignment.Top)

        //大黑球 上
        Stack() {
          Stack() {
            Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.Black)
            Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.White)
          }
        }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Top)

        //大白球 下
        Stack() {
          Stack() {
            Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.White)
            Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.Black)
          }
        }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Bottom)

      }
      .width(`${this.cellWidth}px`)
      .height(`${this.cellWidth}px`)
      .borderWidth(1)
      .borderColor(Color.Black)
      .borderRadius('50%')
      .backgroundColor(Color.White)
      .clip(true)
      .rotate({
        angle: this.angle
      })
      .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {
        console.info('Test Row isVisible:' + isVisible + ', currentRatio:' + currentRatio)
        if (isVisible && currentRatio >= 1.0) {
          console.info('Test Row is fully visible.')
          this.startAnim()
        }

        if (!isVisible && currentRatio <= 0.0) {
          console.info('Test Row is completely invisible.')
          this.endAnim()
        }
      })
    }
    .width(`${this.taiChiWidth}px`)
    .height(`${this.taiChiWidth}px`)
  }
}

@Entry
@Component
struct Page08 {
  @State loadingWidth: number = 150
  @State isShowLoading: boolean = true;
  @State animationCurve: Curve = Curve.Linear

  build() {
    Column({ space: 20 }) {

      Text('官方Loading组件')
      Column() {
        LoadingProgress().width(this.loadingWidth)
          .visibility(this.isShowLoading ? Visibility.Visible : Visibility.None)
      }.height(this.loadingWidth).width(this.loadingWidth)

      Text('自定义太极Loading组件')
      Column() {
        TaiChiLoadingProgress({ taiChiWidth: vp2px(this.loadingWidth), animationCurve: this.animationCurve })
          .visibility(this.isShowLoading ? Visibility.Visible : Visibility.Hidden)
      }.height(this.loadingWidth).width(this.loadingWidth)

      Row() {
        Flex({ wrap: FlexWrap.Wrap }) {
          Text('显示/隐藏')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.isShowLoading = !this.isShowLoading
            })
          Text('Linear动画')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.animationCurve = Curve.Linear
            })
          Text('FastOutLinearIn动画')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.animationCurve = Curve.FastOutLinearIn
            })
          Text('EaseIn动画')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.animationCurve = Curve.EaseIn
            })
          Text('EaseOut动画')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.animationCurve = Curve.EaseOut
            })
          Text('EaseInOut动画')
            .textAlign(TextAlign.Center)
            .width('200lpx')
            .height('200lpx')
            .margin('10lpx')
            .backgroundColor(Color.Black)
            .borderRadius(5)
            .backgroundColor(Color.Orange)
            .fontColor(Color.White)
            .clickEffect({ level: ClickEffectLevel.LIGHT })
            .onClick(() => {
              this.animationCurve = Curve.EaseInOut
            })
        }.width('660lpx')
      }.width('100%').justifyContent(FlexAlign.Center)
    }
    .height('100%')
    .width('100%')
    .backgroundColor("#f9feff")
  }
}


http://www.kler.cn/a/400886.html

相关文章:

  • DataOps for LLM 的数据工程技术架构实践
  • 多线程4:线程池、并发、并行、综合案例-抢红包游戏
  • 机器学习(西瓜书)-BP神经网络实现
  • H.265流媒体播放器EasyPlayer.js网页直播/点播播放器WebGL: CONTEXT_LOST_WEBGL错误引发的原因
  • 数据处理与统计分析——05-Pandas中DataFrame的方法、属性、索引等一系列操作
  • uniapp 跨域前端代理
  • 使用 Keras 训练一个循环神经网络(RNN)
  • Spring MVC前后端数据传输项目实践
  • 【nginx】client timed out和send_timeout的大小设置
  • Python模块、迭代器、正则表达式
  • redis服务启动windows客户端操作 (双开)
  • ETH钱包地址如何获取 如何购买比特币
  • PHP Switch 语句
  • Python模块、迭代器与正则表达式day10
  • 红日靶场-1详细解析(适合小白版)
  • 如何理解AGI是具备普通人类所有认知能力的通用 AI
  • 沃丰科技呼叫中心质检:定义、重要性及选择策略
  • C++设计模式:工厂方法模式
  • 软件Bug和缺陷的区别是什么?
  • 机器学习的主流数据集
  • Python提取PDF和DOCX中的文本、图片和表格
  • 51c自动驾驶~合集28
  • uniapp开发微信小程序笔记4-自定义组件
  • 加密市场动态:暴涨后的调整与未来趋势
  • Go语言24小时极速学习教程(二)复合数据(集合)操作
  • 客运购票售票小程序校园巴士预约售票小程序开发方案php+uniapp