Transition elements below a transitioned v-if - javascript

I have two elements, and the top one's visibility is controlled by a v-if on a simple boolean.
transition(name="fade")
#element1(v-if="showFirst")
p Foo
#element2
p Bar
The first element is wrapped in a <transition> tag, exactly as per the Vue documentation.
However, while this does create a fading animation, the rest of the content on the page still jumps very jarringly.
How can I create a transition that will also smoothly transform the position of any and all siblings that follow?
A fiddle demoing this issue.

You need to use a transition-group and key your dynamic div and static div
<transition-group name="fade">
<div v-if="switc" key="dynamic" class="animated">
...
</div>
<div key="main-content" class="animated">
...
</div>
</transition-group>
And use this css classes
.fade-enter,
.fade-leave-active {
opacity: 0;
transform: translateY(-10px);
}
.fade-leave-active {
position: absolute;
}
.animated {
transition: all 0.5s;
/*display: flex;*/
width: 100%;
}
The real trick is to change position to absolute when leaving, then any other content can take correct position.
To know more about how Vue animate things please see this FLIP explanation post
And please see this working fiddle
https://jsfiddle.net/bjfhth7c/4/
Edit
By mistake I did set display: flex; in .animated class, that was causing to every inner element to render in a strange way.
So now, I completely remove .animate class, and instead apply transition: all 0.5s and width:100% to every direct inner element of .wrapper
My final scss looks like this:
.wrapper {
display: flex;
flex-direction: column;
>* {
transition: all 0.5s;
width:100%;
};
}
.fade-enter,
.fade-leave-active {
opacity: 0;
transform: translateY(-10px);
}
.fade-leave-active {
position: absolute;
}
Flex layout is a extend subject, but in short for this particular case flex-direction: column is arranging elements one bellows previous one.
If one of those elements has absolute position will be ignored in flex layout so any other elements will be redistributed on available space.
Please see this guide about flexbox and last working fiddle hope it helps.

You can use a slideDown/slideUp animation instead. For achieve this you don't need to know a height of a sliding element, the principles of max-height transition explained there.
So, as a result it will cause animated moving of elements below target.
Check out my example based on your fiddle.

vue js provides different transition classes, you have to use those properly to smooth the transition, I have tried with your example in this fiddle with some CSS, have a look.
.fade-enter-active, .fade-leave-active {
transition: all .5s;
height: 100px;
opacity: 0.5;
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
height: 0px;
opacity: 0;
}
Some details from documentation:
There are six classes applied for enter/leave transitions.
v-enter: Starting state for enter. Added before element is inserted, removed one frame after element is inserted.
v-enter-active: Active state for enter. Applied during the entire entering phase. Added before element is inserted, removed when transition/animation finishes. This class can be used to define the duration, delay and easing curve for the entering transition.
v-enter-to: Only available in versions >=2.1.8. Ending state for enter. Added one frame after element is inserted (at the same time v-enter is removed), removed when transition/animation finishes.
v-leave: Starting state for leave. Added immediately when a leaving transition is triggered, removed after one frame.
v-leave-active: Active state for leave. Applied during the entire leaving phase. Added immediately when leave transition is triggered, removed when the transition/animation finishes. This class can be used to define the duration, delay and easing curve for the leaving transition.
v-leave-to: Only available in versions >=2.1.8. Ending state for leave. Added one frame after a leaving transition is triggered (at the same time 7. v-leave is removed), removed when the transition/animation finishes.
You can as well use CSS animations where you can provide on different phases of transition what will be your css property to make your transitions more smooth, like following and demo fiddle:
.fade-enter-active {
animation: bounce-in .5s;
}
.fade-leave-active {
animation: bounce-out .5s;
}
#keyframes bounce-in {
0% {
height: 5px;
}
30% {
height: 30px;
}
50% {
height: 50px;
}
100% {
height: 100px;
}
}
#keyframes bounce-out {
0% {
height: 90px;
}
50% {
height: 50px;
}
100% {
height: 0px;
}
}

Related

When an element unmounts, how can you make the remaining elements perform a slide transition/animation to fill the space?

