实战机型

在这里插入图片描述

虚拟机说明

由于我真机的是6.1.0版本,也就是API23,所以对应匹配的虚拟机肯定要一致的。
在这里插入图片描述

常量string.json设置

{
  "string": [
    {
      "name": "module_desc",
      "value": "module description"
    },
    {
      "name": "EntryAbility_desc",
      "value": "description"
    },
    {
      "name": "EntryAbility_label",
      "value": "label"
    },
    {
      "name": "tab_clock",
      "value": "时钟"
    },
    {
      "name": "tab_date_weather",
      "value": "日期天气"
    },
    {
      "name": "tab_health",
      "value": "步数心率"
    },
    {
      "name": "label_steps",
      "value": "步数"
    },
    {
      "name": "label_steps_target",
      "value": "目标"
    },
    {
      "name": "label_heart_rate",
      "value": "心率"
    },
    {
      "name": "label_heart_unit",
      "value": "BPM"
    },
    {
      "name": "weather_sunny",
      "value": "晴"
    },
    {
      "name": "weather_temp",
      "value": "26°"
    },
    {
      "name": "weather_city",
      "value": "深圳"
    },
    {
      "name": "week_monday",
      "value": "星期一"
    },
    {
      "name": "week_tuesday",
      "value": "星期二"
    },
    {
      "name": "week_wednesday",
      "value": "星期三"
    },
    {
      "name": "week_thursday",
      "value": "星期四"
    },
    {
      "name": "week_friday",
      "value": "星期五"
    },
    {
      "name": "week_saturday",
      "value": "星期六"
    },
    {
      "name": "week_sunday",
      "value": "星期日"
    }
  ]
}

颜色常量

{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    },
    {
      "name": "bg_deep_blue",
      "value": "#0A192F"
    },
    {
      "name": "accent_gold",
      "value": "#FFD700"
    },
    {
      "name": "accent_cyan_blue",
      "value": "#4D9DE0"
    },
    {
      "name": "accent_purple",
      "value": "#C77DFF"
    },
    {
      "name": "accent_pink",
      "value": "#FF6B9D"
    },
    {
      "name": "accent_mint",
      "value": "#00F5D4"
    },
    {
      "name": "card_bg",
      "value": "#112240"
    },
    {
      "name": "text_primary",
      "value": "#E6F1FF"
    },
    {
      "name": "text_secondary",
      "value": "#8892B0"
    }
  ]
}

index.ets

页面都写在了index.ets中。

@Entry
@Component
struct Index {
  @State currentIndex: number = 0;
  @State hours: string = '00';
  @State minutes: string = '00';
  @State seconds: string = '00';
  @State gradientAngle1: number = 0;
  @State gradientAngle2: number = 120;
  @State gradientAngle3: number = 240;
  @State spot1Angle: number = 0;
  @State spot2Angle: number = 90;
  @State spot3Angle: number = 210;
  @State breathScale: number = 1.0;
  @State heartRate: number = 72;
  @State steps: number = 6428;
  @State stepsTarget: number = 10000;
  @State dateStr: string = '08月28日';
  @State weekdayIdx: number = 4;

  private timeTimerId: number = -1;
  private animTimerId: number = -1;
  private heartTimerId: number = -1;

  aboutToAppear(): void {
    this.updateTime();
    this.updateDate();
    this.timeTimerId = setInterval(() => {
      this.updateTime();
    }, 1000);
    this.animTimerId = setInterval(() => {
      this.gradientAngle1 = (this.gradientAngle1 + 1.2) % 360;
      this.gradientAngle2 = (this.gradientAngle2 + 0.9) % 360;
      this.gradientAngle3 = (this.gradientAngle3 + 0.6) % 360;
      this.spot1Angle = (this.spot1Angle + 0.35) % 360;
      this.spot2Angle = (this.spot2Angle - 0.25 + 360) % 360;
      this.spot3Angle = (this.spot3Angle + 0.2) % 360;
    }, 50);
    this.heartTimerId = setInterval(() => {
      let delta: number = Math.floor(Math.random() * 7) - 3;
      let next: number = this.heartRate + delta;
      if (next < 62) {
        next = 62;
      }
      if (next > 88) {
        next = 88;
      }
      animateTo({ duration: 600, curve: Curve.EaseInOut }, () => {
        this.heartRate = next;
        this.breathScale = 0.98 + Math.random() * 0.04;
      });
    }, 1500);
  }

