[Vue] Composition API - 주사위 만들기 예제
leteu
·2022. 2. 22. 09:13
Composition API 사용법 공유해볼 겸 해서 주사위나 만들어보려고 한다.
.vue 파일 없이 js 파일로만 짜 보려고 한다.
h 함수를 통해 랜더를 할 생각인데 이건 react의 JSX createElement와 유사하게 사용할 수 있다.
jsx로 Vue 컴포넌트 만들다 보면 React도 쉽게 적응하지 않을까 싶다.
( 오류 있다고 나오면 좌측 하단 새로고침 눌러주면 잘 나온다 )
#1 뭔가 있어 보이는 초반 세팅
컴포넌츠 폴더에 Dice 폴더 만들어서 아래 파일들을 생성해준다.
- index.js
- Dice.js
- DiceFace.js
- dice.sass
컴포넌트에 입혀줄 스타일은 sass 파일로 만들어준 뒤 dice를 사용하는 컴포넌트에서 Import 해서 사용하거나 글로벌 sass에서 import 해주는 방식으로 사용할 것이다.
index.js에서는 Dice.js에서 짠 JSX 컴포넌트를 불러와서 사용할 것이다. 이렇게 해주면 Dice 폴더를 불러와서 뭔가 있어 보이고 깔끔하게 컴포넌트를 쓸 수 있다.
JSX로 코드를 짤 땐 컴포넌트를 적당히 나눠서 사용하면 코드의 재사용성을 늘릴 수 있다.
DiceFace.js에서 눈금을 만들고 Dice.js에서 주사위 컴포넌트로 만들어 주려고 한다.
Dice.js 와 DiceFace.js 는 defineComponent를 불러와 내보내 준다.
// Dice.js
import { defineComponent } from "vue";
import DiceFace from "./DiceFace";
export default defineComponent({
components: {
DiceFace
},
});
// DiceFace.js
import { defineComponent } from "vue";
export default defineComponent({
});
index.js는 내보내기 한 Dice.js를 불러와서 내보내 준다.
// index.js
import Dice from "./Dice";
export default Dice;
주사위를 확인할 App.vue에서는 Dice 컴포넌트와 dice.sass를 불러와 사용해준다.
// App.vue
<template>
<div class="dice-box">
<Dice />
</div>
</template>
<script>
import Dice from '/src/components/Dice';
export default {
name: "App",
components: {
Dice
},
};
</script>
<style lang="sass">
@import ./components/Dice/dice
.dice-box
width: 100%
height: 400px
display: flex
justify-content: space-around
align-items: center
</style>
#2 주사위 눈금 컴포넌트 제작
랜더링은 h 함수를 통해 할 수 있습니다.
faceValue를 props로 받아 눈금이 몇인지 받아줍시다.
Array.from 함수는 비어있는 배열을 만들어 줄 수 있습니다.
첫 번째 아규먼트로 { length: number } 두 번째 아규먼트로 배열에 값을 리턴해줄 수 있습니다.
// DiceFace.js
import { defineComponent, h } from "vue";
export default defineComponent({
props: {
faceValue: {
type: Number,
validator: val => val >= 1 && val <=6,
required: true
}
},
setup(props) {
return () => h('div',
{
class: `dice-face dice-face__${props.faceValue}`
},
Array.from(
{
length: props.faceValue
},
(_, index) => index+1
).map(dot => {
return h('div', { class: 'dice-face__dot' })
})
)
}
});
// Dice.js
import { defineComponent, h } from "vue";
import DiceFace from "./DiceFace";
export default defineComponent({
setup() {
return () => [
h(DiceFace, { faceValue: 1 }),
h(DiceFace, { faceValue: 2 }),
h(DiceFace, { faceValue: 3 }),
h(DiceFace, { faceValue: 4 }),
h(DiceFace, { faceValue: 5 }),
h(DiceFace, { faceValue: 6 }),
]
}
});
위 코드 작성 시 개발자 도구에서 element가 잘 표출되고 있는 걸 확인할 수 있다.
이제 이쁘게 꾸며보자
/* dice.sass */
*
box-sizing: border-box
$dice-size: 100px
$dot-size: 20px
.dice-face
width: $dice-size
height: $dice-size
background-color: #e7e7e7
object-fit: contain
border: 1px solid #333
display: flex
justify-content: space-between
&__dot
display: block
width: $dot-size
height: $dot-size
background-color: #676767
box-shadow: inset -0.15rem 0.15rem 0.25rem rgba(0, 0, 0, 0.5)
border-radius: 50%
&__1
align-items: center
justify-content: center
&__2
padding: 20px
> div
&:last-child
align-self: flex-end
&__3
padding: 15px
> div
&:nth-child(2)
align-self: center
&:last-child
align-self: flex-end
&__4
padding: 10px
display: grid
grid-template-columns: 40px 40px
align-items: center
justify-items: center
&__5
padding: 12px
display: grid
align-items: center
justify-items: center
>div
&:nth-child(1)
grid-column: 1
&:nth-child(2)
grid-column: 3
&:nth-child(3)
grid-column: 2
&:nth-child(4)
grid-column: 1
&:nth-child(5)
grid-column: 3
&__6
padding: 10px
display: grid
grid-template-columns: 40px 40px
align-items: center
justify-items: center
#3 주사위 컴포넌트 제작
// Dice.js
import { defineComponent, h } from "vue";
import DiceFace from "./DiceFace";
export default defineComponent({
props: {
value: {
type: Number,
validator: (val) => val >= 1 && val <= 6,
required: true
}
},
setup(props) {
return () =>
h(
"div",
{
class: `dice dice__show-${props.value}`
},
[
h(DiceFace, { faceValue: 1 }),
h(DiceFace, { faceValue: 2 }),
h(DiceFace, { faceValue: 3 }),
h(DiceFace, { faceValue: 4 }),
h(DiceFace, { faceValue: 5 }),
h(DiceFace, { faceValue: 6 })
]
);
}
});
// App.vue
<template>
<div class="dice-box">
<Dice :value="1" />
</div>
</template>
...
/* dice.sass */
.dice
width: $dice-size
height: $dice-size
position: relative
transform-style: preserve-3d
.dice-face
position: absolute
top: 0
left: 0
.dice-face
&__1
transform: rotateY(0deg) translateZ(50px)
&__2
transform: rotateY(90deg) translateZ(50px)
&__3
transform: rotateX(-90deg) translateZ(50px)
&__4
transform: rotateX(90deg) translateZ(50px)
&__5
transform: rotateY(-90deg) translateZ(50px)
&__6
transform: rotateY(180deg) translateZ(50px)
...
.dice-face에 앱솔루트를 주고 한대 모아준다.
.dice는 transform-style를 preserve-3d로 줘서 3d처럼 만들어준다.
rotate를 사용해 1번 눈금을 기준으로 정육면체를 만들어준다.
주사위는 완성됐다.
이제 잘 만들어졌는지 확인해보자.
#4 keyframes 사용해서 주사위 굴려보기
spin 키프레임을 만들어 준 뒤 .roll-dice 클래스에 애니메이션으로 추가해 준다.
/* dice.sass */
.roll-dice
animation: spin .5s linear infinite
transition: transform 0.8s
transform-origin: center
@keyframes spin
0%
transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg)
100%
transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg)
// Dice.js
.
.
.
props: {
.
.
.
roll: {
type: Boolean,
default: false
},
},
setup(props) {
return () => h(
"div",
{
class: `dice dice__show-${props.value} ${props.roll ? 'roll-dice' : ''}`
},
[
h(DiceFace, { faceValue: 1 }),
h(DiceFace, { faceValue: 2 }),
h(DiceFace, { faceValue: 3 }),
h(DiceFace, { faceValue: 4 }),
h(DiceFace, { faceValue: 5 }),
h(DiceFace, { faceValue: 6 })
]
);
}
.
.
.
// App.vue
<template>
<div class="dice-box">
<Dice :value="1" roll />
</div>
</template>
Dice 컴포넌트에 roll 프롭을 받아서 클래스를 조건 처리해준다.
'프로그래밍 > Vuejs' 카테고리의 다른 글
[vuejs] Component 기초 - template, render, setup, slot (0) | 2023.03.04 |
---|---|
[Vue 3] Props, Emit (0) | 2022.03.17 |
[Vue] Quasar Vue UI framwork 소개 (1) | 2022.03.02 |
[Vue 3] 라우트 변경, 새로고침, 창 닫기 감지 (0) | 2022.02.17 |
[Vuejs] 개발 환경 세팅 (0) | 2021.12.31 |