Pixi.js 간단한 예제 (with Vue) - (2)

|

화면에 피카츄 그리기

<template>
  <div id="container">
    <canvas id="pixi-canvas"></canvas>
  </div>
</template>

<script>
import * as PIXI from "pixi.js";
import { onMounted } from "@vue/runtime-core";

export default {
  setup() {
    onMounted(() => {
      const app = createPixiApp();

      createObject(app);
    });

    const createPixiApp = () => {
      var canvas = document.getElementById("pixi-canvas");

      const app = new PIXI.Application({
        width: 1000,
        height: 1000,
        antialias: true,
        backgroundAlpha: true,
        view: canvas,
      });

      return app;
    };

    const createObject = (app) => {
      const pikachu = PIXI.Sprite.from(require("@/assets/pikachu.png"));
      pikachu.x = 50;
      pikachu.y = 50;
      pikachu.width = 832;
      pikachu.height = 846;

      app.stage.addChild(pikachu);
    };
  },
};
</script>

<style>
#container {
  display: block;
  background: white;
  padding: 20px;
}

#pixi-canvas {
  width: 800px;
  height: 800px;
}
</style>

사용한 리소스

image

실행 화면

image

Pixi.js 간단한 예제 (with Vue) - (1)

|

Vue.js에서 Pixi.js를 사용하는 간단한 예제

Vue.js에서 Pixi.js를 사용하는 방법은 크게 다음과 같은 3가지 방식이 있습니다.

  • HTML canvas 요소를 정의하고 그 위에 Pixi를 구동하는 방법
  • HTML body 요소에 Pixi를 직접 삽입하는 방법
  • HTML div 요소에 Pixi를 직접 삽입하는 방법

HTML canvas 위에서 Pixi 구동하는 방법

미리 canvas를 html로 정의하고 그 위에 Pixi를 구동하는 방법입니다.

<template>
  <div id="container">
    <canvas id="pixi-canvas"></canvas>
  </div>
</template>

<script>
import * as PIXI from "pixi.js";
import { onMounted } from "@vue/runtime-core";

export default {
  setup() {
    onMounted(() => {
      const app = createPixiApp();

      draw(app);
    });

    const createPixiApp = () => {
      var canvas = document.getElementById("pixi-canvas");
      console.log(`window size(${window.innerWidth}, ${window.innerHeight})`);
      console.log(`canvas size(${canvas.width}, ${canvas.height})`);

      const app = new PIXI.Application({
        width: window.innerWidth,
        height: window.innerHeight,
        antialias: true,
        backgroundAlpha: true,
        view: canvas,
      });

      return app;
    };

    const draw = (app) => {
      const graphics = new PIXI.Graphics();
      graphics.lineStyle(8, 0x008080);
      graphics.moveTo(0, 250);
      graphics.lineTo(800, 500);
      app.stage.addChild(graphics);

      const sprite = PIXI.Sprite.from(require("@/assets/sample.png"));
      app.stage.addChild(sprite);

      let elapsed = 0.0;
      app.ticker.add((delta) => {
        elapsed += delta;
        sprite.x = 100.0 + Math.cos(elapsed / 50.0) * 100.0;
      });
    };

    return {
      draw,
    };
  },
};
</script>

<style scoped>
#container {
  display: block;
  background: white;
  padding: 20px;
}

#pixi-canvas {
  width: 80vw;
  height: 80vh;
}
</style>

실행 화면

image


Pixi 내에서 Canvas 정의해서 HTML 문서의 Body에 추가하는 방법

<template>
  <div id="pixi-container"></div>
</template>

<script>
import * as PIXI from "pixi.js";
import { onMounted } from "@vue/runtime-core";

