From db3b628a14994d7f32d390038f2809373c115908 Mon Sep 17 00:00:00 2001 From: cheliangzhao Date: Mon, 25 Sep 2023 19:32:27 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feat:=20finish=20cards=20game?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entry/src/main/ets/components/CardView.ets | 84 ++++++++++++++++ entry/src/main/ets/components/GameView.ets | 37 ++++++++ .../main/ets/entryability/EntryAbility.ets | 2 +- entry/src/main/ets/models/Card.ets | 12 +++ entry/src/main/ets/models/ObservedArray.ets | 10 ++ entry/src/main/ets/models/viewModel.ets | 35 +++++++ entry/src/main/ets/pages/GamePage.ets | 45 +++++++++ entry/src/main/ets/pages/Index.ets | 95 ------------------- entry/src/main/ets/pages/OverPage.ets | 28 ++++++ .../resources/base/profile/main_pages.json | 3 +- 10 files changed, 254 insertions(+), 97 deletions(-) create mode 100644 entry/src/main/ets/components/CardView.ets create mode 100644 entry/src/main/ets/components/GameView.ets create mode 100644 entry/src/main/ets/models/Card.ets create mode 100644 entry/src/main/ets/models/ObservedArray.ets create mode 100644 entry/src/main/ets/models/viewModel.ets create mode 100644 entry/src/main/ets/pages/GamePage.ets delete mode 100644 entry/src/main/ets/pages/Index.ets create mode 100644 entry/src/main/ets/pages/OverPage.ets diff --git a/entry/src/main/ets/components/CardView.ets b/entry/src/main/ets/components/CardView.ets new file mode 100644 index 0000000..70cca5b --- /dev/null +++ b/entry/src/main/ets/components/CardView.ets @@ -0,0 +1,84 @@ +import { Card } from '../models/Card' + +@Component +export struct CardView { + @ObjectLink card: Card + @Consume count: number + @State cardRotateAngle: number = 0 + @State angle: number = 0 + + animate() { + if (this.card.isMatch) { + this.cardRotateAngle = 0 + animateTo({ + duration: 1000, + tempo: 1, + curve: Curve.Linear, + delay: 200, + iterations: -1, + playMode: PlayMode.Normal + }, () => { + this.angle = 360 + }) + } + if (this.card.isFaceUp && !this.card.isMatch) { + animateTo({ + duration: 300, + tempo: 2, + curve: Curve.Linear, + delay: 0, + iterations: 1, + playMode: PlayMode.Normal + }, () => { + this.cardRotateAngle = 180 + }) + } else if (!this.card.isFaceUp) { + animateTo({ + duration: 300, + tempo: 2, + curve: Curve.Linear, + delay: 0, + iterations: 1, + playMode: PlayMode.Normal + }, () => { + this.cardRotateAngle = 0 + }) + } + } + + reverse() { + this.card.isFaceUp = !this.card.isFaceUp + this.count += 1 + this.animate() + } + + build() { + Column() { + Column() { + } + .width('100%') + .height('100%') + .border({ color: '#fffa9e0f', width: 2, radius: 15 }) + .backgroundColor(this.card.isFaceUp ? '#fff5f0f0' : '#fffa9e0f') + .aspectRatio(2 / 3) + .rotate({ x: 0, y: 1, z: 0, angle: this.cardRotateAngle }) + .onClick(() => { + this.reverse() + }) + + Text(this.card.name) + .textAlign(TextAlign.Center) + .visibility(this.card.isFaceUp ? Visibility.Visible : Visibility.Hidden) + .fontSize(20) + .position({ x: '50%', y: '50%' }) + .markAnchor({ x: '50%', y: '50%' }) + //.rotate(this.card.isMatcsh ? {angle:this.angle} : {angle:360}) + } + .padding({ left: 2, right: 2 }) + .margin({ top: 10 }) + .width(80) + .height(120) + } +} + + diff --git a/entry/src/main/ets/components/GameView.ets b/entry/src/main/ets/components/GameView.ets new file mode 100644 index 0000000..751df5a --- /dev/null +++ b/entry/src/main/ets/components/GameView.ets @@ -0,0 +1,37 @@ +import { CardView } from './CardView' +import { ObservedArray } from '../models/ObservedArray' +import { Card } from '../models/Card' + +@Component +export struct GameView { + @ObjectLink cards: ObservedArray + + @Builder + getTitle() { + Text("卡片小游戏") + .fontSize(20) + .fontWeight(FontWeight.Bold) + .width('100%') + } + + build() { + Column() { + Row() { + this.getTitle() + } + + Flex({ + direction: FlexDirection.Row, + wrap: FlexWrap.Wrap, + justifyContent: FlexAlign.SpaceEvenly, + alignContent: FlexAlign.SpaceEvenly + }) { + ForEach(this.cards, (card, idx) => { + CardView({ card: card }) + }) + } + } + .padding({ left: 6, right: 6 }) + .margin({ top: 20 }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index 7af2367..d00702f 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -17,7 +17,7 @@ export default class EntryAbility extends UIAbility { // Main window is created, set main page for this ability hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); - windowStage.loadContent('pages/Index', (err, data) => { + windowStage.loadContent('pages/GamePage', (err, data) => { if (err.code) { hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; diff --git a/entry/src/main/ets/models/Card.ets b/entry/src/main/ets/models/Card.ets new file mode 100644 index 0000000..22672af --- /dev/null +++ b/entry/src/main/ets/models/Card.ets @@ -0,0 +1,12 @@ +@Observed +export class Card { + name: string + id: string + isFaceUp: boolean = false + isMatch: boolean = false + + constructor(name: string) { + this.name = name + this.id = Date.now() + this.name + } +} diff --git a/entry/src/main/ets/models/ObservedArray.ets b/entry/src/main/ets/models/ObservedArray.ets new file mode 100644 index 0000000..7185ac2 --- /dev/null +++ b/entry/src/main/ets/models/ObservedArray.ets @@ -0,0 +1,10 @@ +@Observed +export class ObservedArray extends Array { + constructor(args?: T[]) { + if (args instanceof Array) { + super(...args) + } else { + super() + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/models/viewModel.ets b/entry/src/main/ets/models/viewModel.ets new file mode 100644 index 0000000..3504129 --- /dev/null +++ b/entry/src/main/ets/models/viewModel.ets @@ -0,0 +1,35 @@ +import { Card } from './Card' +import { ObservedArray } from './ObservedArray' + +export class CardViewModel { + cardList: ObservedArray = [] + + constructor() { + this.initCardList() + } + + + // 初始化cardList赋值 + private initCardList() { + const words = ["A", "A", "B", "B", "C", "C", "D", "D", "E", "E", "F", "F"] + this.cardList = new ObservedArray(words.map(w => new Card(w))) + this.shuffle() + } + + // 打乱元素 + private shuffle() { + this.cardList.forEach((_, idx) => { + const j = Math.floor(Math.random() * (idx + 1)); // 生成随机索引 + [this.cardList[idx], this.cardList[j]] = [this.cardList[j], this.cardList[idx]]; // 交换元素 + }) + } + + public getCardList() { + return this.cardList + } + + // 判断是否全部翻面 + public isAllFaceUp() { + return !this.cardList.some(card =>!card.isFaceUp) + } +} diff --git a/entry/src/main/ets/pages/GamePage.ets b/entry/src/main/ets/pages/GamePage.ets new file mode 100644 index 0000000..9ccba40 --- /dev/null +++ b/entry/src/main/ets/pages/GamePage.ets @@ -0,0 +1,45 @@ +import { GameView } from '../components/GameView' +import { CardViewModel } from '../models/viewModel' +import router from '@ohos.router' + +@Preview +@Entry +@Component +struct Index { + @State viewModel: CardViewModel = new CardViewModel() + @Provide @Watch("handleAllFaceUp") count: number = 0 + private time: number = 0 + private interval: number + + onPageShow() { + this.interval = setInterval(() => { + this.time++; + }, 1000) + } + + onPageHide() { + if (this.interval) clearTimeout(this.interval) + } + + /** + * 当所有Cards都反面,跳转About page + */ + handleAllFaceUp() { + if (this.viewModel.isAllFaceUp()) { + router.pushUrl({ + url: "pages/OverPage", + params: { + count: this.count, + time: this.time, + } + }) + } + } + + build() { + Column() { + GameView({ cards: this.viewModel.getCardList() }) + } + } +} + diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets deleted file mode 100644 index 4f8f5aa..0000000 --- a/entry/src/main/ets/pages/Index.ets +++ /dev/null @@ -1,95 +0,0 @@ -@Preview -@Entry -@Component -struct Index { - @State message: string = 'Hello World'; - @State messageList: any[] = [ - { message: this.message, checked: false }, - { message: this.message, checked: false }, - { message: this.message, checked: false }, - { message: this.message, checked: false }, - { message: this.message, checked: false }, - ] - private textController: TextInputController = new TextInputController() - private tabsController: TabsController = new TabsController() - - build() { - Column() { - Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) { - TabContent() { - Column() { - Text(this.message) - .fontSize(20) - .fontWeight(FontWeight.Bold) - .width('100%') - .padding({ left: 10, right: 10 }) - - List() { - ForEach(this.messageList, (item, idx) => { - ListItem() { - Row() { - Checkbox() - .select(item.checked) - .onChange((value) => { - // item.checked = value // array list 修改 item 的属性无法触发 UI 更新 - // this.messageList[idx].checked = value // array list 修改 item 的属性无法触发 UI 更新 - this.messageList[idx] = { ...item, checked: value } // array list 替换 item 触发 UI 更新 - // array list 整体替换,触发 UI 更新 - // const newMessageList = this.messageList - // newMessageList[idx].checked = value - // this.messageList = [...newMessageList] - }) - Text(item.message) - .decoration({ type: item.checked ? TextDecorationType.LineThrough : TextDecorationType.None }) - } - } - .align(Alignment.Start) - .width('100%') - .margin({ top: 4 }) - .padding({ left: 4, right: 4, top: 2, bottom: 2 }) - .backgroundColor(Color.Gray) - .borderRadius(20) - }) - } - .listDirection(Axis.Vertical) - } - } - .tabBar('green') - .align(Alignment.Top) - - TabContent() { - Column() { - TextInput({ placeholder: '请输入密码', controller: this.textController }) - .type(InputType.Password) - .margin({ top: 4 }) - - Button('设置光标位置', { type: ButtonType.Capsule, stateEffect: true }) - .onClick(() => { - this.textController.caretPosition(2) - }) - } - } - .tabBar("red") - .align(Alignment.Top) - - TabContent() { - LoadingProgress() - .color(Color.Blue) - .height(60) - .width(60) - } - .tabBar('yellow') - .align(Alignment.Top) - } - .barWidth('100%') // 设置TabBar宽度 - .barHeight(60) // 设置TabBar高度 - .width('100%') // 设置Tabs组件宽度 - .height('100%') // 设置Tabs组件高度 - .backgroundColor(0xF5F5F5) // 设置Tabs组件背景颜色 - .vertical(false) - .padding({ top: 10, left: 4, right: 2 }) - } - .height('100%') - .padding({ left: 4, right: 4 }) - } -} diff --git a/entry/src/main/ets/pages/OverPage.ets b/entry/src/main/ets/pages/OverPage.ets new file mode 100644 index 0000000..64cc1be --- /dev/null +++ b/entry/src/main/ets/pages/OverPage.ets @@ -0,0 +1,28 @@ +import router from '@ohos.router' + +@Preview +@Entry +@Component +struct About { + @State count: number = router.getParams()?.['count'] ?? 0 + @State time: number = router.getParams()?.['time'] ?? 0 + + build() { + Column() { + Text("恭喜通关!") + .fontSize(30) + .fontWeight(FontWeight.Bold) + Text(`点击了${this.count}次,共耗时${this.time}秒`) + .opacity(0.6) + Text('超越了99%的人') + .fontColor(Color.Blue) + Button("再来一次").margin({ top: 10 }) + .onClick(() => { + router.pushUrl({ url: "pages/GamePage" }) + }) + } + .justifyContent(FlexAlign.Center) + .height('100%') + .width('100%') + } +} diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 1898d94..acfd4d9 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -1,5 +1,6 @@ { "src": [ - "pages/Index" + "pages/GamePage", + "pages/OverPage" ] }