定时器、递归、Math、Date和String的学习

87 阅读5分钟

定时器

日常开发中如果需要代码 延迟一会执行,或者每隔一段时间执行一次,就可以使用定时器
定时器有两种:

  • setTimeout: 延迟执行
  • setInterval: 间隔执行

setTimeout

setTimeout可以用来设置一个定时器,当设置的时间到了之后就会去执行指定的函数
执行一次用 setTimeout
场景如下:

延时器场景1.png 延时器场景2.png

定义setTimeout的基本语法

// setTimout会返回一个数字,表示当前延迟器的标记值
let 变量 = setTimeout(需要运行的函数,延迟时间单位毫秒)


//举例:
let timeId = setTimeout(()=>{
  console.log('我被执行了') // ✨✨✨只执行一次
} , 1000)

清除setTimeout的执行

clearTimeout(setTimout的标记值)

//举例:
// 1. 创建延迟器,使用timeId保存延迟器的标记值
let timeId = setTimeout(()=>{
  console.log('我被执行了')
} , 3000)

// 2. 清除延迟器的继续执行
clearTimeout(timeId)
小案例

settimeout案例.gif

@Entry
@Component
struct Index {
  @State msg:string = 'setTimeout延时器'
  timeId?:number
  build() {
    Column() {
      Text(this.msg)
        .fontSize(30)
      Row() {
        Button('开启延时器')
          .onClick(()=>{
            this.timeId = setTimeout(()=>{
              this.msg = '我被修改了'
            },2000)
          })
        Button('清楚延时器')
          .onClick(()=>{
            clearTimeout(this.timeId)
          })
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Pink)
  }
}

setInterval

setInterval 也是可以用来设置一个定时器,根据设置的时间间隔来执行指定的函数
执行多次用 setInterval!!
场景如下:

setInterval场景1.png setInterval场景2.png

定义setInterval的基本语法

// setInterval会返回一个数字,表示当前定时器的标记值
let 变量 = setInterval(需要运行的函数,定时器执行间隔单位毫秒)

//举例:
let timeId = setInterval(()=>{
  console.log('我被执行了') // ✨✨✨每隔1秒执行一次
} ,
1000) // ✨✨每隔1秒执行一次 , 1秒 = 1000毫秒

清除setInterval的执行

clearInterval(setInterval的标记值)

//举例:
// 1. 创建定时器,使用timeId保存延迟器的标记值
let timeId = setTInterval(()=>{
  console.log('我被执行了')
} , 3000)

// 2. 清除定时器器的继续执行
clearInterval(timeId)

案例-获取验证码

定时器案例1.png 定时器案例2.png

需求:

