动手学鸿蒙App开发 HarmonyOS(06)时间屏保

前言

本篇实现一种带有背景色黑白渐变呼吸效果 并且带有翻转卡片动效的数字时钟屏保。 随着秒钟卡片的翻转和背景色有节奏的变换,给人一种时间飞逝的压迫感。仿佛也暗示着作者急切得盼望着坐上下班回家的公交车。

有兴趣的同学可以跟着做,但建议不要长时间使用该款时间屏保。看多了以后明显会对精神状态有影响。

效果展示

实现原理

1. 呼吸背景色

背景组件是一块全屏的普通Row, 通过定时器启动动画效果,对组件的brightness属性进行修改。由暗到亮,再由亮到暗分2个阶段完成

brightness number 1.0 为当前组件添加高光效果,入参为高光比例,值为1时没有效果,小于1时亮度变暗,0为全黑,大于1时亮度增加,数值越大亮度越大。取值范围:[0, +∞)**说明:**设置小于0的值时,按值为0处理。从API version 9开始,该接口支持在ArkTS卡片中使用。

关于组件的图像效果其实还有很多其他的属性可以进行修改,可以根据UI需求来使用。具体的属性列表可以参考官方文档: 图像效果

2. 翻转卡片

翻转卡片的实现是由上下两层组件来实现的。 当由定时器触发的动画开始时,下层卡片更新为新的时间。上层卡片开始在y方向的中点开始向后翻转。而下层卡片以y方向-90度开始向上翻转并在卡片正面用户时结束动画。

大家可以尝试其他角度或者动画效果来尝试改变动效。

对时间新值@watch的使用值得新手同学注意,这里通过值的变更来触发动画效果。

代码

时间卡片组件

ts 复制代码
@Component
@Preview
export struct FlipText {
  private textWidth = 90
  private textHeight = 110
  private fontSize = 70
  private defaultBgColor = '#66e6e6e6'
  private textBorderRadius: number = 10;
  // 上层动画角度
  @State angleTop: number = 0
  // 底层动画角度
  @State angleBottom: number = -90
  // 旧值
  @Prop oldValue: string
  // 新值,加入监听
  @Link @Watch('valueChange') newValue: string

  /**
   * 监听新值变化
   */
  valueChange() {
    if (this.oldValue === this.newValue) return
    this.angleTop = 0
    this.startTopAnimate()
    this.startBottomAnimate();
  }

  build() {
    Stack() {
      // 底层文字
      Text(this.newValue)
        .commonStyle(this.fontSize, this.textBorderRadius, this.defaultBgColor)
        .rotate({ x: 1, centerY: '50%', angle: this.angleBottom })

      // 顶层文字动画
      Text(this.oldValue)
        .commonStyle(this.fontSize, this.textBorderRadius, this.defaultBgColor)
        .rotate({ x: 1, centerY: '50%', angle: this.angleTop })
    }
    .width(this.textWidth)
    .height(this.textHeight)
  }

  /**
   * 启动顶层文字动画
   */
  startTopAnimate() {
    animateTo({
      duration: 400,
      onFinish: () => {
        this.angleTop = 0
        this.oldValue = this.newValue
      }
    }, () => {
      this.angleTop = 90
    })
  }

  /**
   * 启动底层文字动画
   */
  startBottomAnimate() {
    animateTo({
      duration: 400,
      onFinish: () => {
        this.angleBottom = -90
        this.oldValue = this.newValue
      }
    }, () => {
      this.angleBottom = 0
    })
  }
}

// Text公共样式
@Extend(Text)
function commonStyle(fontSize: number | string | Resource,
                     borderRadius: Length,
                     bgColor: ResourceColor) {
  .width('100%')
  .height('100%')
  .fontColor(Color.Black)
  .fontSize(fontSize)
  .textAlign(TextAlign.Center)
  .borderRadius(borderRadius)
  .backgroundColor(bgColor)
  .shadow({ radius: 30, color: '#ffacacac', offsetY: 10 })
}

小时,分钟,秒钟 封装到一起

ts 复制代码
import { FlipText } from './FlipText'

