鸿蒙多设备应用开发——wearable开发实战
·
实战机型

虚拟机说明
由于我真机的是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)
}
}
初始页面效果



简洁化处理一下

简单多了。
更多推荐




所有评论(0)