iT邦幫忙

2022 iThome 鐵人賽

DAY 8
4
Modern Web

派對動物嗨起來!系列 第 8

D08 - 載入就應該要有載入的樣子

  • 分享至 

  • xImage
  •  

本系列文已改編成書「甚麼?網頁也可以做派對遊戲?使用 Vue 和 babylon.js 打造 3D 派對遊戲吧!」

書中不只重構了程式架構、改善了介面設計,還新增了 2 個新遊戲呦!ˋ( ° ▽、° )

新遊戲分別使用了陀螺儀與震動回饋,趕快買書來研究研究吧!ლ(╹∀╹ლ)

在此感謝深智數位的協助,歡迎大家前往購書,鱈魚感謝大家 (。・∀・)。

助教:「所以到底差在哪啊?沒圖沒真相,被你坑了都不知道。(´。_。`)」

鱈魚:「你對我是不是有甚麼很深的偏見啊 (っ °Д °;)っ,來人啊,上連結!」

Yes


過場用的進入漸出組件完成後,讓我們把原本的全白畫面換成 QQ 的「載入畫面」的樣子吧!

新增背景用組件 background-polygons-loading

src\components\background-polygons-loading.vue

<template>
</template>

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

interface Props {
  label?: string;
}
const props = withDefaults(defineProps<Props>(), {
  label: '',
});
</script>

<style scoped lang="sass">
</style>

首先老樣子來定義一下輸入參數。

interface Props {
  mainColor?: string;
}
const props = withDefaults(defineProps<Props>(), {
  mainColor: '#c8e6b1',
});

不過除了設定顏色,我還真的暫時想不到有甚麼參數 XD。

再來新增最外層容器,並將自動計算出來的 style 綁上去,產生背景顏色。

<template>
  <div
    class="flex flex-center overflow-hidden"
    :style="backgroundStyle"
  >
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, ref } from 'vue';
import { colors } from 'quasar';
const { lighten, textToRgb, rgbToHsv, hsvToRgb, rgbToHex } = colors;

...

const backgroundStyle = computed(() => {
  // 變亮
  const lightenColor = lighten(props.mainColor, 20);

  // 變暗並偏移色相
  const darkColor = lighten(props.mainColor, -14);

  const hsvColor = rgbToHsv(textToRgb(darkColor));
  hsvColor.h -= 15;

  const offsetColor = rgbToHex(hsvToRgb(hsvColor));

  return {
    background: `linear-gradient(-30deg, ${offsetColor}, ${props.mainColor}, ${lightenColor}, ${props.mainColor}, ${offsetColor})`
  }
});
</script>

接著暫時在 App.vue 中引入組件,方便讓我們看看目前的模樣,同時刪除 loading.show() 的程式,以免產生干擾。

src\App.vue

<template>
  <router-view />
  <loading-overlay />

  <background-polygons-loading />
</template>

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

import LoadingOverlay from './components/loading-overlay.vue';
import BackgroundPolygonsLoading from './components/background-polygons-loading.vue';

document.title += ` v${import.meta.env.PACKAGE_VERSION}`;
</script>

<style lang="sass">
...
</style>

目前看起來應該長這樣。

Untitled

嗯…一點都不像讀取畫面呢!( ͡° ͜ʖ ͡°)

先別急著走啊,讓我們加點反覆動作的元素,應該就會像讀取畫面了!

讓我們引入多邊形來用用吧,印入多邊形,並先定義一下多邊形的樣式。

import PolygonBase, { ShapeType } from './polygon-base.vue';

const polygons = ref<{
  id: string;
  shape: `${ShapeType}`;
  color: string;
}[]>([
  {
    id: '1',
    shape: 'square',
    color: `#FA9500`,
  },
  {
    id: '2',
    shape: 'round',
    color: `#EB6424`,
  },
  {
    id: '3',
    shape: 'triangle',
    color: `#F07167`,
  },
]);

接著在 template 中,用 v-for 產生多邊形。

<template>
  <div
    class="flex flex-center overflow-hidden"
    :style="backgroundStyle"
  >
    <div class="flex gap-16">
      <div
        v-for="(poly) in polygons"
        :key="poly.id"
      >
        <polygon-base
          size="7.4rem"
          :shape="poly.shape"
          fill="solid"
          :color="poly.color"
          opacity="0.1"
        />
      </div>
    </div>
  </div>
</template>

多邊形外層還包一個 div,是為了提升加入 CSS 動畫的彈性。

現在多邊形出現了!(゜▽゜*)♪

Untitled

助教:「怎麼這種顏色,臭魚美感炸裂?(ಠಿ_ಠ)」。

鱈魚:「你才美感炸裂,你全家都美感炸裂 (っ °Д °;)っ」