@Component
@Preview
export struct TimeFlipView {
  // 小时-旧值
  @State oldHours: string = ''
  // 小时-新值
  @State newHours: string = ''
  // 分钟-旧值
  @State oldMinutes: string = ''
  // 分钟-新值
  @State newMinutes: string = ''
  // 秒数-旧值
  @State oldSeconds: string = ''
  // 秒数-新值
  @State newSeconds: string = ''

  @Builder Colon() {
    Column() {
      Circle().width(8).height(8).fill(Color.Black)
      Circle().width(8).height(8).fill(Color.Black).margin({ top: 10 })
    }.padding(10)
  }

  build() {
    Row() {
      // 翻页组件-显示小时
      FlipText({ oldValue: this.oldHours, newValue: $newHours })
      // 冒号
      this.Colon()
      // 翻页组件-显示分钟
      FlipText({ oldValue: this.oldMinutes, newValue: $newMinutes })
      // 冒号
      this.Colon()
      // 翻页组件-显示秒数
      FlipText({ oldValue: this.oldSeconds, newValue: $newSeconds })
    }
    .justifyContent(FlexAlign.Center)
    // .width('100%')
    // .height('100%')
    .onAppear(() => {
      // 开启定时器
      this.initDate()
      setInterval(() => {
        this.updateDate()
      }, 1000)
    })
  }

  /**
   * 初始化时间
   */
  initDate() {
    let date = new Date()
    // 设置小时
    this.oldHours = this.format(date.getHours())
    // 设置分钟
    this.oldMinutes = this.format(date.getMinutes())
    // 设置秒数
    this.oldSeconds = this.format(date.getSeconds())
    // 设置新的秒数
    this.newSeconds = date.getSeconds() + 1 === 60 ? '00' : this.format(date.getSeconds() + 1)
  }

  /**
   * 更新时间
   */
  updateDate() {
    let date = new Date()
    console.log(`${date.getHours()}时${date.getMinutes()}分${date.getSeconds()}秒`)
    // 当新值改变,才有动画
    if (date.getSeconds() === 59) {
      this.newSeconds = '00'
      this.newMinutes = date.getMinutes() + 1 === 60 ? '00' : this.format(date.getMinutes() + 1)
      if (date.getMinutes() === 59) {
        this.newHours = date.getHours() + 1 === 24 ? '00' : this.format(date.getHours() + 1)
      }
    } else {
      this.newSeconds = this.format(date.getSeconds() + 1)
    }
  }

  /**
   * 不足十位前面补零
   */
  format(param) {
    let value = '' + param
    if (param < 10) {
      value = '0' + param
    }
    return value
  }
}

主页

ts 复制代码
@Entry
@Component
struct Index {
  @State bgBrightness: number = 0.0

  /**
   * 启动背景亮度动画
   */
  startBrightnessAnimate() {
    animateTo({
      duration: 2000,
      onFinish: () => {
        animateTo({
          duration: 400,
          onFinish: () => {
            this.bgBrightness = 0.0
          }
        }, () => {
          this.bgBrightness = 0.0
        })
      }
    }, () => {
      this.bgBrightness = 1.0
    })
  }

  build() {
    Stack() {
      Row()
        .width('100%').height('100%')
        .backgroundColor(Color.White)
        .brightness(this.bgBrightness)
        .onAppear(() => {
          setInterval(() => {
            this.startBrightnessAnimate()
          }, 2500)
      })
      Row() {
        TimeFlipView()
      }
      .alignItems(VerticalAlign.Center)
    }
  }
}

总结

眼睛有点花了已经开始,写这个系列非常不容易,就像每天不知道中午点什么外卖,需要耗费许多想象力。

望各位耗子尾汁,点赞关注收藏三连。感谢大家!下次见~

相关推荐
sun00770015 小时前
android ndk编译valgrind
android
AI视觉网奇17 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空17 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet18 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin18 小时前
PHP serialize 序列化完全指南
android·开发语言·php
tangweiguo0305198719 小时前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
爱笑的眼睛1121 小时前
HarmonyOS 应用开发深度解析:基于声明式UI的现代化状态管理实践
华为·harmonyos
前端世界21 小时前
HarmonyOS 实战:如何用数据压缩和解压让应用更快更省
华为·harmonyos
00后程序员张21 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
安卓开发者1 天前
鸿蒙Next Web组件详解:属性设置与事件处理实战
前端·华为·harmonyos