  1. 获取验证码(setTimout的使用练习)
    a. 点击【发送验证码】按钮之后,延迟 2 秒(setTimeout)获取一个 4 位的随机数(Math.floor(1000+Math.random()*9000)),通过弹框显示出来(AlertDialog.show({message:}))
  2. 倒计时效果(setInterval的使用练习)
    a. 点击【发送验证码】后 -> 文字变开始每隔1秒钟切换显示【xx秒后获取】 ,倒计时达到0秒的时候,恢复显示未【发送验证码】文字,并清除定时器
    代码如下:
@Entry
@Component
struct Index {
  @State time:number = 0 //控制倒计时
  timeId?:number  //
  build() {
    Column() {
      this.titleBuilder()
      TextInput({ placeholder: '请输入手机号' })
        .textInputExtend()
      Divider()
      Row() {
        TextInput({ placeholder: '请输入验证码' })
          .textInputExtend()
          .layoutWeight(1)
        Text(this.time == 0 ?'发送验证码':`${this.time}秒后获取`)
          .fontSize(14)
          .fontColor(Color.Gray)
          .onClick(()=>{
            // 防止在 倒计时 时重复点击
            if(this.time != 0){
              return
            }
            //1. 4位随机数
            let randomNum = Math.floor(Math.random()*9000 + 1000)
            //2. 2秒后提示
            setTimeout(()=>{
              AlertDialog.show({message:randomNum.toString()})
            },2000)

            // 60秒倒计时
            this.time = 60
            this.timeId = setInterval(()=>{
              this.time--
              if(this.time<=0){   //倒计时为0时清楚定时器
                clearInterval(this.timeId)
              }
            },1000)
          })
      }
      .width('100%')

      Divider()

      Button('登录')
        .width('100%')
        .type(ButtonType.Normal)
        .backgroundColor('#ea6051')
        .margin({ top: 50 })
    }
    .padding({ top: 80, left: 40, right: 40 })
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  titleBuilder() {
    Text('短信登录')
      .fontSize(25)
      .fontWeight(600)
      .margin({ bottom: 30 })
  }
}
@Extend(TextInput)
function textInputExtend() {
  .backgroundColor(Color.White)
  .padding({ left: 0, top: 20, bottom: 20 })
  .placeholderColor('#ccc')
}

递归

基本用法

一个函数(方法) ,在其方法体中调用自己的写法就是递归

// 没有退出条件的 递归函数--无限递归
function func(){
  func()
}
//调用 执行函数
func()

递归必须有退出条件

为了防止无限递归导致崩溃,我们在做具体递归调用时,必须设置退出条件

function printLog(num: number) {
  console.log(`你好,第${num}次打印`)
  // 递减
  num--
  // 退出条件
  if (num > 0) {
    printLog(num) 
  }
}

// 打印 2 次
printLog(2)

常用内置对象

Math 对象

拥有一些数学常数属性和数学函数方法。Math 的所有属性与方法都是静态的,使用的时候直接通过Math点出来即可

常用属性

Math.PI
圆周率,一个圆的周长和直径之比,约等于 3.14159。 console.log(Math.PI.toString())

常用方法

方法说明
Math.random()返回一个 0 到 1 之间的伪随机数。
Math.ceil(x)返回大于一个数的最小整数,即一个数向上取整后的值。
Math.floor(x)返回小于一个数的最大整数,即一个数向下取整后的值。
Math.round(x)返回四舍五入后的整数。
Math.abs(x)返回一个数的绝对值
Math.max([x[,y[, …]]])返回零到多个数值中最大值。
Math.min([x[,y[, …]]])返回零到多个数值中最小值。
const numA: number = 1.5
console.log(Math.ceil(numA) + '') // 向上取整 2
console.log(Math.floor(numA) + '') // 向下取整 1
console.log(Math.round(numA) + '') // 四舍五入 2

const numB:number = -9
console.log(Math.abs(numB) + '') // 绝对值 9

const numList: number[] = [13, 2, 31, 42, 15, 56, 27, 28]
const max: number = Math.max(...numList)
const min: number = Math.min(...numList)
console.log('max:', max) // 最大值
console.log('min:', min) // 最小值

// 0-1 取得到0,取不到 1
console.log(Math.random() + '')

// 返回 0-n的随机数的函数
function getRandomArbitrary(max: number): number {
  return Math.floor(Math.random() * (max + 1))
}

// 返回 min-max 的随机数的函数
function getRandomIntInclusive(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}

Date 对象

用来创建、解析、操作日期和时间。

// 获取当前日期
let date1 = new Date()
console.log('date1:',date1)

//获取指定日期
let date2 = new Date('1995-01-01T01:11:00')
console.log('date2:',date2)
// Unix时间戳 是指从1970年1月1日(UTC)开始到现在经历的时间(毫秒)
let date3 = new Date(1706170405708)
console.log('date3:',date3) 

date的使用.png

常用方法

实例方法
方法名作用说明
getFullYear获取年份4 位数年份
getMonth获取月份取值 0-11
getDate获取日期月份中的日期
getHours获取小时
getMinutes获取分钟
getSeconds获取秒
getDay获取星期周日为 0
静态方法
方法名作用说明
now获取当前时间时间戳
//实例方法
console.log('年',new Date().getFullYear())
console.log('月',new Date().getMonth()+1)
console.log('日',new Date().getDate())
console.log('时',new Date().getHours())
console.log('分',new Date().getMinutes())
console.log('秒',new Date().getSeconds())
console.log('星期',new Date().getDay())
//静态方法  得到的值是一个时间戳
console.log('',Date.now())

Date的实例和静态.png

案例-时钟

始终案例.png

代码如下:

@Entry
@Component
struct Date01 {
  // 1. 获取当前日期
  getNow() {
    let date = new Date()
    let year = date.getFullYear()
    let month = date.getMonth() + 1
    let day = date.getDate()

    return `${year}${month}${day}日`
  }

  // 2. 获取星期
  getWeek() {
    let date = new Date()
    let weekDay = date.getDay() // 周日为0
    let resWeek = ''
    switch (weekDay) {
      case 0:
        resWeek = '周日'
        break
      case 1:
        resWeek = '周一'
        break
      case 2:
        resWeek = '周二'
        break
      case 3:
        resWeek = '周三'
        break
      case 4:
        resWeek = '周四'
        break
      case 5:
        resWeek = '周五'
        break
      case 6:
        resWeek = '周六'
        break
      default:
    }

    return resWeek
  }

  // 3. 设置时钟时间变化
  @State hour: number = 0
  @State min: number = 0
  @State seconds: number = 0