export default {
  setup() {
    onMounted(() => {
      const app = createPixiApp();
      document.body.appendChild(app.view);

      draw(app);
    });

    const createPixiApp = () => {
      const app = new PIXI.Application({
        width: 800,
        height: 600,
        backgroundColor: "0x000000",
      });

      return app;
    };

    const draw = (app) => {
      const graphics = new PIXI.Graphics();
      graphics.lineStyle(8, 0x008080);
      graphics.moveTo(0, 250);
      graphics.lineTo(800, 500);
      app.stage.addChild(graphics);

      const sprite = PIXI.Sprite.from(require("@/assets/sample.png"));
      app.stage.addChild(sprite);

      let elapsed = 0.0;
      app.ticker.add((delta) => {
        console.log(`delta: ${delta}`);
        elapsed += delta;
        sprite.x = 100.0 + Math.cos(elapsed / 50.0) * 100.0;
      });
    };

    return {
      draw,
    };
  },
};
</script>

Pixi 내에서 Canvas 정의해서 HTML 문서의 div 요소에 추가하는 방법

<template>
  <div id="pixi-container"></div>
</template>

<script>
import * as PIXI from "pixi.js";
import { onMounted } from "@vue/runtime-core";

export default {
  setup() {
    onMounted(() => {
      const app = createPixiApp();
      var container = document.getElementById("pixi-container");
      container.appendChild(app.view);

      draw(app);
    });

    const createPixiApp = () => {
      const app = new PIXI.Application({
        width: 800,
        height: 600,
        backgroundColor: "0x000000",
      });

      return app;
    };

    const draw = (app) => {
      const graphics = new PIXI.Graphics();
      graphics.lineStyle(8, 0x008080);
      graphics.moveTo(0, 250);
      graphics.lineTo(800, 500);
      app.stage.addChild(graphics);

      const sprite = PIXI.Sprite.from(require("@/assets/sample.png"));
      app.stage.addChild(sprite);

      let elapsed = 0.0;
      app.ticker.add((delta) => {
        console.log(`delta: ${delta}`);
        elapsed += delta;
        sprite.x = 100.0 + Math.cos(elapsed / 50.0) * 100.0;
      });
    };

    return {
      draw,
    };
  },
};
</script>

CSS - Flexible Box

|

Flexible Box

Flexible Box는 CSS3에 처음 소개된 기능으로 반응형 웹을 아주 쉽게 만들 수 있는 기술입니다. 기존의 속성들로는 박스의 배치 순서를 변경하거나 자유롭게 바꾸는 것이 불가능했지만, Flexible Box를 이용하면 아주 쉽고 자유롭게 다양한 화면을 구성할 수 있습니다.

기존에는 float와 같은 요소들이 있었으나 화면의 요소들을 ‘배치’하는 기술이 아니었습니다.

축의 방향

Flexible Box는 주축과 교차축이 있습니다. 축의 방향은 가장 중요한 요소로 방향이 가로인경우는 왼쪽에서 오른쪽으로, 방향이 세로인 경우는 위에서 아래쪽으로 박스들이 배치됩니다.


Flexible Box 작동을 위한 기본 설정

아주 간단한 Flexible Box의 기본 설정은 다음과 같습니다.

#wrap {
  display: -webkit-flex;
  display: flex;
  width: 90%;
  height: 500px;
  margin: 0 auto;
  background: orange;
}

flexible-direction

flex-direction 속성을 이용해서 자식 상자들의 방향을 정할 수 있습니다.

  • row : 박스를 왼쪽에서 오른쪽으로 배치
  • row-reverse
  • column : 박스를 위에서 아래로 배치
  • column-reverse
#wrap {
  display: -webkit-flex;
  display: flex;
  flex-direction: row;
  ...
}

예제

<template>
  <div id="wrap">
    <div></div>
    <div></div>
    <div></div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
#wrap {
  display: -webkit-flex;
  display: flex;
  flex-direction: row;
  width: 90%;
  height: 500px;
  margin: 0 auto;
  background: white;
  border: 2px solid black;
}

#wrap div {
  width: 33.333%;
}

#wrap div:first-child {
  background: orange;
}

#wrap div:nth-child(2) {
  background: violet;
}