I have a notifications container which has fixed position on the right of the screen, but has no size by default. Each time a notification is rendered, it will populate the container, giving it width and height, and the notifications will appear one after the other in a column. I chose to use flex, but the same thing can be achieved without flex, and instead giving each child a margin-bottom for the gap.
.notifications {
position: fixed;
top: 5rem;
right: 1rem;
width: auto;
height: auto;
gap: 0.5rem;
display: flex;
flex-direction: column;
}
The notification itself has a lot of parts to it and the implementation isn't really relevant, so to simplify, Each notification has basic slide-in and slide-out animations:
.favorites-notification {
// other styles
transform: translateX(200%);
opacity: 0;
animation: slideIn 0.375s ease-out forwards, slideOut 0.375s ease-out 2.625s forwards;
}
#keyframes slideIn {
100% {
transform: translateX(0%);
opacity: 1;
}
}
#keyframes slideOut {
100% {
transform: translateY(-100%);
opacity: 0;
}
}
This looks pretty good when only one notification is rendered on the screen. However, when there are multiple notifications, in a top to bottom list, the remaining notifications will snap in place to the empty space left behind by the unmounted notification. Each notification's lifespan is set to 3 seconds with a setTimeout in useEffect on mount. How do I get the remaining elements to slide up to fill the position left by the unmounted notification, rather than just snap in place when the notification unmounts? I want the other notifications to slide up at the same time as the highest one is sliding up and out so it looks smooth.
I've tried collapsing the height of the notification to remove in the slideout animation, but it doesn't achieve the desired result and it's very unsmooth. I also don't want to affect the height of the notification that's sliding out because it lingers on the screen for a bit, and that's not the affect I'm going for. I know that translate doesn't affect document flow, so the DOM still thinks the element is where it originally was until it unmounts.
All help is appreciated. If my question/explanation isn't clear, please point that out and I'll revise it.
Edit: Using the top property didn't seem to work either. Setting negative margin-top is better, but it starts off smooth and still snaps at the end. To make negative margin work smoothly, I have to know the exact size of the notification, and that varies.

Why doesn't a css transition fire immediately after display change

TL;DR A css transition on opacity does not work immediately after display change, but works with setTimeout(.., 100). Why?
What do I want?
I want to flash a message for a couple of seconds and then fade it out. Seems pretty basic, right?
What do I have?
Well, here's a jsfiddle, but let me explain in detail.
Say I have a message block
<div id="message" class="message">
Here be dragons
</div>
Which starts hidden but opaque
.message {
opacity: 1;
display: none;
}
Once I've prepared my message I want to show it.
document.getElementById("message").style.display = "block"
Now I want the message to fade out so I added a simple transition on opacity.
.flash {
opacity: 0;
transition: opacity 2s ease-out 1s;
}
Which I apply with the following
document.getElementById("message").classList.add("flash")
What goes wrong?
The message div is shown but it stays invisible as the opacity: 0 immediately applies. Besides, the transitionend event is not firing, which makes me think the transition does not happen at all for some reason. Weird, right?
However, everything's fine once I add the timeout
document.getElementById("message").style.display = "block"
setTimeout(() => (document.getElementById("message").classList.add("flash")), 100)
That works but seems like a totally dirty hack. Why is it like this?
You can see this behaviour on jsfiddle with two buttons aptly named 'Working' and 'Not working';
There are two things that together cause this:
When items have display: none the opacity is ignored (as not relevant). And so when you apply display: block to them they render the provided opacity with the current value without any transition effect.
Changes you apply to the style attribute all apply together at the moment a paint (asynchronous) happens, and so the transition definition comes too late.
First, make sure to set the transition effect definition before the actual application of it, in the message CSS class.
I would then suggest using height instead of display to get the same effect. You would need to switch the border on and off also (through its width):
document.getElementById("working").addEventListener("click" , () => {
document.getElementById("message").classList.add("flash");
})
// reset
document.getElementById("message").addEventListener("transitionend" , () => {
document.getElementById("message").classList.remove("flash")
})
.message {
border: solid 0px;
background: grey;
opacity: 1;
transition: opacity 2s ease-out 1s;
overflow: hidden;
height: 0;
}
.flash {
height: 100%;
opacity: 0;
border-width: 1px
}
<div id="message" class="message">
Here be dragons
</div>
<button id="working"> Working </button>
Why it doesn't work is because you are applying display:block and opactiy: 0 at the same time. When you set the attribute display in css it ignores all transition, there has to be an event in between setting display and transitions. An alternative is using visibility:hidden and visibility:visible instead of display but note that this only hides the element and the element is still present in its position

when we collapse transition is not good

