iT邦幫忙

2024 iThome 鐵人賽

DAY 18
0
Modern Web

創意前端設計:用 Vue.js 打造 30 個互動實用功能系列 第 18

Day18 Vue.js 動效分類實戰 (10) 旋轉特輯 - 打造讓你愛不釋手的互動小遊戲!

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241002/20124462V2DoyFXw1l.jpg


旋轉特效大解析:創造令人上癮的互動遊戲體驗

嘿!你有沒有注意過那些可愛的按鈕或圖標,在網頁上輕輕一碰就會旋轉起來,簡單卻讓人忍不住再多點幾次?
其實這就是網頁設計中的旋轉動效,不僅能讓頁面看起來更有趣,還能帶給使用者一種「哇,這個網站好好玩」的感覺!

今天,我們要來聊聊這個超受歡迎的網頁動態特效——rotation(旋轉效果)

不過,光是讓一個元素轉起來怎麼夠呢?
我們還會從基礎講起,然後一步步進階,教你怎麼把這些旋轉效果玩得更有創意、更有趣!

甚至,我們會用 Vue.js 來做一個簡單又讓人上癮的小遊戲,讓畫面動起來,讓人眼睛離不開!

我們先來看看怎麼實現最基本的左旋轉右旋轉特效,接著再進一步探討如何把它變成一個好玩的小遊戲。
準備好了嗎?Let's go~ 🎮


https://ithelp.ithome.com.tw/upload/images/20241002/20124462fyTB5RE80Q.png


來點旋轉壽司吧!

接下來,我們來看看如何用 Vue.js 實現一個簡單又有趣的旋轉動效!
這裡我們會創建一個 8x8 的方格,展示兩種旋轉特效:順時針旋轉逆時針旋轉

畫面上的元素會是一個個可愛的壽司 🍣 和旋轉 icon ↻,是不是有點餓了呢?
嘿嘿,別急,先讓它們轉起來,看看旋轉特效是怎麼讓這些小圖案更活潑有趣的吧!


  • TypeScript 部分:動態生成旋轉方格

img

<script lang="ts" setup>
import { computed } from "vue";

// 定義方格的行列數(8x8)
const pixelNum: number = 8;

// 定義一個包含旋轉圖案的字符列表
const characterList: string[] = ["🍣", "↻"]; // 我們用壽司和旋轉符號來展示旋轉效果

// sushiGrid 是一個計算屬性,用來生成 8x8 的網格,每個元素有自己的字符和旋轉方向
const sushiGrid = computed(() => {
  return Array.from({ length: pixelNum * pixelNum }, (_, index) => {
    // 計算當前元素的行和列
    const row = Math.floor(index / pixelNum);
    const column = index % pixelNum;

    // 根據行列的和決定字符(壽司或旋轉符號)
    const character = characterList[(row + column) % 2];

    // 根據行列的和來決定旋轉方向,行列和為偶數時設置為 reverse(逆時針旋轉),否則為順時針旋轉
    const rotationDirection = (row + column) % 2 === 0 ? "reverse" : "";

    // 返回一個包含字符和旋轉方向的物件
    return { character, rotationDirection };
  });
});
</script>
  • 解說:
  1. pixelNum:這裡我們定義了一個數字 8,表示網格是 8 行 8 列的方格,總共有 64 個元素。
  2. characterList:我們使用了兩個字符——一個是壽司符號 🍣,另一個是旋轉符號 ↻。這兩個符號將會被交替放置在方格中。
  3. sushiGrid:這是一個 Vue.js 的 computed(計算屬性),用來動態生成一個 8x8 的方格。
    • 透過 Array.from(),我們創建一個長度為 pixelNum * pixelNum 的陣列(這裡是 64 個元素)。
    • 我們用每個元素的 來決定它的 字符旋轉方向
    • 行列的和是偶數的時候,旋轉方向為 "reverse",代表逆時針旋轉。否則為順時針旋轉。

  • Template 部分:展示旋轉的方格
<template>
    <div id="sushiContainer">
        <!-- 這裡用 v-for 迴圈遍歷 sushiGrid 中的每個元素 -->
        <div v-for="(item, index) in sushiGrid" :key="index"
            :class="`${item.rotationDirection} flex justify-center items-center origin-center`">
            {{ item.character }}
        </div>
    </div>
</template>
  • 解說:
  1. v-for:我們使用 Vue 的 v-for 指令來遍歷 sushiGrid 中的每個元素,並且根據 index 來為每個元素分配一個唯一的 key
  2. item.rotationDirection:這裡根據每個元素的 rotationDirection 動態添加 reverse 或空字串作為 class,決定元素是順時針還是逆時針旋轉。
  3. item.character:每個格子中的字符是壽司 🍣 或旋轉符號 ↻,這取決於我們之前在 computed 中設定的行列規則。
  4. class 屬性:使用 Flexbox 將每個格子中的字符置中,並且設置旋轉的中心點為該元素的中心。

  • CSS 部分:定義旋轉動效
<style scoped>

