0👍
✅
You should use the <TransitionGroup>
Vue component to apply transitions/animations to components. No need for you to manage the animation timing manually, the transitions will be applied to any mounting/destroying components.
Here is a working example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
<script>const rid = _ => "rid_" + Array.from(crypto.getRandomValues(new BigUint64Array(2))).map(item => item.toString(36)).join("");</script>
<script>const ider = query => "#"+$(query).last().attr(`id`, rid()).attr(`id`);</script>
<style>
component { display: none; }
app { display: block; }
</style>
</head>
<body>
<style>
@keyframes vbst_toast-show {
0% { opacity: 0%; }
100% { opacity: 100%; }
}
@keyframes vbst_toast-fade {
0% { opacity: 100%; }
100% { opacity: 0%; }
}
.vbst_toast-show {
animation-name: vbst_toast-show;
animation-duration: 1s;
animation-play-state: running;
animation-fill-mode: forwards;
}
.vbst_toast-fade {
animation-name: vbst_toast-fade;
animation-duration: 1s;
animation-play-state: running;
animation-fill-mode: forwards;
}
.fade-move,
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateX(30px);
}
.fade-leave-active {
position: absolute;
}
</style>
<component name="bs-alert">
<div :id="toast.id" class="toast show">
<div class="toast-header">
<i class="bi" :class="['bi-'+toast.icon,'text-'+toast.color]"></i>
<strong class="ms-2" :class="['text-'+toast.color]">{{toast.title}}</strong>
<small class="ms-auto">{{toast.time}}</small>
<button class="ms-2 btn btn-link text-danger p-0 bi bi-x"></button>
</div>
<div class="toast-body">{{toast.body}}</div>
</div>
</component>
<app>
<transition-group name="fade">
<bs-alert
v-for="toast in toasts"
:toast="toast"
:key="toast.id"
/>
</transition-group >
</app>
<script>
APP$VUEBS_TOASTER = Vue.createApp({
data() {
return {
title: "<%- title %>",
toasts: []
}
},
components: {
'bs-alert': {
props: ['toast'],
template: ider(`component[name="bs-alert"]`),
data() {
return {
animation: 'vbst_toast-show'
}
},
mounted() {
setTimeout(() => {
this.animation = 'vbst_toast-fade';
}, 6000);
}
}
},
methods: {
addToast(toast) {
this.toasts.push(toast);
setTimeout(_=>{ this.toasts.shift() }, 3000);
}
}
}).mount(ider(`app`))
</script>
<button style="position: fixed; bottom: 0; right: 0;">add toast</button>
<script>
$("button").last().on("click", function() {
APP$VUEBS_TOASTER.addToast({
id: rid(),
color: "primary",
icon: "alarm",
title: "title",
body: `hello world!`
});
})
</script>
</body>
</html>
Source:stackexchange.com