【Harmony】鸿蒙自定义倒计时控件CountdownView

介绍

倒计时组件,支持自定义天、时、分、秒、毫秒的显示样式,可配置倒计时开始、结束、更新等回调事件。

链接

https://gitee.com/HW-Commons/ZUtils/blob/master/ui/countdown/src/main/ets/components/CountdownView.ets

效果图

在这里插入图片描述

下载安装

在每个har/hsp模块中,通过ohpm工具下载安装库:

ohpm install @hzw/zcountdown

使用方法

@Component
struct Index {
  build() {
    Column() {
      // 基础用法
      CountdownView({
        // 倒计时结束时间
        endTime: systemDateTime.getTime() + 1000 * 60 * 60 * 24 * 3,
        // 时间倒计时回调,回调剩余时间
        updateTime: (time: number) => {
          console.log(`${new Date(time)}`)
        },
      })

      // 自定义后缀文本
      CountdownView({
        endTime: systemDateTime.getTime() + 1000 * 60 * 60 * 24 * 3,
        suffixHour: "时",
        suffixMinute: "分",
        suffixSecond: "秒",
        suffixMillisecond: "毫秒",
        isShowMillisecond: true,
        updateTime: (time: number) => {
          console.log(`${new Date(time)}`)
        },
      })

      // 自定义样式
      CountdownView({
        endTime: systemDateTime.getTime() + 1000 * 60 * 60 * 24 * 3,
        started: true,
        timeFontColor: Color.White,
        timeStartBgColor: Color.Red,
        timeEndBgColor: Color.Pink,
        timeBgRadius: 10,
        timeFontSize: 14,
        timeWidth: 50,
        timeHeight: 20,
        suffixText: ":",
        suffixFontColor: Color.Blue,
        suffixFontSize: 14,
        suffixWidth: 20,
        dayFontColor: Color.Green,
        dayFontSize: 14,
        dayPrefix: "距离活动还有",
        daySuffix: "天",
        dayMargin: 10,
        isShowDay: true,
        isShowHour: true,
        isShowMinute: true,
        isShowSecond: true,
        isShowMillisecond: true,
        onStart: () => {
          console.log("倒计时开始")
        },
        onEnd: () => {
          console.log("倒计时结束")
        },
        updateTime: (time: number) => {
          console.log(`${new Date(time)}`)
        },
      })

      // 使用TextModifier自定义样式
      CountdownView({
        endTime: systemDateTime.getTime() + 1000 * 60 * 60 * 24 * 3,
        dayPrefix: "离结束还有",
        daySuffix: "日",
        suffixHour: "时",
        suffixMinute: "分",
        suffixSecond: "秒",
        dayPrefixModifier: new TextModifier().fontColor(Color.Red),
        dayModifier: new TextModifier().fontColor(Color.White)
          .backgroundColor(Color.Green).padding(4).margin(2).borderRadius(4),
        daySuffixModifier: new TextModifier().fontColor(Color.Blue),
        hourModifier: new TextModifier().fontColor(Color.White)
          .backgroundColor(Color.Red).padding(4).margin(2).borderRadius(100).rotate({angle: 30}),
        hourSuffixModifier: new TextModifier().fontColor(Color.Brown),
        minuteModifier: new TextModifier().fontColor(Color.Black),
        minuteSuffixModifier: new TextModifier().fontColor(Color.White)
          .backgroundColor(Color.Orange).width(30).height(30).fontSize(18).margin(4).borderRadius(10),
        secondModifier: new TextModifier().fontColor(Color.Pink),
        secondSuffixModifier: new TextModifier().fontColor(Color.Red)
          .visibility(Visibility.Visible),
        updateTime: (time: number) => {
          console.log(`${new Date(time)}`)
        },
      })
    }
  }
}

参数说明

