1. 項(xiàng)目背景
翻頁(yè)時(shí)鐘(Flip Clock)是一種有趣的機(jī)電數(shù)字計(jì)時(shí)設(shè)備,用電腦動(dòng)畫的方式實(shí)現(xiàn)翻頁(yè)時(shí)鐘,也是一種特別的復(fù)古UI交互體驗(yàn)。
本項(xiàng)目豈在通過(guò)OpenHarmony的ArkUI框架,用TS擴(kuò)展的聲明式開發(fā)范式eTS,來(lái)實(shí)現(xiàn)翻頁(yè)時(shí)鐘的體驗(yàn)。
本項(xiàng)目的開發(fā)環(huán)境如下:
具體實(shí)現(xiàn)的效果是這樣的:
本項(xiàng)目的主要知識(shí)點(diǎn)如下:
-
UI狀態(tài):@Prop、@Link、@Watch
-
形狀裁剪屬性:clip
-
顯式動(dòng)畫:animateTo
使用基于eTS的聲明式開發(fā)范式的方舟開發(fā)框架,采用更接近自然語(yǔ)義的編程方式,讓開發(fā)者可以直觀地描述UI界面,不必關(guān)心框架如何實(shí)現(xiàn)UI繪制和渲染,實(shí)現(xiàn)極簡(jiǎn)高效開發(fā)。開發(fā)框架不僅從組件、動(dòng)效和狀態(tài)管理三個(gè)維度來(lái)提供UI能力,還提供了系統(tǒng)能力接口,實(shí)現(xiàn)系統(tǒng)能力的極簡(jiǎn)調(diào)用。
關(guān)于語(yǔ)法和概念詳細(xì)請(qǐng)直接看官網(wǎng)官方文檔地址:
https://docs.openharmony.cn/pages/v3.1/zh-cn/application-dev/ui/ui-ts-overview.md/
3. 實(shí)現(xiàn)思路時(shí)鐘翻頁(yè)效果,用到四個(gè)Text組件,使用堆疊容器Stack。底層:用到兩個(gè)裁剪過(guò)后的Text上下顯示;頂層:也是用兩個(gè)裁剪后的Text做動(dòng)畫效果,進(jìn)行X軸角度旋轉(zhuǎn)。3.1 裁剪Text
裁剪前:
裁剪后:
使用形狀裁剪屬性clip
裁剪Text上半部:從坐標(biāo)(0,0)往下裁剪,clip(new Rect({ width: this.width, height: this.height / 2 }))
裁剪Text下半部:從坐標(biāo)(0,height / 2)往下裁剪,clip(new Path().commands(this.bottomPath))
struct Test {
private width = 90
private height = 110
private fontSize = 70
private defaultBgColor = '#ffe6e6e6'
private borderRadius = 10
// 下半部裁剪路徑
private bottomPath = `M0 ${vp2px(this.height / 2)}
L${vp2px(this.width)} ${vp2px(this.height / 2)}
L${vp2px(this.width)} ${vp2px(this.height)}
L0 ${vp2px(this.height)} Z`
build() {
Row() {
Text('24')
.width(this.width)
.height(this.height)
.fontColor(Color.Black)
.fontSize(this.fontSize)
.textAlign(TextAlign.Center)
.borderRadius(this.borderRadius)
.backgroundColor(this.defaultBgColor)
.clip(new Rect({ width: this.width, height: this.height / 2 }))
Text('25')
.margin({left:20})
.width(this.width)
.height(this.height)
.fontColor(Color.Black)
.fontSize(this.fontSize)
.textAlign(TextAlign.Center)
.borderRadius(this.borderRadius)
.backgroundColor(this.defaultBgColor)
.clip(new Path().commands(this.bottomPath))
}.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
3.2放入堆疊容器四個(gè)裁剪后的Text放入到堆疊容器中(代碼片段):
Stack() {
// 底層文字上部
Text(this.newValue)
......
.clip(new Rect({ width: this.width, height: this.height / 2 }))
// 底層文字下部
Text(this.oldValue)
......
.clip(new Path().commands(this.bottomPath))
// 頂層文字上部動(dòng)畫
Text(this.oldValue)
......
.clip(new Rect({ width: this.width, height: this.height / 2 }))
.rotate({ x: 1, centerY: '50%', angle: this.angleTop })
// 頂層文字下部動(dòng)畫
Text(this.newValue)
......
.margin({ top: 3 })
.clip(new Path().commands(this.bottomPath))
.rotate({ x: 1, centerY: '50%', angle: this.angleBottom })
}
3.3使用顯式動(dòng)畫先頂層上部的動(dòng)畫,上部旋轉(zhuǎn)角度從0到90停止,接下來(lái)執(zhí)行頂層下部的動(dòng)畫,下部旋轉(zhuǎn)角度從-90到0停止,停止完后重置初始狀態(tài),上部旋轉(zhuǎn)角度 = 0、下部旋轉(zhuǎn)角度 = -90(代碼片段)
/**
* 啟動(dòng)頂層文字上部動(dòng)畫
*/
startTopAnimate() {
animateTo({
duration: 400,
onFinish: () => {
this.startBottomAnimate()
this.animateBgColor = '#ffededed'
}
}, () => {
this.angleTop = 90
this.animateBgColor = '#ffc5c5c5'
})
}
/**
* 啟動(dòng)頂層文字下部動(dòng)畫
*/
startBottomAnimate() {
animateTo({
duration: 400,
onFinish: () => {
this.angleTop = 0
this.angleBottom = -90
this.animateBgColor = this.defaultBgColor
this.oldValue = this.newValue
}
}, () => {
this.angleBottom = 0
this.animateBgColor = this.defaultBgColor
})
}
3.4組件封裝翻頁(yè)邏輯封裝成組件,提供給外部調(diào)用,根據(jù)外部傳入的雙向數(shù)據(jù)綁定:newValue,監(jiān)聽(tīng)數(shù)據(jù)變化,有變化則啟動(dòng)翻頁(yè)動(dòng)畫(代碼片段):
export struct FlipPage {
// 頂層上部動(dòng)畫角度
0
angleTop: number = // 頂層下部動(dòng)畫角度
90
angleBottom: number = - // 舊值
oldValue: string // 新值,加入監(jiān)聽(tīng)
newValue: string /**
* 監(jiān)聽(tīng)新值變化
*/
valueChange() {
if (this.oldValue === this.newValue) return
this.startTopAnimate()
}
build() {
Stack() {
// 底層文字上部
Text(this.newValue)
......
.clip(new Rect({ width: this.width, height: this.height / 2 }))
// 底層文字下部
Text(this.oldValue)
......
.clip(new Path().commands(this.bottomPath))
// 頂層文字上部動(dòng)畫
Text(this.oldValue)
......
.clip(new Rect({ width: this.width, height: this.height / 2 }))
.rotate({ x: 1, centerY: '50%', angle: this.angleTop })
// 頂層文字下部動(dòng)畫
Text(this.newValue)
......
.margin({ top: 3 })
.clip(new Path().commands(this.bottomPath))
.rotate({ x: 1, centerY: '50%', angle: this.angleBottom })
}
}
/**
* 啟動(dòng)頂層文字上部動(dòng)畫
*/
startTopAnimate() {
......
}
3.5外部調(diào)用界面加載成功后,開啟循環(huán)定時(shí)器setInterval、間隔1秒更新時(shí)間。更改newValue的值,翻頁(yè)組件內(nèi)部進(jìn)行動(dòng)畫翻頁(yè)。
import { FlipPage } from '../componet/FlipPage'
struct Index {
// 小時(shí)-舊值
rs: string = ''
oldHou // 小時(shí)-新值
string = ''
newHours: // 分鐘-舊值
string = ''
oldMinutes: // 分鐘-新值
string = ''
newMinutes: // 秒數(shù)-舊值
string = ''
oldSeconds: // 秒數(shù)-新值
string = ''
newSeconds:
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() {
// 翻頁(yè)組件-顯示小時(shí)
FlipPage({ oldValue: this.oldHours, newValue: $newHours })
// 冒號(hào)
this.Colon()
// 翻頁(yè)組件-顯示分鐘
FlipPage({ oldValue: this.oldMinutes, newValue: $newMinutes })
// 冒號(hào)
this.Colon()
// 翻頁(yè)組件-顯示秒數(shù)
FlipPage({ oldValue: this.oldSeconds, newValue: $newSeconds })
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
.onAppear(() => {
// 開啟定時(shí)器
this.initDate()
setInterval(() => {
this.updateDate()
}, 1000)
})
}
/**
* 初始化時(shí)間
*/
initDate() {
let date = new Date()
// 設(shè)置小時(shí)
this.oldHours = this.format(date.getHours())
// 設(shè)置分鐘
this.oldMinutes = this.format(date.getMinutes())
// 設(shè)置秒數(shù)
this.oldSeconds = this.format(date.getSeconds())
// 設(shè)置新的秒數(shù)
this.newSeconds = date.getSeconds() + 1 === 60 ? '00' : this.format(date.getSeconds() + 1)
}
/**
* 更新時(shí)間
*/
updateDate() {
let date = new Date()
console.log(`${date.getHours()}時(shí)${date.getMinutes()}分${date.getSeconds()}秒`)
// 當(dāng)新值改變,才有動(dòng)畫
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)
}
}
/**
* 不足十位前面補(bǔ)零
*/
format(param) {
let value = '' + param
if (param < 10) {
value = '0' + param
}
return value
}
}
4.總結(jié)根據(jù)上面的實(shí)現(xiàn)思路和5個(gè)步驟流程,相信你也掌握了翻頁(yè)時(shí)鐘原理,拆分成一步一步還是很簡(jiǎn)單的,最主要還是對(duì)API的熟悉和聲明式語(yǔ)法的掌握。HarmonyOS的API是根據(jù)OpenHarmony去更新的,兩者區(qū)別語(yǔ)法都一樣,只是OpenHarmony的API比較新,功能比較完善和成熟的,所以本項(xiàng)目直接使用OpenHarmony SDK開發(fā)。
本文完寫在最后我們最近正帶著大家玩嗨OpenHarmony。如果你有好玩的東東,歡迎投稿,讓我們一起嗨起來(lái)!有點(diǎn)子,有想法,有Demo,立刻聯(lián)系我們:合作郵箱:zzliang@atomsource.org
原文標(biāo)題:玩嗨OpenHarmony:基于OpenHarmony的ArkUI翻頁(yè)時(shí)鐘
文章出處:【微信公眾號(hào):開源技術(shù)服務(wù)中心】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
開源技術(shù)
+關(guān)注
關(guān)注
0文章
389瀏覽量
7950 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3725瀏覽量
16370
原文標(biāo)題:玩嗨OpenHarmony:基于OpenHarmony的ArkUI翻頁(yè)時(shí)鐘
文章出處:【微信號(hào):開源技術(shù)服務(wù)中心,微信公眾號(hào):共熵服務(wù)中心】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論