#wrap div:nth-child(3) {
  background: teal;
}
</style>

Image

아이템을 여러 줄로 배치하기

기본적으로 Flexible Box 내의 자식 박스들은 한 줄로만 배치됩니다. 만약 여러 줄로 배치하고 싶은 경우에는 flex-wrap 속성을 넣어줘야 합니다. 사용해야 합니다.

  • nowrap(기본값) : 박스를 한 줄로 배치
  • wrap : 박스를 여러 줄로 배치
  • wrap-reverse

flex-flow

flex-flow를 이용해서 flex-directionflex-wrap을 한 번에 설정할 수도 있습니다.

#wrap {
  display: -webkit-flex;
  display: flex;
  flex-flow: row wrap;
  ...
}

justify-content

주축 방향으로 박스를 정렬하는 명령어입니다.

  • flex-start(기본값)
  • flex-end
  • center
  • space-between
  • space-around

align-items

교차축 방향으로 박스를 배치할 경우에는 align-items 속성을 이용할 수 있습니다.

  • stretch(기본값) : 박스를 확장해서 배치
  • flex-start
  • flex-end
  • center
  • baseline : 시작점에 배치되는 자식 박스의 글자 베이스 라인에 맞춰 배치

예제

<template>
  <div id="wrap">
    <div></div>
    <div></div>
    <div></div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
#wrap {
  display: -webkit-flex;
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  width: 90%;
  height: 500px;
  margin: 0 auto;
  background: white;
  border: 2px solid black;
}

#wrap div {
  width: 33.33%;
  height: 200px;
}

#wrap div:first-child {
  background: orange;
}

#wrap div:nth-child(2) {
  background: violet;
}

#wrap div:nth-child(3) {
  background: teal;
}
</style>

Image

Vue.js 3.0 기본 레이아웃 - (4) Navigation Drawer 슬라이딩 에니메이션

|

Navigation Drawer Slide 애니메이션

기존 코드에서 아래 부분에 애니메이션이 추가되었습니다.

<script setup>
let isDrawerOpened = false;

const toggleDrawer = () => {
  isDrawerOpened = !isDrawerOpened;
  if (isDrawerOpened) {
    document.getElementById("drawer").style.width = "200px";
  } else {
    document.getElementById("drawer").style.width = "0px";
  }
};
</script>

#drawer {
  display: flex;
  flex-direction: column;
  width: 0px;
  height: 100vh;
  background: darkcyan;
  color: white;
  transition: 0.2s;
  overflow-x: hidden;
}

전체 코드

<template>
  <div id="wrapper">
    <!-- Navigation Drawer -->
    <div id="drawer">
      <div id="drawer-header">SnowDeer Drawer</div>
      <div id="drawer-content">
        <div id="drawer-menu-item">
          <i class="bi bi-house"></i>
          <span>Home</span>
        </div>
        <div id="drawer-menu-item">
          <i class="bi bi-gift"></i>
          <span>Hello</span>
        </div>
        <div id="drawer-menu-item">
          <i class="bi bi-info-square"></i>
          <span>About</span>
        </div>
      </div>
    </div>
    <!-- Main -->
    <div id="main">
      <!-- AppBar -->
      <div id="appbar">
        <div id="hamburg-menu" class="h-center v-center" @click="toggleDrawer">
          <i class="bi bi-list"></i>
        </div>
        <div id="title" class="v-center">AppBar Title</div>
        <div id="action-items">
          <i class="action-item bi bi-heart"></i>
          <i class="action-item bi bi-three-dots-vertical"></i>
        </div>
      </div>
      <!-- Content -->
      <div id="content"></div>
    </div>
  </div>
</template>

<script setup>
let isDrawerOpened = false;

const toggleDrawer = () => {
  isDrawerOpened = !isDrawerOpened;
  if (isDrawerOpened) {
    document.getElementById("drawer").style.width = "200px";
  } else {
    document.getElementById("drawer").style.width = "0px";
  }
};
</script>