  build() {
    Column() {
      Column() {
        Text(`${this.getNow()} ${this.getWeek()}`)
          .fontColor(Color.White)
          .fontSize(20)
        Stack() {
          Row({ space: 10 }) {
            Text(this.hour < 10 ? '0' + this.hour : this.hour.toString())
              .textStyle()
            Text(this.min < 10 ? '0' + this.min : this.min.toString())
              .textStyle()
            Text(this.seconds < 10 ? '0' + this.seconds : this.seconds.toString())
              .textStyle()
          }
          .onAppear(() => {
            // 3. 设置时钟时间变化
            setInterval(() => {
              let date = new Date()
              this.hour = date.getHours()
              this.min = date.getMinutes()
              this.seconds = date.getSeconds()
            }, 1000)
          })

          Divider()
            .strokeWidth(2)
            .color(Color.Black)
        }
        .padding(10)

        Text('求知若渴,虚心若愚')
          .fontColor(Color.White)
          .fontSize(18)
      }

    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Black)
    .justifyContent(FlexAlign.Center)
  }
}


@Extend(Text)
function textStyle() {
  .width(100)
  .height(100)
  .backgroundColor('#191919')
  .borderRadius(10)
  .textAlign(TextAlign.Center)
  .fontColor(Color.White)
  .fontSize(70)
  .fontWeight(900)
}
}

时钟案例.gif

String

split(分隔)

split() 方法根据传入的内容将字符串分隔为数组 语法:字符串.split(分隔符)// 返回切割之后的数组

// 1. 切割为数组
const branchFilter: string = ' 全部分路  | 对抗路 |   中路  | 发育路  | 游走 | 打野 '

const tabFilter: string = '  所有段位  - 巅峰赛1350+ -   顶端排位 - 赛事  '

// 2. 获取日期
const dateStr:string =  '2024-04-27'

let arr1 = branchFilter.split('|')
console.log(JSON.stringify(arr1))

let arr2 = tabFilter.split('-')
console.log(JSON.stringify(arr2))

let arr3 = dateStr.split('-')
console.log(JSON.stringify(arr3))

split.png

trim(去空格)

trim方法会从字符串的两端移除空白字符,并返回一个【新的字符串】,而不会修改原始字符串。语法:字符串.trim()// 返回去除空格之后的字符串

// trim 去除两边空格
// 1.基础文本
const str: string = '   123   ' 
console.log(str.trim()) // '123'
// 2.用户名
const username = '  jack  ' 
console.log(username.trim()) // 'jack'
// 3.密码
const password = ' 1234abc '
console.log(password.trim()) // '1234abc'

toLowerCase(转小写) 和 toUpperCase(转大写)

语法:
字符串.toLowerCase()// 返回转为小写之后的字符串
字符串.toUpperCase()// 返回转为大写之后的字符串

includes(判断是否存在)

includes() 方法执行区分大小写的搜索,以确定是否可以在一个字符串中找到另一个字符串,并根据情况返回 true 或 false。
语法:字符串.includes(查询的字符串)// 返回判断结果 true / false

slice(提取)

slice方法提取字符串的一部分,并将其作为新字符串返回,而不修改原始字符串。
语法:
字符串.slice(起始索引)// 从起始索引切割到末尾
字符串.slice(起始索引,结束索引) // 从起始索引,切换到结束索引

案例-热度榜单

王者荣耀英雄榜单.png

页面数据

创建 HeroData.ets 文件,参考路径如下: /data/HeroData.ets

export const branchFilter: string = ' 全部分路   | 对抗路   | 中路  | 发育路   |    游走 | 打野 '

// 英雄数据
export interface Hero {
  banRate: number // ban 率
  showRate: number // 登场率
  winRate: number // 胜率
  tRank: string // 热度
  heroCareer: string // 英雄分类
  heroName: string // 名字
  heroIcon: ResourceStr // 头像
  heroId: number // id
}

// 数组
export const heroList: Hero[] = [
  {
    banRate: 0.403,
    winRate: 0.519,
    showRate: 0.284,
    tRank: 't0',
    heroCareer: '辅助',
    heroId: 505,
    heroIcon: $r('app.media.ic_avatar_505'),
    heroName: '瑶'
  },
  {
    banRate: 0.702,
    winRate: 0.477,
    showRate: 0.097,
    tRank: 't0',
    heroCareer: '辅助',
    heroId: 191,
    heroIcon: $r('app.media.ic_avatar_191'),
    heroName: '大乔'
  },
  {
    banRate: 0.567,
    winRate: 0.487,
    showRate: 0.076,
    tRank: 't0',
    heroCareer: '辅助/坦克',
    heroId: 187,
    heroIcon: $r('app.media.ic_avatar_187'),
    heroName: '东皇太一'
  },
  {
    banRate: 0.452,
    winRate: 0.512,
    showRate: 0.12,
    tRank: 't0',
    heroCareer: '辅助/坦克',
    heroId: 113,
    heroIcon: $r('app.media.ic_avatar_113'),
    heroName: '庄周'
  },
  {
    banRate: 0.356,
    winRate: 0.503,
    showRate: 0.162,
    tRank: 't0',
    heroCareer: '辅助',
    heroId: 184,
    heroIcon: $r('app.media.ic_avatar_184'),
    heroName: '蔡文姬'
  },
  {
    banRate: 0.482,
    winRate: 0.475,
    showRate: 0.082,
    tRank: 't0',
    heroCareer: '辅助',
    heroId: 159,
    heroIcon: $r('app.media.ic_avatar_159'),
    heroName: '朵莉亚'
  },
  {
    banRate: 0.022,
    winRate: 0.495,
    showRate: 0.297,
    tRank: 't0',
    heroCareer: '法师',
    heroId: 142,
    heroIcon: $r('app.media.ic_avatar_142'),
    heroName: '安琪拉'
  },
  {
    banRate: 0.396,
    winRate: 0.496,
    showRate: 0.098,
    tRank: 't0',
    heroCareer: '法师',
    heroId: 563,
    heroIcon: $r('app.media.ic_avatar_563'),
    heroName: '海诺'
  },
]

实现

import { branchFilter, Hero, heroList } from '../data/HeroData' //导入数据

let branch = branchFilter.split('|') //把分路数据去分隔符形成数组

@Entry
@Component
struct Index {
  // 日期
  time() {
    let time = new Date()
    let year = time.getFullYear()
    let month = time.getMonth() + 1
    let day = time.getDate()

    return `更新时间:${year}/${month}/${day}/`
  }

