前言
本篇实现一种带有背景色黑白渐变呼吸效果 并且带有翻转卡片动效的数字时钟屏保。 随着秒钟卡片的翻转和背景色有节奏的变换,给人一种时间飞逝的压迫感。仿佛也暗示着作者急切得盼望着坐上下班回家的公交车。
有兴趣的同学可以跟着做,但建议不要长时间使用该款时间屏保。看多了以后明显会对精神状态有影响。
效果展示
实现原理
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)
}
}
}
总结
眼睛有点花了已经开始,写这个系列非常不容易,就像每天不知道中午点什么外卖,需要耗费许多想象力。
望各位耗子尾汁,点赞关注收藏三连。感谢大家!下次见~