ãã®èšäºã¯Vue Advent Calendar 2022ã®20æ¥ç®ïŒå€ªå¹³æŽæšæºæïŒã§ã
ãåãã®éããVueã§ã¯ã³ã³ããŒãã³ãã䜿ã£ãŠç°¡åã«èŠçŽ ã®è¡šç€º/é衚瀺ãã©ã³ãžã·ã§ã³ãæžããŸãã
âãããªãã€ã§ããã
<script setup lang="ts">
import { ref } from "vue";
const visible = ref(true);
</script>
<template>
<label>
<input type="checkbox" v-model="visible" />ãã¿ã³ã衚瀺
</label>
<Transition appear>
<button v-if="visible">ãã¿ã³ã ã</button>
</Transition>
</template>
<style lang="scss" scoped>
.v-enter-active,
.v-leave-active {
transition: opacity 2s;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>
倧äœVueã®å
¥éèšäºã ãšããããªæãã®äŸã§Transition
ç°¡åïŒãããïŒã§çµãã£ã¡ããã®ã ãã©ãã¡ãã£ãšæ·±ã¿ã«ããããšæãåºããªãã®ãTransition
ã§ããç¹ã«ãé衚瀺åŽ(leave)ã®ã¢ãã¡ãŒã·ã§ã³ã«ã¯æ²Œãå€ãã§ãã
leaveã¢ãã¡ãŒã·ã§ã³ã£ãŠãããããšã¯æ¶ããã ããªã®ã§è»œèŠãããã¡ãšãããã埮åŠã«ã¡ãããšåããŠãªãæ°ãããŠãèŠãŠèŠã¬ãµããããã¡ãªéšåã§ãããã
ãã®èšäºã§ã¯Vueã®Transition
ãã¡ãã£ãšã ãæ·±æãããŠãèŠãŠèŠã¬ãµããããŠãã埮åŠãªéšåãã¯ãªã¢ã«ããŠãæ°æã¡ããðæ°å¹Žãè¿ããã...ãããªäŒç»ã§ããæãåœãããã·ããæ¹ã¯ãã²ãä»ãåããã ãããŸãã
ãããã1ïŒ ã³ã³ããŒãã³ãã«<Transition>
ãæžãããæ¶ããæã ãã¢ãã¡ãŒã·ã§ã³ããªã
倧äœ<Transition>
ã䜿ã£ãŠæåã«ãããã®ããèªäœã®ã³ã³ããŒãã³ãã«ããæãã«è¡šç€º/é衚瀺ãã©ã³ãžã·ã§ã³ãã€ããããšæã£ãæã§ãïŒåœç€Ÿæ¯ïŒã
ãšããããäœãèããã«è¡šç€º/é衚瀺ã®ã¿ã€ãã³ã°ã§ããæãã«ãã§ãŒãã€ã³/ãã§ãŒãã¢ãŠãããŠããããã¿ã³ã®ã³ã³ããŒãã³ããäœããŸãããã
<script setup lang="ts"></script>
<template>
<Transition appear>
<div class="TamaSan"></div>
</Transition>
</template>
<style lang="scss" scoped>
.TamaSan {
position: relative;
width: 100px;
height: 150px;
background-image: url("@/assets/tama.svg");
background-size: contain;
background-position: center bottom;
background-repeat: no-repeat;
animation: constant-rotate 4s linear infinite;
}
/* å®åžžã¢ãã¡ãŒã·ã§ã³ïŒåžžæåè»¢ïŒ */
@keyframes constant-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* enter/leaveã¢ãã¡ãŒã·ã§ã³ */
.v-enter-active,
.v-leave-active {
transition: opacity 2s, rotate 2s, scale 2s;
}
.v-enter-from {
rotate: -100deg;
scale: 0.5;
opacity: 0;
}
.v-leave-to {
rotate: 300deg;
scale: 0.5;
opacity: 0;
}
</style>
ã§ãããã芪ã³ã³ããŒãã³ããã衚瀺/é衚瀺ããŸãã
<script setup lang="ts">
import { ref } from "vue";
import TamaSan from "./demo0x/TamaSan.vue";
const visible = ref(true);
</script>
<template>
<label>
<input type="checkbox" v-model="visible" />
ããŸããã³ã³ããŒãã³ãã衚瀺(v-if)
</label>
<div class="tama">
<TamaSan v-if="visible" />
</div>
</template>
ããã§åããããªæ°ãããããªãã§ããããšãããã©ã£ããããã®ãã©ã³ãžã·ã§ã³ã¯ååããåããŸãããè¡šç€ºã®æ¹ã¯ããããªãªã®ã§ãããéè¡šç€ºã®æ¹ããã©ã³ãžã·ã§ã³ããã«äžç¬ã§æ¶ããŸãã
POINTïŒãã©ã³ãžã·ã§ã³ã¯<Transition>
ãçããŠããªããšåçãããªã
<Transition>
ã®äžèº«ããã©ã³ãžã·ã§ã³ããããã®çµ¶å¯Ÿæ¡ä»¶ãã<Transition>
èªèº«ãçåããŠããããšãã§ãããã©ã³ãžã·ã§ã³äžã§ã<Transition>
èªäœãæ¶ãããšäžèº«ãåœç¶å³åº§ã«æ¶æ»
ããŸãã
ããã£ãŠããŸãã°åœããåãªã®ã§ãããçµæ§èŠèœãšããã¡ãªã®ã§ãäžåºŠãã¡ããšå£°ã«åºããŠçè§£ããŠããããååã§ã
äžçªç°¡åãªè§£æ±ºæ³ã¯v-if
ãv-show
ã«å€ããŠããŸãããšã§ãã
v-show
ã§é衚瀺ã«ãªã£ãŠãã³ã³ããŒãã³ãèªäœã¯ã¢ã³ããŠã³ããããã«çããŠããã®ã§ãã<Transition>
èªèº«ãååšããŠããããšãã®æ¡ä»¶ãæºãããŸã
<script setup lang="ts">
import { ref } from "vue";
import TamaSan from "./demo0x/TamaSan.vue";
const visible = ref(true);
</script>
<template>
<label>
<input type="checkbox" v-model="visible" />
ããŸããã³ã³ããŒãã³ãã衚瀺ïŒv-showïŒ
</label>
<div class="tama">
<TamaSan v-show="visible" />
</div>
</template>
v-show
ã ãšäžèŠãªã³ã³ããŒãã³ãã®äžèº«ãæ®ã£ãŠããŸã£ãŠå«ãªå Žåã¯ãã³ã³ããŒãã³ãèªäœãã¢ã³ããŠã³ãããã®ã§ã¯ãªããã³ã³ããŒãã³ãã«è¡šç€ºãå¶åŸ¡ããprops
ãçãããã³ã³ããŒãã³ãã®äžã§v-if
ã䜿ãã®ãããã§ãã
<script setup lang="ts">
withDefaults(
defineProps<{
/** 衚瀺ãããïŒé衚瀺ã®å Žåã³ã³ããŒãã³ãã®äžèº«ã¯ã¢ã³ããŠã³ããããŸã */
visible?: boolean;
}>(),
{
visible: true,
}
);
</script>
<template>
<Transition appear>
<div class="TamaSan" v-if="visible"></div>
</Transition>
</template>
<style scoped>/* ç¥*/</style>
ãããã2ïŒleaveãã©ã³ãžã·ã§ã³äžã¯äžèº«ãæŽæ°ãããªã
ãããçµæ§ãªæ²Œãªã®ã§ãããVueã®<Transition>
ã¯è¡šç€º(enter)æã¯ãã©ã³ãžã·ã§ã³äžãäžèº«ãæŽæ°ãããã®ã§ãããé衚瀺(leave)æã¯æŽæ°ãè¡ãããŸãããã©ãããããšãã¯ãâã®äŸãèŠããšããããŸã
ãã®äŸã§ã¯åžžã«æŽæ°ããç¶ããçŸåšæå»ã衚瀺ããã³ã³ããŒãã³ãã<Transition>
ã§2ç§ãããŠè¡šç€º/é衚瀺ããŠããã ãã§ãããã§ãŒãã€ã³æã«ã¯æèšãåããç¶æ
ã§åŸã
ã«çŸããŸããããã§ãŒãã¢ãŠãæã¯æèšã®æ°åãæ¢ãŸã£ãç¶æ
ã§æ¶ããŠããããšãããããŸãã
é¡äŸãšããŠãã¢ã³ããŠã³ãçŽåïŒonBeforeUnmount以é
ïŒã«ref
ãreactive
ã倿ŽããŠãåæ ãããªããæ²ŒããããŸãã
POINT: ã¢ã³ããŠã³ãæã®ã¢ãã¡ãŒã·ã§ã³äžã¯ã³ã³ããŒãã³ãã¯ãã§ã«ãªã¢ã¯ãã£ãã§ã¯ãªã
ããã¯å®éã«ã¯åé¡ãšãããããVueã®èšèšã§ããVueã§ã¯leaveãã©ã³ãžã·ã§ã³ã®éå§æç¹ã§DOMã®å
容ã¯å®å
šã«ããªãŒãºããããã©ã³ãžã·ã§ã³ãäœç§ãã£ãŠããã©ã³ãžã·ã§ã³äžã«DOMã®äžèº«ã¯æŽæ°ïŒåã¬ã³ããªã³ã°ïŒãããŸãããå®éã«ã¯ããã¯ããªããããããæ©èœã§ãã衚瀺ããå
容ããªããªã£ãã®ã§<Transition>
ã§ãã§ãŒãã¢ãŠãããããã¿ãããªã±ãŒã¹ã§ã¯ããã®æåããªããšïŒè¡šç€ºãããã®ããªããªã£ãŠããŸã£ãã®ã§ïŒãã§ãŒãã¢ãŠãããè¡ãããšãã§ããŸããã
ãããåºæ¬çã«ã¯v-if
ã§ã¯ãªãv-show
ã«å€ããã°è§£æ±ºã§ããŸãããã ããåå ã§æžããéããããã¯ããã§å°ãããšãããã®ã§äœ¿ãåããå¿
èŠã§ãã
-
leaveãã©ã³ãžã·ã§ã³éå§æã®ç¶æ ã§DOMãåºå®ãããå Žåâv-ifã§ãã©ã³ãžã·ã§ã³ïŒã³ã³ããŒãã³ãã¯ã¢ã³ããŠã³ããããïŒ
-
leaveãã©ã³ãžã·ã§ã³äžãææ°ã®ç¶æ ã«DOMãæŽæ°ãç¶ãããå Žåâv-showã§ãã©ã³ãžã·ã§ã³ïŒã³ã³ããŒãã³ãã¯ã¢ã³ããŠã³ããããã«ãã®ãŸãŸæ®ãïŒ
⌠v-showã䜿ã£ãŠleaveãã©ã³ãžã·ã§ã³äžããªã¢ã¯ãã£ããçããäŸ
ãããã3ïŒããããã©ããããã<Transition>
䜿ããªãã§å
šéšèªåã§ã¢ãã¡ãŒã·ã§ã³æžããŠãããã§ããïŒ
<Transition>
ã¯ãã¡ããšäœ¿ãã°åŒ·åã§äŸ¿å©ãªã®ã§ãããè²ã
æ²Œãæ·±ãã®ã§ãç¡çã«äœ¿ããªãéžæè¢ãããã ãšæã£ãŠããŸãã
enter(appear)ã¢ãã¡ãŒã·ã§ã³
ç»å Žæã®ã¢ãã¡ãŒã·ã§ã³ã¯å
ã
æ²Œãæ·±ããªãã®ã§<Transition>
ã䜿ã£ãŠããã»ã©ãããã©ããã¯ãªãã®ã§ããããã®å<Transition>
ã䜿ããªããŠãè¯ãã±ãŒã¹ãå€ãã§ãã
åçŽãªenterïŒããŠã³ãæïŒã¢ãã¡ãŒã·ã§ã³ã§ããã°ã以äžã®ããã«1ååçã®animation
ãæå®ããã°äºè¶³ããŸãã
<style scoped>
.TamaButton {
animation: appear 1s;
}
@keyframe {
from { opacity: 0 }
to { opacity: 1 }
}
</style>
leaveã¢ãã¡ãŒã·ã§ã³
leaveåŽã¯<Transition>
ãšv-show
ã䜿ãã®ãåºæ¬çã«ã¯ãããã©ãããå°ãªããšæã£ãŠããã®ã§ããããŸãã«ã¢ã³ããŠã³ãæã«å¥œãã«ã¢ãã¡ãŒã·ã§ã³ãããŠãããã£ãŠæãããšããããŸãã
ããŸãçã£åœãªæžãæ¹ã§ã¯ãªããšæãã®ã§ãããäžã®ããã«onBeforeUnmount
ã§èŠçŽ ãååŸããŠãWeb Animations APIãªãGSAPãªãã§å¥œãã«ã¢ãã¡ãŒã·ã§ã³ãããããšããããŸããåè¿°ããéãonBeforeUnmount
以éãã³ã³ããŒãã³ãã®DOMã¯ãªã¢ã¯ãã£ãã§ã¯ãªããªãã®ã§ã奜ãåææžãæããŠãåºæ¬çã«å®å
šã§ãã
<script setup lang="ts">
import { onBeforeUnmount, ref } from "vue";
import { useTimestamp } from "./useTimestamp";
const { timestamp } = useTimestamp();
const elRef = ref<HTMLElement>();
onBeforeUnmount(() => {
const el = elRef.value;
if (!el) return;
el.animate(
[
{ opacity: 1, scale: 1 },
{ opacity: 0, scale: 0 },
],
1000
);
});
</script>
<template>
<button ref="elRef">{{ timestamp }}</button>
</template>
ãã ããããã ãã©ã¢ãã¡ãŒã·ã§ã³éå§ãšåæã«ã¢ã³ããŠã³ããããDOMãç Žæ£ãããŠããŸãã®ã§ãç Žæ£ãé
ãããããã ãã«èŠªåŽã«duration
ãæå®ãã<Transition>
ãèšçœ®ããŸãã
<script setup lang="ts">
import { ref } from "vue";
import TimeStampClock from "./components/TimeStampClock.vue";
const visible = ref(true);
</script>
<template>
<label><input type="checkbox" v-model="visible" />æèšã衚瀺</label>
<Transition :duration="1000">
<TimeStampClock v-if="visible" />
</Transition>
</template>
äžèŠãããšé¢åãªã ãã«ãèŠããŸãããããŒãžé·ç§»ã®ãã©ã³ãžã·ã§ã³ãªã©ãäžåºŠã«ããããã®åå«ã³ã³ããŒãã³ãããã©ã³ãžã·ã§ã³ã€ããªããã¢ã³ããŠã³ããããããªã±ãŒã¹ã§ã¯äœ¿ããæ¹æ³ã§ããæ¬¡ã®äŸã§ã¯ããã®æ¹æ³ã§<TamaSans>
ã³ã³ããŒãã³ãã®è¡šç€ºïŒããŠã³ãïŒ/é衚瀺ïŒã¢ã³ããŠã³ãïŒãåãæ¿ããŠããŸãã
<script setup lang="ts">
import { ref } from "vue";
import TamaSans from "./demo2x/TamaSans.vue";
const visible = ref(true);
</script>
<template>
<div class="DemoStage01">
<label>
<input type="checkbox" v-model="visible" />
ããŸãããã¡ã®ã³ã³ããŒãã³ãã衚瀺(v-if)
</label>
<div class="tama">
<Transition :duration="2000">
<TamaSans v-if="visible" />
</Transition>
</div>
</div>
</template>
<TamaSans>
ã³ã³ããŒãã³ãã®äžã§ã¯ïŒãã®ããã«å
ã®åã»å«ã³ã³ããŒãã³ããŸã§å«ããŠïŒã¢ã³ããŠã³ãæã«2ç§ã®ç¶äºãäžããããããšã«ãªãã®ã§ããã®ç¯å²ã§åã
èªç±ã«leaveã¢ãã¡ãŒã·ã§ã³ãå®è£
ããããšãã§ããããã«ãªããŸãã
ããŸã詳ããã¯ãªãã§ãããã«ãŒã¿ãŒã«äœ¿ãã°Nuxtã®PageTransition
ã¿ãããªããšãã§ãããšæããŸãã
ãŸãšãïŒVueã®<Transition>
ã¯äŸ¿å©ã ãã©æ²Œãæ·±ã
以äžãVueã®<Transition>
åå¿è
ãããããã¡ãªæ²Œã§ããã
ãããåããã ãšã¯æããŸããããã®æ²Œã«ããã£ãŠåèŠå
«èŠããã®ã¯å
šéšç§ã§ãããããå²ãšçµæ§æè¿...
<Transition>
ã¯äŸ¿å©ã ãã©ãå®éçµæ§ããã¥ã¡ã³ãã«æèšãããŠããªãããšãå€ãå¥¥ã®æ·±ãæ©èœã§ããæå€ãšäœå¹Žäœ¿ã£ãŠãŠãããã£ãŠããªãããšãå€ãã£ããããã®ã§ãããŸã«æ·±æã£ãŠã¿ãã®ãè¯ãã§ãã
ææ¥ã¯ @nishihara_zari ããã§ã