How can I do a easy image replace with opacity transition in vue? - vue.js

I've created a code which is changing the current image after 3 secconds. Now the new current image is getting displayed without a smooth opacity effect. This looks bad and I want to change that. I want to do it with VueJS but wasn't apple to do it.
At the moment picture 1 is replaced with picture 2. But I would want that picture 2 replaces picture 1 with a transition. From vueJS I found the following:
.fade-enter-active, .fade-leave-active {
transition: opacity .25s ease-in-out;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
{
opacity: 0;
}
and that you can use it within a component, but I didn't quite understand that.
Current code:
const app = new Vue({
el: '#app',
data: {
images: [
'img1.jpg', 'img2.jpg', 'img3.jpg'
],
currentImage: 'img1.jpg',
counter: 0,
loaded: true
},
created() {
let self = this;
setInterval(function () {
self.loaded = !self.loaded;
self.currentImage = self.images[self.counter++];
if (self.counter > self.images.length - 1)
self.counter = 0;
}, 3000);
}
});
template html code:
<div id="app">
<div class="col-avatar">
<div class="prot-image img-fill-container">
<transition name="fade">
<img class="avatar" v-if="loaded" :src="'/caro/' + currentImage"
alt="Avatar">
</transition>
</div>
</div>
</div>
JS fiddle
https://jsfiddle.net/sheyu2rn/

Change .fade-enter, .fade-leave-to to .fade-enter-to, .fade-leave-to:
.fade-enter-active, .fade-leave-active {
transition: opacity .25s ease-in-out;
}
.fade-enter-to, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
{
opacity: 0;
}

Related

how to delay elements entering the DOM using TransitionGroup?

The problem I am running into is that all the elements are rendered first before the animation starts. The effect that I want to achieve is that the element is animated while entering the DOM and it happens one after another. What's wrong with my code?
<template>
<TransitionGroup appear name="stagger" tag="ul">
<li v-for="(num, index) in list" :style="{ '--order': index }" :key="num">
{{ num }}
</li>
</TransitionGroup>
</template>
<script setup>
const list = Array.from({ length: 5 }, (_, index) => index + 1);
</script>
<style scoped>
.stagger-enter-active {
animation-name: stagger;
animation-duration: 0.5s;
animation-delay: calc(var(--order) * 0.15s);
}
#keyframes stagger {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
After reading the official doc one more time, I realized that the same animation effect can be made with just transition and it works, which got me wondering what's the main difference between these two approaches?
.stagger-enter-from {
opacity: 0;
transform: translateX(-1em);
}
.stagger-enter-active {
transition: opacity 0.5s linear, transform 0.5s ease;
transition-delay: calc(var(--order) * 0.15s);
}

How to make transition work with "visibility" but not "display"?

The transition element of vue only works with display:none but not visibility:hidden, is there any way to make it work with visibility? I want to get the clientWidth of the element before it shows up, with display:none I can't get that value.
By the way I'm using vue3.
Here is the reproduction demo:
https://codesandbox.io/s/competent-hermann-b1s5q
I'm going to assume, for the sake of argument, that you genuinely do need to use visibility for hiding and that other potential solutions (such as opacity) won't work in your real use case, possibly because they don't prevent user interactions with the element.
However, the assertion in the question is slightly misleading. It isn't really a difference between display and visibility. The real difference here is that the display case is using v-show, which includes special handling for transitions.
The current source code for v-show can be seen here:
https://github.com/vuejs/vue-next/blob/d7beea015bdb208d89a2352a5d43cc1913f87337/packages/runtime-dom/src/directives/vShow.ts
A similar approach can be used to construct a directive that uses visibility. Below is an example. It is based on the code for v-show but I've cut it back to just the code required for this particular use case:
const visible = {
updated(el, { value, oldValue }, { transition }) {
if (!value === !oldValue) {
return
}
if (value) {
transition.beforeEnter(el)
el.style.visibility = ''
transition.enter(el)
} else {
transition.leave(el, () => {
el.style.visibility = 'hidden'
})
}
}
}
Vue.createApp({
data() {
return {
show: true
};
},
methods: {
toggle() {
this.show = !this.show;
}
},
directives: {
visible
}
}).mount('#app')
#app {
text-align: center;
}
.tooltip-enter-active {
transition: transform 0.4s ease-out, opacity 0.3s ease-out;
}
.tooltip-leave-active {
transition: transform 0.35s ease-in, opacity 0.28s ease-out;
}
.tooltip-enter-from {
transition: none;
}
.tooltip-enter-from,
.tooltip-leave-to {
transform: translateY(-30px) scale(0.96);
opacity: 0;
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.prod.js"></script>
<div id="app">
<transition name="tooltip">
<div v-visible="show">
Using visibility
</div>
</transition>
<button #click="toggle">toggle message</button>
</div>
I did also have to make a small CSS change to give the enter transition a kick:
.tooltip-enter-from {
transition: none;
}
You'd probably be better off without <transition> in this case:
const app = Vue.createApp({
data() {
return {
show: true,
};
},
methods: {
toggle() {
const tooltip = this.$refs.tooltip;
this.show = !this.show;
tooltip.classList.toggle("tooltip-show");
},
},
mounted() {
console.log('Tooltip-width: ', this.$refs.tooltip.clientWidth);
},
});
app.mount('#app')
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.tooltip {
opacity: 0;
transform: translateY(-30px) scale(0.96);
transition: transform 0.35s, opacity 0.25s;
}
.tooltip-show {
opacity: 1;
transform: translateY(0) scale(1);
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.js"></script>
<div id="app">
<div class="tooltip" ref="tooltip">This will work!</div>
<button #click="toggle">toggle tooltip</button>
</div>

How to use Vue transition to expand and shrink a div

Using Vue, I have two divs that I want to toggle with a transition. I want slowly expand the div to my desired width on click, and then shrink on another click. I can get the div to expand on one click, but can't figure out how to shrink it on the second click.
Not really clear if just specifying the width of the divs is enough, or if I also have to specify the same width in the css transition classes.
This fiddle shows what I've been trying: https://jsfiddle.net/vxmh8auo/1/
JS
new Vue({
el: '#app',
data: {
showButton: true
},
methods: {
randomise () { this.n = Math.random() }
},
components:{'input-div':blah}
});
CSS
.interaction {
border: 10px solid lightgreen;
display: flex;
flex: 1 0 auto;
max-height: 225px;
transition: max-height 0.25s ease-out;
}
.default {
width: 20px;
}
.bigger{
width: 200px;
}
.expand-enter-active, .expand-leave-active {
transition-property: width;
transition-duration: 5s;
}
.expand-leave-to {
width: 200px;
}
.expand-enter{
width: 20px;
}
HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div class="interaction">
<button #click="showButton=!showButton">
<transition name="expand" mode="out-in">
<div v-if="showButton" v-bind:class="showButton ? 'default':'bigger'" key="small"> B </div>
<div v-else class="bigger" key="big"> Bigger </div>
<!--<input-div><</input-div>-->
</transition>
</button>
</div>
</div>
You can do something like this, add class 'default' and toggle class 'bigger':
<button #click="toggleBigger">
<transition name="expand" mode="out-in">
<div class="default" v-bind:class="{ bigger: showButton }" key="small"> B </div>
<!--<input-div><</input-div>-->
</transition>
</button>
Add method toggleBigger, its more readable this way:
methods: {
toggleBigger(){
this.showButton = !this.showButton;
}
And add transitions on your css classes directly like this:
.default {
transition: max-height 0.25s ease-out;
width: 20px;
transition-property: width;
}
.bigger{
transition: max-height 0.25s ease-out;
width: 200px;
transition-duration: 5s;
transition-property: width;
}
You can see working fiddle HERE
PS I did not removed unnecessary classes from fiddle.
This seems so hackey and containing a lot of unnecessary classes, but it works: https://jsfiddle.net/df70pk68/
Again, my use case is a button that expands to a component with an input box and then shrinks again. My solution was to immediately make the component opacity zero, then slowly shrink it. The prevents the input box from breaching the border of the shrinking div. I would love it if someone could figure out a more elegant way to do this....
HTML
<div id="app">
<div class="interaction">
<button #click="showButton=!showButton">
<transition name="fade" mode="out-in">
<div v-if="showButton" class="default" key="small"> B </div>
<input-div class="bigger" v-else><</input-div>
</transition>
</button>
</div>
</div>
CSS
.interaction {
border: 10px solid lightgreen;
display: flex;
flex: 1 0 auto;
max-height: 225px;
}
JS
const blah = Vue.component('input-div',{
template: '<div><input type="text" readonly></div>'
});
new Vue({
el: '#app',
data: {
showButton: true
},
components:{'input-div':blah}
});
.default {
width: 20px;
}
.bigger{
width: 250px;
}
.fade-leave-active {
transition: all 5s ease;
}
.fade-leave-to{
width: 300px;
}
.bigger.fade-leave-to{
width:20px;
opacity: 0
}
.bigger.fade-leave-active{
transition: opacity 0s ease;
transition: width 5s ease;
}
maybe you should forget vue's support on this, only use css3 can do that, and very simple.
first ,this is your div's code
<div id="my-div"></div>
#my-div{
transition: width 0.5s; /* this is the key code you need */
}
then, you can change the width of my-div use vue or js-dom or whatever, the magic thing will happen.

Vuejs transition classes are not applying on Vue Component

I'm using VueJs transition to make a component appVacancies slide.
<transition name="slide-down" mode="out-in" appear>
<appVacancies v-if="showVacancies"></appVacancies>
</transition>
It's Vue specific CSS for transition is
// Slide Down animation
.slide-down-active, .slide-down-leave-active {
transition: all .5s ease-out;
}
.slide-down-enter, .slide-down-leave-to {
opacity: 0;
transform: translateY(20%);
}
On created() life-cycle hook I'm just using setTimeout() to make showVacancies boolean to true.
I also have a fade animation with similar configuration but that is working.
<transition name="fade">
<h1 v-if="!showMotion" class="display-2 grad-text mt-4">Welcome!</h1>
</transition>
// CSS
.fade-enter-active, .fade-leave-active {
transition: opacity .5s ease-out;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
I don't know what I'm doing wrong here. Please help.

How to use Vue JS transition "inside" another transition?

Say I have a normal transition, which works perfectly fine (following vue docs). But I would like to have another transition INSIDE that one.
So for example, an element slides in, but then the text within that fades in at the same time?
I can't get the inside child transition to animate. It's not getting fired? I've tried "appear" also thinking the node is new.
There's almost no information out there on this.
<div id="demo">
<transition name="slide">
<div v-if="show">
<transition name="slide-fade">
<p>hello</p>
</transition>
</div>
</transition>
</div>
Transition effects
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.slide-fade-enter-active {
transition: all 0.3s ease;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
You did not seem to add the transition CSS for the slide component. The following should work:
const vm = new Vue({
el: '#demo',
data() {
return {
show: true
}
}
})
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: opacity 0.5s;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
.slide-enter-active {
transition: all 0.3s ease;
}
.slide-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-enter,
.slide-leave-to {
transform: translateX(10px);
opacity: 0;
}
/* Some styling to make them noticeable */
.parent {
background-color: lightgray;
padding: 2px;
}
.child {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<transition name="slide">
<div v-if="show" class="parent">
<transition name="slide-fade">
<p class="child">hello</p>
</transition>
</div>
</transition>
<button #click="show = !show">Toggle</button>
</div>