#sushiContainer {
    --pixel-num: 8;
    display: grid;
    font-size: calc(100vw / var(--pixel-num)); /* 每個格子的大小會根據視窗寬度自動縮放 */

    --pixel-height: calc(100vw / var(--pixel-num)); /* 計算每個格子的高度 */
    grid-template:
        repeat(var(--pixel-num), var(--pixel-height)) /
        repeat(var(--pixel-num), var(--pixel-height)); /* 創建 8x8 方格佈局 */
}

#sushiContainer>* {
    animation: rotation 3s linear infinite; /* 為每個格子添加旋轉動畫,循環3秒 */
}

#sushiContainer .reverse {
    animation-direction: reverse; /* 當有 reverse class 時,元素逆時針旋轉 */
}

/* 定義旋轉動畫 */
@keyframes rotation {
    from {
        transform: rotate(0);
    }
    to {
        transform: rotate(360deg); /* 順時針旋轉一圈 */
    }
}
</style>
  • 解說:
  1. #sushiContainer:這裡我們使用 CSS Grid 來創建一個 8x8 的方格佈局,並且根據螢幕的寬度自動調整格子的大小。
  2. animation:每個元素都應用了一個叫做 rotation 的 CSS 動畫,持續時間為 3 秒,並且會無限循環。
  3. animation-direction: reverse:如果元素有 reverse 類名,它會以逆時針的方式旋轉。
  4. @keyframes rotation:這是關鍵的旋轉動畫,從 0 度旋轉到 360 度,讓元素順時針旋轉。

  • 旋轉壽司小結:

在這個範例中,我們成功用 Vue.js 和簡單的 CSS 實現了旋轉動效,讓壽司 🍣 和旋轉 icon ↻ 都活靈活現地轉了起來!
我們透過 左旋轉右旋轉 的搭配,讓每個格子都充滿動感,並巧妙地使用了 @keyframesanimation-direction 來輕鬆控制旋轉方向。
這些基礎打好之後,接下來就能進階挑戰,讓這些旋轉特效變成有趣的互動遊戲了!

準備好進一步深入探索了嗎?Let’s roll~ 🎮


今晚來點什麼呢?

我們從簡單的旋轉特效延伸到一個有趣的互動遊戲。
遊戲的核心目標是找出並點擊畫面中隨機出現的目標食物字符,並消除它們。
如果玩家成功找出所有目標字符,遊戲將顯示「恭喜過關」的彈窗,並提供「再玩一次」的選項。

這段代碼展示了從簡單的旋轉特效延伸到一個有趣的互動遊戲。
遊戲的核心目標是找出並點擊畫面中隨機出現的目標食物字符,並消除它們。如果玩家成功找出所有目標字符,遊戲將顯示「恭喜過關」的彈窗,並提供「再玩一次」的選項。

https://ithelp.ithome.com.tw/upload/images/20241002/20124462FvJm6xZw1k.png


互動遊戲的規格

https://ithelp.ithome.com.tw/upload/images/20241002/20124462oLEmbUgSyr.png

  1. 隨機食物字符生成:我們從 foodList 中隨機挑選一組字符,並將這些字符作為遊戲中的旋轉圖案。
  2. 目標字符選定:從隨機字符集中選定一個目標字符,玩家需要在所有旋轉的字符中找出並點擊這個目標字符。
  3. 旋轉效果:每個字符有一個旋轉效果,行列奇偶性決定了順時針還是逆時針旋轉,增加了視覺上的趣味性。
  4. 點擊檢測與回饋:點擊正確的字符後,該字符將被替換為 "⭕"。如果點擊錯誤,字符將暫時顯示為 "❌" 並在 1 秒後恢復原狀。
  5. 過關判定:當玩家消除所有目標字符後,彈出「恭喜過關」的通知,並提供重新開始遊戲的選項。

程式碼詳解:

1. 隨機字符生成與目標設定

const foodList = ['🍕', '🍔', '🍟', '🌭', '🍿', '🧂', '🥓', '🥚', '🥞', '🍳', '🍞', '🥐', '🥨', '🥯', '🥖', '🧀', '🥗', '🥙', '🥪', '🌮', '🌯', '🥫', '🍖', '🍗', '🥩', '🍠', '🥟', '🥠', '🥡', '🍱', '🍘', '🍙', '🍚', '🍛', '🍜', '🍣', '🍤', '🍥', '🥮', '🍢', '🥘', '🍲', '🍝', '🥣', '🥧', '🍦', '🍧', '🍨', '🍩', '🍪', '🎂', '🍰', '🧁', '🍫', '🍬', '🍭', '🍡', '🍮', '🍯', '🍼', '🥛', '☕', '🍵', '🍶', '🍾', '🍷', '🍸', '🍹', '🍺', '🍻', '🥂', '🥃', '🥤', '🥢', '🍴', '🥄', '🏺', '🥝', '🥥', '🍇', '🍈', '🍉', '🍊', '🍋', '🍌', '🍍', '🥭', '🍎', '🍏', '🍐', '🍑', '🍒', '🍓', '🍅', '🍆', '🌽', '🌶', '🍄', '🥑', '🥒', '🥬', '🥦', '🥔', '🥕', '🌰', '🥜', '💐', '🌸', '🌹', '🌺', '🌻', '🌼', '🌷', '🥀', '🌱', '🌲', '🌳', '🌴', '🌵', '🌾', '🌿', '🍀', '🍁', '🍂', '🍃']; 