<style>
.h-center {
  display: flex;
  flex-direction: row;
  justify-content: center;
}

.v-center {
  display: flex;
  flex-direction: row;
  align-items: center;
}

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#wrapper {
  display: -webkit-flex;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
  width: 100vw;
  height: 100vh;
  margin: 0 auto;
}

#main {
  flex: 1;
  flex-flow: column wrap;
  justify-content: flex-start;
  background: #bbe4e9;
  transition: margin-left 0.5s;
}

#appbar {
  display: flex;
  flex-direction: row;
  height: 48px;
  background: #edb1f1;
}

#hamburg-menu {
  width: 48px;
  height: 48px;
  background: #d59bf6;
  cursor: pointer;
}

#title {
  flex: 1;
  height: 48px;
}

#action-items {
  display: flex;
  flex-direction: row;
  height: 48px;
  background: #d59bf6;
}

.action-item {
  width: 48px;
  height: 48px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

#drawer {
  display: flex;
  flex-direction: column;
  width: 0px;
  height: 100vh;
  background: darkcyan;
  color: white;
  transition: 0.2s;
  overflow-x: hidden;
}

#drawer-header {
  width: 100%;
  height: 100px;
  padding: 20px;
  background: teal;
}

#drawer-content {
  flex: 1;
}

#drawer-menu-item {
  padding: 16px;
  cursor: pointer;
}

#drawer-menu-item:hover {
  color: black;
  background: turquoise;
}
</style>

Vue.js 3.0 기본 레이아웃 - (3) Navigation Drawer 레이아웃

|

Navigation Drawer

<template>
  <div id="wrapper">
    <div id="drawer">
      <div id="drawer-header">SnowDeer Drawer</div>
      <div id="drawer-content">
        <div id="drawer-menu-item">
          <i class="bi bi-house"></i>
          <span>Home</span>
        </div>
        <div id="drawer-menu-item">
          <i class="bi bi-gift"></i>
          <span>Hello</span>
        </div>
        <div id="drawer-menu-item">
          <i class="bi bi-info-square"></i>
          <span>About</span>
        </div>
      </div>
    </div>
    <div id="main">
      <div id="appbar">
        <div id="hamburg-menu" class="h-center v-center">
          <i class="bi bi-list"></i>
        </div>
        <div id="title" class="v-center">AppBar Title</div>
        <div id="action-items">
          <i class="action-item bi bi-heart"></i>
          <i class="action-item bi bi-three-dots-vertical"></i>
        </div>
      </div>
      <div id="content"></div>
    </div>
  </div>
</template>

<style>
.h-center {
  display: flex;
  flex-direction: row;
  justify-content: center;
}

.v-center {
  display: flex;
  flex-direction: row;
  align-items: center;
}

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#wrapper {
  display: -webkit-flex;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
  width: 100vw;
  height: 100vh;
  margin: 0 auto;
}

#appbar {
  display: flex;
  flex-direction: row;
  height: 48px;
  background: #edb1f1;
}

#hamburg-menu {
  width: 48px;
  height: 48px;
  background: #d59bf6;
  cursor: pointer;
}

#title {
  flex: 1;
  height: 48px;
}

#action-items {
  display: flex;
  flex-direction: row;
  height: 48px;
  background: #d59bf6;
}

.action-item {
  width: 48px;
  height: 48px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

#drawer {
  display: flex;
  flex-direction: column;
  width: 200px;
  height: 100vh;
  background: darkcyan;
  color: white;
}

#drawer-header {
  width: 200px;
  height: 100px;
  padding: 20px;
  background: teal;
}

#drawer-content {
  flex: 1;
}

#drawer-menu-item {
  padding: 16px;
  cursor: pointer;
}

#drawer-menu-item:hover {
  color: black;
  background: turquoise;
}

#main {
  flex: 1;
  flex-flow: column wrap;
  justify-content: flex-start;
  background: #bbe4e9;
}
</style>

image