  aboutToDisappear(): void {
    if (this.timeTimerId !== -1) {
      clearInterval(this.timeTimerId);
    }
    if (this.animTimerId !== -1) {
      clearInterval(this.animTimerId);
    }
    if (this.heartTimerId !== -1) {
      clearInterval(this.heartTimerId);
    }
  }

  private updateTime(): void {
    let now: Date = new Date();
    let h: number = now.getHours();
    let m: number = now.getMinutes();
    let s: number = now.getSeconds();
    this.hours = h < 10 ? '0' + h.toString() : h.toString();
    this.minutes = m < 10 ? '0' + m.toString() : m.toString();
    this.seconds = s < 10 ? '0' + s.toString() : s.toString();
  }

  private updateDate(): void {
    let now: Date = new Date();
    let mo: number = now.getMonth() + 1;
    let da: number = now.getDate();
    let moStr: string = mo < 10 ? '0' + mo.toString() : mo.toString();
    let daStr: string = da < 10 ? '0' + da.toString() : da.toString();
    this.dateStr = moStr + '月' + daStr + '日';
    let w: number = now.getDay();
    this.weekdayIdx = w === 0 ? 6 : w - 1;
  }

  private getWeekdayString(idx: number): string {
    if (idx === 0) { return '星期一'; }
    if (idx === 1) { return '星期二'; }
    if (idx === 2) { return '星期三'; }
    if (idx === 3) { return '星期四'; }
    if (idx === 4) { return '星期五'; }
    if (idx === 5) { return '星期六'; }
    return '星期日';
  }

  private getSpotTranslationX(angle: number, radius: number, center: number): number {
    let rad: number = angle * Math.PI / 180;
    return center + Math.cos(rad) * radius;
  }

  private getSpotTranslationY(angle: number, radius: number, center: number): number {
    let rad: number = angle * Math.PI / 180;
    return center + Math.sin(rad) * radius;
  }

  @Builder
  private cardBackdrop(pageIndex: number) {
    Column() {
    }
    .width('92%')
    .height('92%')
    .borderRadius(28)
    .linearGradient({
      angle: (this.gradientAngle1 + pageIndex * 60) % 360,
      colors: [
        ['rgba(17,34,64,0.68)', 0],
        ['rgba(10,25,47,0.58)', 0.5],
        ['rgba(26,52,94,0.62)', 1]
      ]
    })
    .border({
      width: 1,
      color: 'rgba(255,215,0,0.25)'
    })
    .shadow({
      radius: 24,
      color: 'rgba(77,157,224,0.22)',
      offsetX: 0,
      offsetY: 4
    })
  }

  @Builder
  private backgroundBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      Column() {
      }
      .width('100%')
      .height('100%')
      .linearGradient({
        direction: GradientDirection.Bottom,
        colors: [['#050E1F', 0], ['#0A192F', 0.5], ['#0F2847', 1]]
      })

      Circle({ width: 180, height: 180 })
        .fill(Color.White)
        .radialGradient({
          center: [50, 50],
          radius: 80,
          colors: [['rgba(77,157,224,0.75)', 0], ['rgba(77,157,224,0.15)', 0.55], ['rgba(77,157,224,0)', 1]]
        })
        .translate({
          x: this.getSpotTranslationX(this.spot1Angle, 80, 0),
          y: this.getSpotTranslationY(this.spot1Angle, 60, 0)
        })
        .blur(18)
        .animation({
          duration: 50,
          curve: Curve.Linear,
          iterations: 1,
          playMode: PlayMode.Normal
        })

      Circle({ width: 200, height: 200 })
        .fill(Color.White)
        .radialGradient({
          center: [50, 50],
          radius: 90,
          colors: [['rgba(199,125,255,0.65)', 0], ['rgba(199,125,255,0.12)', 0.55], ['rgba(199,125,255,0)', 1]]
        })
        .translate({
          x: this.getSpotTranslationX(this.spot2Angle, 70, 10),
          y: this.getSpotTranslationY(this.spot2Angle, 70, -10)
        })
        .blur(22)
        .animation({
          duration: 50,
          curve: Curve.Linear,
          iterations: 1,
          playMode: PlayMode.Normal
        })