参数名类型默认值说明
endTimenumber0倒计时结束时间戳(毫秒)
startedbooleantrue是否开始倒计时
timeFontColorResourceColorColor.Black时间字体颜色
timeStartBgColorResourceColorColor.Transparent时间背景渐变开始颜色
timeEndBgColorResourceColorColor.Transparent时间背景渐变结束颜色
timeBgRadiusnumber0时间背景圆角
timeFontSizenumber | string | Resource14时间字体大小
timeWidthnumberundefined时间宽度
timeHeightnumberundefined时间高度
suffixTextstring | Resource“:”分隔符文本
suffixHourstringundefined小时后缀文本
suffixMinutestringundefined分钟后缀文本
suffixSecondstringundefined秒后缀文本
suffixMillisecondstringundefined毫秒后缀文本
suffixFontColorResourceColorColor.Black分隔符字体颜色
suffixFontSizenumber | string | Resource14分隔符字体大小
suffixWidthnumberundefined分隔符宽度
dayFontColorResourceColorColor.Black天数字体颜色
dayFontSizenumber | string | Resource14天数字体大小
dayPrefixstring“”天数前缀文本
daySuffixstring“天”天数后缀文本
dayMarginnumber0天数右侧间距
isShowDaybooleantrue是否显示天数
isShowHourbooleantrue是否显示小时
isShowMinutebooleantrue是否显示分钟
isShowSecondbooleantrue是否显示秒数
isShowMillisecondbooleanfalse是否显示毫秒
dayPrefixModifierTextModifierundefined自定义天数前缀样式
dayModifierTextModifierundefined自定义天数样式
daySuffixModifierTextModifierundefined自定义天数后缀样式
hourModifierTextModifierundefined自定义小时样式
hourSuffixModifierTextModifierundefined自定义小时后缀样式
minuteModifierTextModifierundefined自定义分钟样式
minuteSuffixModifierTextModifierundefined自定义分钟后缀样式
secondModifierTextModifierundefined自定义秒数样式
secondSuffixModifierTextModifierundefined自定义秒数后缀样式
millisecondModifierTextModifierundefined自定义毫秒样式
millisecondSuffixModifierTextModifierundefined自定义毫秒后缀样式
onStart() => void-倒计时开始回调
onEnd() => void-倒计时结束回调
updateTime(time: number) => void-倒计时更新回调,参数为剩余时间(毫秒)

源码

import { systemDateTime } from '@kit.BasicServicesKit'
import { TextModifier } from '@kit.ArkUI'

/**
 * @author: HHBin
 * @date: 2025/1/2
 * @desc: 时间倒计时控件
 */
@ComponentV2
export struct CountdownView {
  /**
   * 倒计时结束时间
   */
  @Param endTime: number = 0
  /**
   * 是否开始倒计时
   */
  @Param started: boolean = true
  /**
   * 时间字体颜色
   */
  @Param timeFontColor: ResourceColor = Color.Black;
  /**
   * 时间背景开始颜色
   */
  @Param timeStartBgColor: ResourceColor = Color.Transparent;
  /**
   * 时间背景结束颜色
   */
  @Param timeEndBgColor: ResourceColor = Color.Transparent;
  /**
   * 时间背景圆角
   */
  @Param timeBgRadius: number = 0;
  /**
   * 时间字体大小
   */
  @Param timeFontSize: number | string | Resource = 14;
  /**
   * 时间宽度
   */
  @Param timeWidth: number | undefined = undefined;
  /**
   * 时间高度
   */
  @Param timeHeight: number | undefined = undefined;
  /**
   * 后缀文本
   */
  @Param suffixText: string | Resource = ":";
  /**
   * 时后缀
   */
  @Param suffixHour: string | undefined = undefined
  /**
   * 分后缀
   */
  @Param suffixMinute: string | undefined = undefined
  /**
   * 秒后缀
   */
  @Param suffixSecond: string | undefined = undefined
  /**
   * 毫秒后缀
   */
  @Param suffixMillisecond: string | undefined = undefined
  /**
   * 后缀颜色
   */
  @Param suffixFontColor: ResourceColor = Color.Black;
  /**
   * 后缀字体大小
   */
  @Param suffixFontSize: number | string | Resource = 14;
  /**
   * 后缀字体宽度
   */
  @Param suffixWidth: number | undefined = undefined;
  /**
   * 天颜色
   */
  @Param dayFontColor: ResourceColor = Color.Black;
  /**
   * 天字体大小
   */
  @Param dayFontSize: number | string | Resource = 14;
  /**
   * 天前缀
   */
  @Param dayPrefix: string = ""
  /**
   * 天后缀
   */
  @Param daySuffix: string = "天"
  /**
   * 天间隔
   */
  @Param dayMargin: number = 0
  /**
   * 是否显示天
   */
  @Param isShowDay: boolean = true
  /**
   * 是否显示小时
   */
  @Param isShowHour: boolean = true
  /**
   * 是否显示分钟
   */
  @Param isShowMinute: boolean = true
  /**
   * 是否显示秒
   */
  @Param isShowSecond: boolean = true
  /**
   * 是否显示毫秒
   */
  @Param isShowMillisecond: boolean = false
  /**
   * 自定义日前缀样式
   */
  @Param dayPrefixModifier: TextModifier | undefined = undefined
  /**
   * 自定义天样式
   */
  @Param dayModifier: TextModifier | undefined = undefined
  /**
   * 自定义日后缀样式
   */
  @Param daySuffixModifier: TextModifier | undefined = undefined
  /**
   * 自定义时样式
   */
  @Param hourModifier: TextModifier | undefined = undefined
  /**
   * 自定义时后缀样式
   */
  @Param hourSuffixModifier: TextModifier | undefined = undefined
  /**
   * 自定义分样式
   */
  @Param minuteModifier: TextModifier | undefined = undefined
  /**
   * 自定义分后缀样式
   */
  @Param minuteSuffixModifier: TextModifier | undefined = undefined
  /**
   * 自定义秒样式
   */
  @Param secondModifier: TextModifier | undefined = undefined
  /**
   * 自定义秒后缀样式
   */
  @Param secondSuffixModifier: TextModifier | undefined = undefined
  /**
   * 自定义毫秒样式
   */
  @Param millisecondModifier: TextModifier | undefined = undefined
  /**
   * 自定义毫秒后缀样式
   */
  @Param millisecondSuffixModifier: TextModifier | undefined = undefined
  /**
   * 时间倒计时开始回调
   */
  @Event onStart?: () => void
  /**
   * 时间倒计时结束回调
   */
  @Event onEnd?: () => void
  /**
   * 时间倒计时回调,回调剩余时间
   */
  @Event updateTime?: (time: number) => void
  @Event $started: (val: boolean) => void = () => {
  }