const pixelNum = 8;
const getRandomElements = function <T>(arr: T[], min: number, max: number): T[] {
    const count = Math.floor(Math.random() * (max - min + 1)) + min;
    const shuffled = [...arr].sort(() => 0.5 - Math.random());
    return shuffled.slice(0, count);
}
const characterList = getRandomElements(foodList, 3, 16);
const targetCharacter = ref<string>(getRandomCharacter());
  • 我們從 foodList 中隨機挑選 3 到 16 個字符作為遊戲中的圖案,並從中選擇一個目標字符,讓玩家去找出來。

2. 點擊處理邏輯

const handleClick = (index: number) => {
    const currentCharacter = sushiGrid.value[index];
    if (currentCharacter.character === targetCharacter.value) {
        sushiGrid.value[index] = { ...currentCharacter, character: "⭕" };
        if (isLevelComplete.value) {
            modelTitle.value = "恭喜過關";
            modelContent.value = `您真厲害!已經找到所有 ${targetCharacter.value} 了!`;
            buttonLabel.value = "再玩一次";
            isModalVisible.value = true;
        }
        return;
    }
    sushiGrid.value[index] = { ...currentCharacter, character: "❌" };
    setTimeout(() => {
        sushiGrid.value[index] = currentCharacter;
    }, 1000);
};
  • 點擊一個字符後,檢查它是否與目標字符匹配。如果正確,該字符將變成 "⭕";如果錯誤,會暫時顯示 "❌" 並在 1 秒後恢復原狀。
  • 如果所有目標字符都被正確找到,會彈出「恭喜過關」的提示框。

https://ithelp.ithome.com.tw/upload/images/20241002/20124462vfcQsoFRZC.png

https://ithelp.ithome.com.tw/upload/images/20241002/20124462lBHpuKQGLz.png


3. 遊戲重置與過關邏輯

const startGame = () => {
    sushiGrid.value = Array.from({ length: pixelNum * pixelNum }, () => ({
        character: getRandomCharacter(),
        rotationDirection: Math.random() > 0.5 ? 'reverse' : ''
    }));
    targetCharacter.value = getRandomCharacter();
    buttonLabel.value = "開始遊戲";
    modelTitle.value = "遊戲規則";
    modelContent.value = `目標:找到並消除所有 ${targetCharacter.value}`;
    isModalVisible.value = true;
    isGameStart.value = false; 
};
  • 遊戲重新開始時,會重新生成一組隨機字符並重新選定一個目標字符,遊戲規則和彈窗也會重置。

<!-- Modal Component -->
<div v-if="isModalVisible" class="fixed inset-0 bg-black bg-opacity-60 flex justify-center items-center z-1 font-mono">
    <div class="w-1/2 h-1/3 bg-white p-8 rounded-lg max-w-md text-center shadow-lg animate-fade-in scale-95">
        <h2 class="text-4xl mb-6">{{ modelTitle }}</h2>
        <p class="text-2xl mb-10">{{ modelContent }}</p>
        <button @click="closeModal" class="bg-orange-500 text-white text-xl font-bold py-2 px-6 rounded-lg hover:bg-orange-700">
            {{ buttonLabel }}
        </button>
    </div>
</div>
  • 當玩家過關或開始遊戲時,這個彈窗會顯示遊戲狀態並提供按鈕控制。

互動設計核心亮點

  1. 隨機多樣:每次生成不同的食物字符和目標,保持遊戲新鮮感。
  2. 旋轉動效:順、逆時針旋轉增添視覺趣味,提升吸引力。
  3. 即時回饋:正確點擊顯示 "⭕",錯誤點擊顯示 "❌" 並恢復,強化互動性。
  • 小結

這樣一個充滿旋轉與趣味的小遊戲,不僅讓我們體驗到 Vue.js 動效的魔力,更感受到編程帶來的創造力與成就感!
其實,寫程式就像這些旋轉的壽司食物一樣,每一行代碼都是一次新的嘗試,每一次挑戰都是讓我們變得更好的機會。

希望你玩得開心,學得愉快,未來的每個小專案都能像這次一樣充滿樂趣!
Keep coding,讓你的世界旋轉起來吧!🎉

img


上一篇
Day17 Vue.js 動效分類實戰 (9) 萌兔吹泡泡特輯 - 顛覆等待的互動視覺體驗
下一篇
Day19 Vue.js 動效分類實戰 (11) 進階背景特輯 - 用 GSAP 打造你的專屬海洋世界
系列文
創意前端設計:用 Vue.js 打造 30 個互動實用功能30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言