      Circle({ width: 160, height: 160 })
        .fill(Color.White)
        .radialGradient({
          center: [50, 50],
          radius: 70,
          colors: [['rgba(255,107,157,0.6)', 0], ['rgba(0,245,212,0.25)', 0.5], ['rgba(0,245,212,0)', 1]]
        })
        .translate({
          x: this.getSpotTranslationX(this.spot3Angle, 90, -15),
          y: this.getSpotTranslationY(this.spot3Angle, 50, 20)
        })
        .blur(20)
        .animation({
          duration: 50,
          curve: Curve.Linear,
          iterations: 1,
          playMode: PlayMode.Normal
        })

      Circle({ width: 60, height: 60 })
        .fill(Color.White)
        .radialGradient({
          center: [50, 50],
          radius: 28,
          colors: [['rgba(255,215,0,0.55)', 0], ['rgba(255,215,0,0.08)', 0.6], ['rgba(255,215,0,0)', 1]]
        })
        .translate({ x: 20, y: -30 })
        .blur(8)
    }
    .width('100%')
    .height('100%')
    .renderGroup(true)
  }

  @Builder
  private fluidDigitBuilder(digit: string, angle1: number, angle2: number, angle3: number, fSize: number) {
    Stack({ alignContent: Alignment.Center }) {
      Text(digit)
        .fontSize(fSize)
        .fontWeight(FontWeight.Bolder)
        .fontColor('#00FFFFFF')
        .linearGradient({
          angle: angle1,
          colors: [['#4D9DE0', 0], ['#C77DFF', 0.5], ['#FF6B9D', 1]]
        })
        .opacity(0.55)
        .blur(6)
        .scale({ x: 1.08, y: 1.08 })

      Text(digit)
        .fontSize(fSize)
        .fontWeight(FontWeight.Bolder)
        .linearGradient({
          angle: angle2,
          colors: [
            ['#00F5D4', 0],
            ['#4D9DE0', 0.25],
            ['#FFD700', 0.5],
            ['#C77DFF', 0.75],
            ['#00F5D4', 1]
          ]
        })
        .opacity(0.85)

      Text(digit)
        .fontSize(fSize)
        .fontWeight(FontWeight.Bolder)
        .linearGradient({
          angle: angle3,
          colors: [['#FFD700', 0], ['#FFFFFF', 0.4], ['#FF6B9D', 0.75], ['#C77DFF', 1]]
        })
        .opacity(0.95)
    }
    .renderGroup(true)
  }

  @Builder
  private colonBuilder(fSize: number) {
    Column() {
      Circle({ width: fSize * 0.12, height: fSize * 0.12 })
        .fill('#FFD700')
        .shadow({ radius: 12, color: '#80FFD700', offsetX: 0, offsetY: 0 })
      Blank()
        .height(fSize * 0.22)
      Circle({ width: fSize * 0.12, height: fSize * 0.12 })
        .fill('#FFD700')
        .shadow({ radius: 12, color: '#80FFD700', offsetX: 0, offsetY: 0 })
    }
    .width(fSize * 0.25)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  private clockCardBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      this.cardBackdrop(0)

      Stack({ alignContent: Alignment.Center }) {
        Circle({ width: 230, height: 230 })
          .fill(Color.White)
          .radialGradient({
            center: [50, 50],
            radius: 115,
            colors: [['rgba(255,215,0,0.18)', 0], ['rgba(77,157,224,0.08)', 0.5], ['rgba(0,0,0,0)', 1]]
          })
          .scale({ x: this.breathScale, y: this.breathScale })
          .animation({ duration: 1500, curve: Curve.EaseInOut, iterations: -1, playMode: PlayMode.Alternate })

        Column({ space: 0 }) {
          Row() {
            this.fluidDigitBuilder(
              this.hours.charAt(0),
              this.gradientAngle1,
              this.gradientAngle2,
              this.gradientAngle3,
              68
            )
            this.fluidDigitBuilder(
              this.hours.charAt(1),
              (this.gradientAngle1 + 60) % 360,
              (this.gradientAngle2 + 90) % 360,
              (this.gradientAngle3 + 30) % 360,
              68
            )
            this.colonBuilder(68)
            this.fluidDigitBuilder(
              this.minutes.charAt(0),
              (this.gradientAngle1 + 180) % 360,
              (this.gradientAngle2 + 200) % 360,
              (this.gradientAngle3 + 150) % 360,
              68
            )
            this.fluidDigitBuilder(
              this.minutes.charAt(1),
              (this.gradientAngle1 + 240) % 360,
              (this.gradientAngle2 + 270) % 360,
              (this.gradientAngle3 + 210) % 360,
              68
            )
          }
          Row() {
            Text(this.seconds)
              .fontSize(24)
              .fontWeight(FontWeight.Bold)
              .linearGradient({
                angle: this.gradientAngle1,
                colors: [['#00F5D4', 0], ['#4D9DE0', 0.5], ['#FFD700', 1]]
              })
              .margin({ right: 6 })
            Text('秒')
              .fontSize(12)
              .fontColor('#8892B0')
          }
          .margin({ top: 6 })
        }
        .alignItems(HorizontalAlign.Center)
      }
      .width('92%')
      .height('92%')
    }
    .width('100%')
    .height('100%')
    .renderGroup(true)
  }

  @Builder
  private weatherIconBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      Circle({ width: 36, height: 36 })
        .fill(Color.White)
        .radialGradient({
          center: [50, 50],
          radius: 18,
          colors: [['#FFD700', 0], ['#FFA500', 0.7], ['#FF8C00', 1]]
        })
        .shadow({ radius: 12, color: '#80FFD700', offsetX: 0, offsetY: 0 })
        .translate({ x: -10, y: -10 })

      Column() {
      }
      .width(50)
      .height(22)
      .borderRadius(11)
      .linearGradient({
        angle: 0,
        colors: [['#4D9DE0', 0], ['#C77DFF', 1]]
      })
      .translate({ x: 8, y: 4 })
      .opacity(0.95)

      Circle({ width: 20, height: 20 })
        .fill('#C77DFF')
        .translate({ x: -2, y: -4 })
      Circle({ width: 26, height: 26 })
        .fill('#4D9DE0')
        .translate({ x: 12, y: -2 })
    }
    .width(90)
    .height(70)
  }

  @Builder
  private dateWeatherCardBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      this.cardBackdrop(1)

      Column() {
        Text(this.dateStr)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#E6F1FF')
          .margin({ top: 14 })

        Text(this.getWeekdayString(this.weekdayIdx))
          .fontSize(12)
          .fontColor('#8892B0')
          .margin({ top: 2 })

        Blank()
          .height(10)

        this.weatherIconBuilder()

        Blank()
          .height(4)

        Row() {
          Text('26°')
            .fontSize(30)
            .fontWeight(FontWeight.Bolder)
            .linearGradient({
              angle: this.gradientAngle2,
              colors: [['#FFD700', 0], ['#FF6B9D', 0.5], ['#C77DFF', 1]]
            })
          Text('晴')
            .fontSize(14)
            .fontColor('#E6F1FF')
            .margin({ left: 8, top: 12 })
        }

        Text('深圳')
          .fontSize(11)
          .fontColor('#8892B0')
          .margin({ top: 2 })
      }
      .width('92%')
      .height('92%')
      .justifyContent(FlexAlign.Start)
      .alignItems(HorizontalAlign.Center)
    }
    .width('100%')
    .height('100%')
    .renderGroup(true)
  }

  @Builder
  private stepsRingBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      Circle({ width: 110, height: 110 })
        .fill('rgba(255,255,255,0.04)')
        .stroke('#1A2A4A')
        .strokeWidth(8)

      Circle({ width: 110, height: 110 })
        .fill('rgba(0,0,0,0)')
        .stroke(Color.White)
        .strokeWidth(8)
        .strokeDashArray([
          345.6 * (this.steps / this.stepsTarget),
          345.6
        ])
        .strokeDashOffset(-86.4)
        .linearGradient({
          angle: this.gradientAngle1,
          colors: [['#00F5D4', 0], ['#4D9DE0', 0.5], ['#FFD700', 1]]
        })
        .animation({ duration: 800, curve: Curve.EaseOut })

      Column({ space: 0 }) {
        Text(this.steps.toString())
          .fontSize(22)
          .fontWeight(FontWeight.Bolder)
          .linearGradient({
            angle: this.gradientAngle3,
            colors: [['#FFD700', 0], ['#FF6B9D', 1]]
          })
        Text('步数')
          .fontSize(10)
          .fontColor('#8892B0')
          .margin({ top: 1 })
        Text('目标 ' + this.stepsTarget.toString())
          .fontSize(9)
          .fontColor('#8892B0')
          .margin({ top: 1 })
      }
      .alignItems(HorizontalAlign.Center)
    }
    .width(120)
    .height(120)
  }

  @Builder
  private heartSectionBuilder() {
    Column({ space: 0 }) {
      Row() {
        Column() {
          Text('♥')
            .fontSize(18)
            .fontColor('#FF6B9D')
            .scale({ x: this.breathScale, y: this.breathScale })
            .animation({
              duration: 900,
              curve: Curve.EaseInOut,
              iterations: -1,
              playMode: PlayMode.Alternate
            })
        }
        .margin({ right: 6 })
        Column({ space: 0 }) {
          Text(this.heartRate.toString())
            .fontSize(26)
            .fontWeight(FontWeight.Bolder)
            .linearGradient({
              angle: this.gradientAngle2,
              colors: [['#FF6B9D', 0], ['#C77DFF', 0.6], ['#FFD700', 1]]
            })
          Text('BPM')
            .fontSize(9)
            .fontColor('#8892B0')
        }
      }
      Text('心率')
        .fontSize(10)
        .fontColor('#8892B0')
        .margin({ top: 2 })
    }
    .alignItems(HorizontalAlign.Center)
  }

  @Builder
  private healthCardBuilder() {
    Stack({ alignContent: Alignment.Center }) {
      this.cardBackdrop(2)

      Column() {
        this.stepsRingBuilder()
        Blank()
          .height(10)
        this.heartSectionBuilder()
      }
      .width('92%')
      .height('92%')
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)
    }
    .width('100%')
    .height('100%')
    .renderGroup(true)
  }

  @Builder
  private pageIndicatorBuilder() {
    Row() {
      ForEach([0, 1, 2], (idx: number) => {
        if (idx === this.currentIndex) {
          Column() {
          }
          .width(16)
          .height(4)
          .borderRadius(2)
          .linearGradient({
            angle: 0,
            colors: [['#FFD700', 0], ['#FF6B9D', 1]]
          })
          .animation({ duration: 260, curve: Curve.EaseOut })
          .margin({ left: 3, right: 3 })
        } else {
          Column() {
          }
          .width(4)
          .height(4)
          .borderRadius(2)
          .backgroundColor('rgba(136,146,176,0.55)')
          .animation({ duration: 260, curve: Curve.EaseOut })
          .margin({ left: 3, right: 3 })
        }
      }, (idx: number) => idx.toString())
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }

  build() {
    Stack({ alignContent: Alignment.Center }) {
      this.backgroundBuilder()

      Column() {
        Swiper() {
          this.clockCardBuilder()
          this.dateWeatherCardBuilder()
          this.healthCardBuilder()
        }
        .width('100%')
        .height('84%')
        .index(this.currentIndex)
        .autoPlay(false)
        .indicator(false)
        .loop(true)
        .curve(Curve.Smooth)
        .duration(420)
        .itemSpace(0)
        .displayMode(SwiperDisplayMode.Stretch)
        .cachedCount(2)
        .onChange((index: number) => {
          animateTo({ duration: 260, curve: Curve.EaseOut }, () => {
            this.currentIndex = index;
          });
        })

        Blank()
          .height(4)

        this.pageIndicatorBuilder()
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)
      .padding({ top: 18, bottom: 12 })
    }
    .width('100%')
    .height('100%')
    .renderGroup(true)
  }
}

初始页面效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

简洁化处理一下

在这里插入图片描述
简单多了。
在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