  @Computed
  get realEndTime(): number {
    return this.endTime > 1e10 ? this.endTime : this.endTime * 1000
  }

  @Monitor("started")
  onStartedChange() {
    if (this.started) {
      this.startInterval()
    } else {
      this.stopInterval()
    }
  }

  @Local private day: number = 0
  @Local private hour: number = 0
  @Local private minute: number = 0
  @Local private second: number = 0
  @Local private millisecond: number = 0
  private left: number = 0
  private intervalId?: number

  aboutToAppear(): void {
    if (this.started) {
      this.startInterval()
    }
  }

  aboutToDisappear(): void {
    this.stopInterval()
  }

  startInterval() {
    this.stopInterval()
    this.onStart?.()
    this.intervalId = setInterval(() => {
      this.left = this.realEndTime - systemDateTime.getTime()
      if (this.left <= 0) {
        this.onEnd?.()
        this.$started(false)
        this.stopInterval()
        return
      }
      this.day = this.getLeftDay(this.left)
      this.hour = this.getLeftHour(this.left)
      this.minute = this.getLeftMinute(this.left)
      this.second = this.getLeftSecond(this.left)
      this.millisecond = this.getLeftMillisecond(this.left)
      this.updateTime?.(this.left)
    }, this.isShowMillisecond ? 10 : 500);
  }

  stopInterval() {
    if (this.intervalId) {
      clearInterval(this.intervalId)
    }
    this.intervalId = undefined
  }