  build() {
    Column() {
      // 底部区域
      Stack({ alignContent: Alignment.Top }) {
        // 背景图
        Image($r('app.media.ic_bg_517'))
          .height(180)
          .width('100%')

        // 操作栏
        Stack() {
          Row() {
            // 左
            Image($r('app.media.ic_public_arrow_left'))
              .width(24)
              .fillColor(Color.White)
            // 右
            Row({ space: 10 }) {
              Image($r('app.media.ic_public_question'))
                .width(24)
                .fillColor(Color.White)
              Image($r('app.media.ic_public_share'))
                .width(24)
                .fillColor(Color.White)
            }
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)

          // 中间文本
          Column() {
            Row() {
              Text('英雄热度榜')
                .fontColor(Color.White)
              Image($r('app.media.ic_public_arrow_down'))
                .width(20)
                .fillColor(Color.White)
            }

            Row({ space: 5 }) {
              Text(this.time())
                .fontColor(Color.White)
                .fontSize(10)
              Text('算法测试中')
                .fontColor(Color.White)
                .fontSize(9)
                .backgroundColor('37bdee')
                .padding(1)
                .borderRadius({ topLeft: 6, bottomRight: 6 })
            }
          }
          .layoutWeight(1)
        }
        .padding({ top: 10, left: 10, right: 10 })
        .zIndex(2)
        .height(55)
        .backgroundColor(`rgba(0, 0, 0, 0.2)`)
      }

      // 筛选区域
      // 段位筛选 tabFilter
      List({ space: 25 }) {
        ForEach(branch, (item: string) => {
          ListItem() {
            Text(item.trim())
              .height(40)
              .padding(10)
              .fontSize(16)
          }
        })
      }
      .listDirection(Axis.Horizontal)
      .scrollBar(BarState.Off)
      .width('100%')
      .height(50)

      // 英雄列表
      Column() {
        // 顶部区域
        Row() {
          Text('英雄')
            .fontWeight(900)
          Blank()
          Row({ space: 10 }) {
            Text('热度')
              .fontWeight(900)
            Text('胜率')
              .fontWeight(900)
            Text('登场率')
              .fontWeight(900)
            Text('Ban率')
              .fontWeight(900)
          }
        }
        .width('100%')
        .padding(10)

        // 列表区域
        List() {
          ForEach(heroList, (item: Hero) => {
            ListItem() {
              Row({ space: 14 }) {
                // 头像
                Image(item.heroIcon)
                  .width(40)
                  .borderRadius(10)
                // 昵称+分类
                Column({ space: 5 }) {
                  Text(item.heroName)
                  Text(item.heroCareer)
                    .fontSize(10)
                    .fontColor(Color.Gray)
                }
                .width(70)
                .alignItems(HorizontalAlign.Start)

                Blank()
                // 热度 胜率 登场率 Ban 率
                Text(item.tRank.toUpperCase())
                  .fontWeight(900)
                Text(`${(item.winRate * 100).toFixed(1)}%`) //给的数据是小数,变成百分比形式保留1位小数
                  .width(45)
                  .fontSize(15)
                Text(`${(item.showRate * 100).toFixed(1)}%`)
                  .width(45)
                  .fontSize(15)
                Text(`${(item.banRate * 100).toFixed(1)}%`)
                  .width(45)
                  .fontSize(15)
              }
              .width('100%')
            }
            .padding(10)
          })

        }
      }
      .layoutWeight(1)

    }
    .width('100%')
    .height('100%')
  }
}