這是因為我打算 mix-blend-mode 屬性讓多邊形與背景色混合,現在來設計一下 CSS。

<template>
  <div
    ...
  >
    <div class="flex gap-16">
      <div
        v-for="(poly) in polygons"
        :key="poly.id"
        class="box"
      >
       ...
      </div>
    </div>
  </div>
</template>

<style scoped lang="sass">
.box
  mix-blend-mode: difference
</style>

現在看起來好多了吧 …(´,,•ω•,,)

Untitled

現在讓這三個多邊形動起來吧,設計邏輯是:

  • 多邊形套上果凍動畫
  • 多邊形容器套上跳動動畫

這兩種動畫合併後就可以產生 Q 彈的果凍跳躍的效果了!( •̀ ω •́ )y

<template>
  <div
    ...
  >
    <div class="flex gap-16">
      <div
        ...
        class="box"
      >
        <polygon-base
          class="jelly-bounce"
          ...
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
...
</script>

<style scoped lang="sass">
.box
  animation: jump 1.4s infinite ease-in-out
  mix-blend-mode: difference

.jelly-bounce 
  animation: jelly-bounce 1.4s infinite ease-in-out
  transform-origin: 50% 100%

@keyframes jump
  0%
    transform: translateY(-30%)
  45%
    transform: translateY(0%)
  100%
    transform: translateY(-30%)

@keyframes jelly-bounce
  0%
    transform: scale( 1 )
  30%
    transform: scale( 1 )
  50%
    transform: scale( 1.2, 0.8 )
  70%
    transform: scale( 0.85, 1.15 )
  80%
    transform: scale( 1.05, 0.95 )
  90%
    transform: scale( 0.98, 1.02 )
  100%
    transform: scale( 1 )
</style>

ezgif-3-fa5872a83e.gif

看起來真不錯,可惡想揉。ლ(´∀`ლ)

最後我們讓三個多邊形的動畫交錯,這樣看起來比較有趣。

直接用 animation-delay 簡單解決!

<template>
  <div
    ...
  >
    <div class="flex gap-16">
      <div
        ...
        class="box"
        :style="`animation-delay: ${i * 0.1}s`"
      >
        <polygon-base
          class="jelly-bounce"
          ...
          :style="`animation-delay: ${i * 0.1}s`"
        />
      </div>
    </div>
  </div>
</template>

...

瞬間完成!

ezgif-3-e650cf7e45.gif

成功產生三個很沒默契的多邊形!ᕕ( ゚ ∀。)ᕗ

最後我們把這個背景加到載入畫面使用吧!

首先刪除 App.vue 中的 background-polygons-loading

src\App.vue

<template>
  <router-view />
  <loading-overlay />
</template>

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

import LoadingOverlay from './components/loading-overlay.vue';

document.title += ` v${import.meta.env.PACKAGE_VERSION}`;
</script>
...

在 loading-overlay 引入 background-polygons-loading,替換原先純白的 div。

src\components\loading-overlay.vue

<template>
  <transition-mask
    ...
  >
    <background-polygons-loading class="absolute inset-0" />
  </transition-mask>
</template>

<script setup lang="ts">
...
import TransitionMask from './transition-mask.vue';
import BackgroundPolygonsLoading from './background-polygons-loading.vue';

...
</script>

讀取背景加入完成,接下來就是來實際試試看效果了。

在 the-home 新增 startParty(),實際使用 use-loading 功能並跳轉 route 看看。

src\views\the-home.vue

<template>
  ...

  <div class="absolute inset-0 flex flex-col flex-center gap-20">
    <btn-base
      label="建立派對"
      ...
      @click="startParty"
    >
      ...
    </btn-base>

    ...
  </div>
</template>

<script setup lang="ts">
...
import { RouteName } from '../router/router';
...
import { useLoading } from '../composables/use-loading';
import { useRouter } from 'vue-router';

const loading = useLoading();
const router = useRouter();

async function startParty() {
  await loading.show();
  router.push({
    name: RouteName.GAME_CONSOLE
  });
}
</script>

...

最後在 game-console 中呼叫 loading.show()。

src\views\game-console.vue

<template>
...
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useLoading } from '../composables/use-loading';

const loading = useLoading();
loading.hide();
</script>
...

來看看效果。

ezgif-3-a64baea804.gif

完成 ✧*。٩(ˊᗜˋ*)و✧*。

接下來讓我們啟動伺服器,準備開始連線!

總結

  • 完成 background-polygons-loading
  • 轉換 route 使用 loading 效果過場

以上程式碼已同步至 GitLab,大家可以前往下載:

GitLab - D08


上一篇
D07 - 開趴前先 loading 一下:使用 Pinia 與 Composition API
下一篇
D09 - NestJS 是啥?好吃嗎?
系列文
派對動物嗨起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言