  build() {
    Row() {
      Text(`${this.dayPrefix}`)
        .fontSize(this.dayFontSize)
        .fontColor(this.dayFontColor)
        .visibility(this.isShowDay ? Visibility.Visible : Visibility.None)
        .textAlign(TextAlign.Center)
        .attributeModifier(this.dayPrefixModifier)
      Text(`${this.day.toString()}`)
        .fontSize(this.dayFontSize)
        .fontColor(this.dayFontColor)
        .visibility(this.isShowDay ? Visibility.Visible : Visibility.None)
        .textAlign(TextAlign.Center)
        .attributeModifier(this.dayModifier)
      Text(`${this.daySuffix}`)
        .fontSize(this.dayFontSize)
        .fontColor(this.dayFontColor)
        .visibility(this.isShowDay ? Visibility.Visible : Visibility.None)
        .textAlign(TextAlign.Center)
        .margin({ right: this.dayMargin })
        .attributeModifier(this.daySuffixModifier)
      Text(`${this.hour.toString().padStart(2, '0')}`)
        .fontSize(this.timeFontSize)
        .fontColor(this.timeFontColor)
        .visibility(this.isShowHour ? Visibility.Visible : Visibility.None)
        .width(this.timeWidth)
        .height(this.timeHeight)
        .textAlign(TextAlign.Center)
        .linearGradient({
          angle: 90,
          colors: [[this.timeStartBgColor, 0.0], [this.timeEndBgColor, 1]]
        })
        .borderRadius(this.timeBgRadius)
        .attributeModifier(this.hourModifier)
      Text(this.suffixHour ?? this.suffixText)
        .fontSize(this.suffixFontSize)
        .fontColor(this.suffixFontColor)
        .visibility(this.isShowHour && this.isShowMinute ? Visibility.Visible : Visibility.None)
        .width(this.suffixWidth)
        .textAlign(TextAlign.Center)
        .fontWeight(FontWeight.Bold)
        .attributeModifier(this.hourSuffixModifier)
      Text(`${this.minute.toString().padStart(2, '0')}`)
        .fontSize(this.timeFontSize)
        .fontColor(this.timeFontColor)
        .visibility(this.isShowMinute ? Visibility.Visible : Visibility.None)
        .width(this.timeWidth)
        .height(this.timeHeight)
        .textAlign(TextAlign.Center)
        .linearGradient({
          angle: 90,
          colors: [[this.timeStartBgColor, 0.0], [this.timeEndBgColor, 1]]
        })
        .borderRadius(this.timeBgRadius)
        .attributeModifier(this.minuteModifier)
      Text(this.suffixMinute ?? this.suffixText)
        .fontSize(this.suffixFontSize)
        .fontColor(this.suffixFontColor)
        .visibility(this.isShowMinute && this.isShowSecond ? Visibility.Visible : Visibility.None)
        .width(this.suffixWidth)
        .textAlign(TextAlign.Center)
        .fontWeight(FontWeight.Bold)
          .attributeModifier(this.minuteSuffixModifier)
      Text(`${this.second.toString().padStart(2, '0')}`)
        .fontSize(this.timeFontSize)
        .fontColor(this.timeFontColor)
        .visibility(this.isShowSecond ? Visibility.Visible : Visibility.None)
        .width(this.timeWidth)
        .height(this.timeHeight)
        .textAlign(TextAlign.Center)
        .linearGradient({
          angle: 90,
          colors: [[this.timeStartBgColor, 0.0], [this.timeEndBgColor, 1]]
        })
        .borderRadius(this.timeBgRadius)
          .attributeModifier(this.secondModifier)
      Text(this.suffixSecond ?? this.suffixText)
        .fontSize(this.suffixFontSize)
        .fontColor(this.suffixFontColor)
        .visibility(this.isShowSecond && this.isShowMillisecond ? Visibility.Visible : Visibility.None)
        .width(this.suffixWidth)
        .textAlign(TextAlign.Center)
        .fontWeight(FontWeight.Bold)
          .attributeModifier(this.secondSuffixModifier)
      Text(`${this.millisecond.toString().padStart(3, '0')}`)
        .fontSize(this.timeFontSize)
        .fontColor(this.timeFontColor)
        .visibility(this.isShowMillisecond ? Visibility.Visible : Visibility.None)
        .width(this.timeWidth)
        .height(this.timeHeight)
        .textAlign(TextAlign.Center)
        .linearGradient({
          angle: 90,
          colors: [[this.timeStartBgColor, 0.0], [this.timeEndBgColor, 1]]
        })
        .borderRadius(this.timeBgRadius)
          .attributeModifier(this.millisecondModifier)
      Text(this.suffixMillisecond)
        .fontSize(this.suffixFontSize)
        .fontColor(this.suffixFontColor)
        .visibility(this.isShowMillisecond && this.isShowMillisecond ? Visibility.Visible : Visibility.None)
        .width(this.suffixWidth)
        .textAlign(TextAlign.Center)
        .fontWeight(FontWeight.Bold)
          .attributeModifier(this.millisecondSuffixModifier)
    }
    .alignItems(VerticalAlign.Center)
  }

  /**
   * 获取剩余天数
   */
  private getLeftDay(time: number): number {
    return Math.floor(time / (1000 * 24 * 60 * 60))
  }

  /**
   * 获取剩余小时
   */
  private getLeftHour(time: number): number {
    return Math.floor((time - this.getLeftDay(time) * (1000 * 24 * 60 * 60)) / (1000 * 60 * 60))
  }

  /**
   * 获取剩余分钟
   */
  private getLeftMinute(time: number): number {
    return Math.floor((time - this.getLeftDay(time) * (1000 * 24 * 60 * 60) -
      this.getLeftHour(time) * (1000 * 60 * 60)) / (1000 * 60))
  }

  /**
   * 获取剩余秒数
   */
  private getLeftSecond(time: number): number {
    return Math.floor((time - this.getLeftDay(time) * (1000 * 24 * 60 * 60) -
      this.getLeftHour(time) * (1000 * 60 * 60) - this.getLeftMinute(time) * (1000 * 60)) / 1000)
  }

  /**
   * 获取剩余毫秒数
   */
  private getLeftMillisecond(time: number): number {
    return time % 1000
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值