When we expand transition is smooth but when we collapse transition is not good... when its about to collapse I see a shake.
I played with transition but its not working. Can you help me providing my code in the fiddle?
.accordion-section {
border-bottom: solid 1px #000;
}
.accordion-section > h3 {
padding: 6px;
font-size: 16px;
background-color: #CCC;
margin: 0;
}
.accordion-section > .body {
height: 0;
padding: 0 10px;
overflow-y: hidden;
transition: height .5s;
transition: height .5s, padding-top .5s, padding-bottom .5s;
}
You can transition max-height instead of height and enclose the body content with padding, etc inside of the element you're transitioning (added .body-inner in .body). I also added a transition for scale() as it will cause a more "accordion" style collapse, but you can try it without that.
with scale() - http://jsfiddle.net/b4L6kyg4/93/
without - http://jsfiddle.net/b4L6kyg4/94/
Just give the initial div background color green. when the accordion is closing it doesn't have any background so it makes it look as if the div is flickering.
.accordion-section > .body {
background: green;
}
There are a couple of things you can do:
First, accelerate some device's hardware by using -webkit-transform: translate3d(0,0,0); . Second, use the CSS animation property transition timing function. I am not sure which effect you are trying to achieve, but you have "ease" on certain elements. Try experimenting with "ease-out". Third, the CSS transitions you're using may not be aligned perfectly with your elements, so when the transition finished running, the div snaps back to its place. A quick patch for this problem may be animation-fill-mode: forwards; . Your fiddle does not have the actual #keyframes for animation, so it is hard to give you any further advice.

Ajax image page transition

I just saw a really cool page transition on Behance where you click a image and it just expands to a new div (I think) with a with of 50%. Can someone explain to me how to make this work or have an example? Transition here:
https://vimeo.com/162486588
You can use css transitions if you like. Check the example that I wrote for you.
.normal {
float: left;
margin-right: 10px;
width: 10%;
height: auto;
transition: width 2s, height 2s;
}
.transition {
width: 50%;
height: auto;
}
Basically they work by defining 'start' values (in our case width & height inside .normal) and also a definition of how to make the transition and what properties to apply it to (in our case width and height with 2s duration each).
If you now add a class to the element that has the properties with different values (in our case .transition), they'll be animated to the new value.
To complete the example, I also added some text that is faded in after the transition has been completed.
The javascript part is fairly simple: When clicking the image, add the .transition class, then wait for 2 seconds (the transition duration) and finally fade in the text.
$('img').click(function() {
$(this).addClass('transition');
setTimeout(function() {
$('.text').fadeIn();
}, 2000);
});

How to force style to apply for transition / Display queuing order

I am trying to get some transitions to work cleanly, but due to the way the browser queues the various JS and display rendering threads, this is becoming increasingly difficult.
I have a <div class="expandable"> which expands on a click event (somewhere else). With overflow control, so its content appears gradually. Once fully expanded, overflow control needs to be removed, as the content may be taller than the expanded-to height, and there are some inner elements that will need to poke outside the area.
I have three bits of CSS:
.expandable {
max-height: 0;
overflow: hidden;
transition: all 2s;
}
.expandable.activating {
max-height: 1000px !important;
overflow: hidden;
transition: all 2s;
}
.expandable.active {
max-height: none;
overflow: visible;
transition: none;
}
Click on the trigger element, and we add .activating to our div. Once the transition is completed, we remove .activating and add .active. So far, so good.
However, when clicking the trigger event while the element is expanded, I run into problems.
With n being the in-code reference to our div…
if (n.classList.contains("active")) {
n.classList.add("activating");
n.classList.remove("active");
window.setTimeout(function () {
n.classList.remove("activating");
}, 0);
}
The actual code is slightly different, as I have utility functions to set pairs of conditional classes, but this is effectively what is happening.
The problem is that with the 0 timeout delay, the .activating class is removed before it is actively rendered into the display. If I up the delay to 10, it renders in about half the time. i.e. sometimes, the panel will contract gracefully, and sometimes it will transition instantly from .active to its default state.
How do I delay the calling of that last class change until .activating has been properly rendered into the display, so that the transition actually works as intended?
(This is happening in Firefox, and exactly the same code seemed to work just fine previously.)
This causes delay if you set the height too large (like max-height: 1000px), especially for the cases when you get wide range of box heights.
So you can try this:
.expandable {
max-height: 0;
overflow: hidden;
transition: all 2s;
}
.expandable.activating {
max-height: 1000px ;
overflow: hidden;
transition: max-height 0.5s cubic-bezier(0, 1.05, 0, 1);
}
.expandable.active {
max-height: none;
overflow: visible;
transition: none;
}
if (n.classList.contains("active")) {
n.classList.add("activating");
n.classList.remove("active");
window.setTimeout(function () {
n.classList.remove("activating");
}, 0.5s);
}

Categories