iT邦幫忙

2022 iThome 鐵人賽

DAY 4
1
Modern Web

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

D04 - 門面怎麼可以沒有背景

  • 分享至 

  • xImage
  •  

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

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

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

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

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

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

Yes


讓我們開啟 Web 專案,遊戲應該要有遊戲的感覺,第一步先來建立第一個背景吧!

預期建立一個有各種浮動多邊效果的背景。

Untitled

建立背景

預計有以下功能:

  • 自訂主色:內容自動依照主色變化
  • 初始數量:畫面出現時,內部初始方塊數量
  • 最大數量:色塊最大數量,超過此數量時,會暫停產生方塊
  • 產生間距:越短生成速度越快

首先建立 background-polygons-floating 組件

src\components\background-polygons-floating.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>

依據需求定義 Props

interface Props {
  mainColor?: string;
  /** 初始數量,畫面出現時,內部初始方塊數量 */
  initialQuantity?: number;
  /** 色塊最大數量,超過此數量時,會暫停產生方塊 */
  maxQuantity?: number;
  /** 產生間距,越短生成速度越快,單位 ms */
  generateInterval?: number;
}
const props = withDefaults(defineProps<Props>(), {
  mainColor: '#ffce5c',
  initialQuantity: 10,
  maxQuantity: 30,
  generateInterval: 500,
});

接著建立方塊容器,並建立背景樣式 backgroundStyle

<template>
  <div
    class="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, 10);

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

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

  const offsetColor = rgbToHex(hsvToRgb(hsvColor));

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

透過 Quasar 提供之 Color Utils,可以輕鬆調整顏色。

接著在 the-home 組件中引入 background-polygons-floating 組件,來實際看看效果。

src\views\the-home.vue

<template>
  <background-polygons-floating class="absolute inset-0" />
</template>

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

import BackgroundPolygonsFloating from '../components/background-polygons-floating.vue';
</script>

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

背景出現了!

Untitled

接著是最重要的方塊部份了,定義一個 polygon-base 組件,用來產生方塊。

建立多邊形組件

老樣子先定義方塊 Props

<script lang="ts">
export enum ShapeType {
  ROUND = 'round',
  TRIANGLE = 'triangle',
  SQUARE = 'square',
  PENTAGON = 'pentagon'
}

export enum FillType {
  SOLID = 'solid',
  FENCE = 'fence',
  SPOT = 'spot',
}
</script>

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

interface Props {
  size?: string;
  color?: string;
  rotate?: string;
  opacity?: string | number;
  shape?: `${ShapeType}`,
  fill?: `${FillType}`
}
const props = withDefaults(defineProps<Props>(), {
  size: '10rem',
  color: 'white',
  rotate: '0deg',
  opacity: '0.4',
  shape: 'round',
  fill: 'fence',
});
</script>

在 setup script 中 export 會發生異常,所以在此建立另一個一般的 script,用來 export 型別資料。參考資料

接著提供 div,準備切割成多邊形外觀

<template>
  <div class="frame">
    <div
      class="polygon"
    />
  </div>
</template>

切割形狀部分,使用 CSS 之 clipPath 實現,所以根據定義的 ShapeType,列舉一下對應的種類。

const clipPathMap = {
  [ShapeType.ROUND]: `circle(50% at 50% 50%)`,
  [ShapeType.SQUARE]: `polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)`,
  [ShapeType.TRIANGLE]: `polygon(50% 0%, 0% 100%, 100% 100%)`,
  [ShapeType.PENTAGON]: `polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)`,
}
const clipPath = computed(() => clipPathMap?.[props.shape] ?? clipPathMap[ShapeType.ROUND]);

接著是填滿樣式(fill)的部分,實心很容易,但是網格狀或斑點狀的填滿效果要怎麼實現呢?有一個辦法,一樣透過萬能的 CSS 實現,也就是 maskImage!(CSS 萬歲 ♪(´▽`))

所以我們從百寶袋(大家可以直接前往 GitLab 專案中下載)變出 mask 要用的 svg 檔案:

  • line.svg
  • round.svg

放置於 public\images 目錄中,並列舉 fill 內容。

const fillMap = {
  [FillType.SOLID]: ``,
  [FillType.FENCE]: `url(/images/line.svg)`,
  [FillType.SPOT]: `url(/images/round.svg)`,
}
const fill = computed(() => fillMap?.[props.fill] ?? fillMap[FillType.SPOT]);

最後把剛剛定義的所有內容集結成 style。

const style = computed(() => ({
  width: props.size,
  height: props.size,
  backgroundColor: props.color,
  maskImage: fill.value,
  maskSize: `6%`,
  opacity: props.opacity,
  clipPath: clipPath.value,
  transform: `rotate(${props.rotate})`,
}));

並加入 div 中。

<template>
  <div class="frame">
    <div
      class="polygon"
      :style="style"
    />
  </div>
</template>

最後我們在 background-polygons-floating 中引入 polygon-base 看看效果吧!

src\components\background-polygons-floating.vue

<template>
  <div
    class="overflow-hidden"
    :style="backgroundStyle"
  >
    <polygon-base
      shape="round"
      fill="solid"
    />
    <polygon-base
      shape="round"
      fill="spot"
    />
    <polygon-base
      shape="triangle"
      fill="fence"
    />
    <polygon-base
      shape="square"
      fill="spot"
    />
    <polygon-base
      shape="pentagon"
      fill="solid"
    />
  </div>
</template>

...

可以看到多樣的多邊形們出現了!ヽ(✿゚▽゚)ノ

Untitled

總結

  • 建立第一個背景
  • 建立多邊形組件

接下來準備讓多邊形們飄起來吧!

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

GitLab - D04


上一篇
D03 - 建立專案:完成 Vue、NestJS、TypeScript 開發環境
下一篇
D05 - 飄吧!多邊形!
系列文
派對動物嗨起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言