I have a simple HeadlessUI Tab component like the one below.
import { Tab } from '#headlessui/react'
function MyTabs() {
return (
<Tab.Group>
<Tab.List>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tab.List>
<Tab.Panels>
<Tab.Panel>Image content 1</Tab.Panel>
<Tab.Panel>Image content 2</Tab.Panel>
<Tab.Panel>Image content 3</Tab.Panel>
</Tab.Panels>
</Tab.Group>
)
}
I would like to smoothly change the view (in this case, each Tab.Panel content) when I click the tab menu.
When I looked into the official example, there was no description of how to handle the transition like fade-in.
I know there is a tailwind fade-in & delay & transition animation CSS tag, but I am unsure where to add that tag so the headlessUI Tabs work smoothly.
Any code example is appreciated!
Use transitions provided from headlessui:
import { Transition } from '#headlessui/react'
Example:
import { Transition } from '#headlessui/react'
import { useState } from 'react'
function MyComponent() {
const [isShowing, setIsShowing] = useState(false)
return (
<>
<button onClick={() => setIsShowing((isShowing) => !isShowing)}>
Toggle
</button>
<Transition
show={isShowing}
enter="transition-opacity duration-75"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
I will fade in and out
</Transition>
</>
)
}
Animating transitions
By default, a Transition will enter and leave instantly, which is probably not what you're looking for if you're using this component.
To animate your enter/leave transitions, add classes that provide the styling for each phase of the transitions using these props:
enter: Applied the entire time an element is entering. Usually you define your duration and what properties you want to transition here, for example transition-opacity duration-75.
enterFrom: The starting point to enter from, for example opacity-0 if something should fade in.
enterTo: The ending point to enter to, for example opacity-100 after fading in.
leave: Applied the entire time an element is leaving. Usually you define your duration and what properties you want to transition here, for example transition-opacity duration-75.
leaveFrom: The starting point to leave from, for example opacity-100 if something should fade out.
leaveTo: The ending point to leave to, for example opacity-0 after fading out.
Reference : Create top-down slide animation using `Transition` from `#headlessui/react` using Tailwind CSS
Related
I have an input field that I want to hide/show and doing so with a fade and slide transition. I've have two examples that I came up with but both have their drawbacks and I'd like to know if there is a more elegant solution.
I just need one of the two questions to be answered as both of them would solve my problem.
Question 1: Is there a way to trigger multiple transitions for one transition-directive?
Question 2: How to add a class that will trigger an ordinary css-transition after an if-statement put the element in the DOM?
Example 1
Svelte does not allow two transitions on the same element. So one solution is to nest two elements as shown below. Is there instead a way to write a custom transition using both fade and slide transition:myMultiTransition?
{#if active === true}
<span transition:fade>
<span transition:slide>
<input type="text" />
</span>
</span>
{/if}
Example 2
In my other solution I just toggle an active class using a normal css transitions. The problem here is that the <input>-field never leaves the DOM. It's 0px height but it seems wrong to leave it there.
How to cuccessfully show the input field with an {#if active === true} and afterwards add a class that trigger the transition effect? Svelte seems to add the active-class that is supposed to trigger the transition before the element has entered the DOM.
I've tried to use await tick(), onMount, beforeUpdate in various combination with no luck.
When adding the class with a delay with setTimeout it works - but I don't like this solution because it could fail if not the timing is exact and I won't want a delay before the transition start.
<span class:{active}>
<input type="text" />
</span>
<style>
.active {
// Normal transition: opacity 1s etc ...
}
</style>
REPL
https://svelte.dev/repl/89cb7d26d9484d0193b4bc6bf59518ef?version=3.38.3
You can create your own transition function:
<script>
import { cubicOut } from 'svelte/easing';
let visibleDoubleElements = false;
function slidefade(node, params) {
const existingTransform = getComputedStyle(node).transform.replace('none', '');
return {
delay: params.delay || 0,
duration: params.duration || 400,
easing: params.easing || cubicOut,
css: (t, u) => `transform-origin: top left; transform: ${existingTransform} scaleY(${t}); opacity: ${t};`
};
}
</script>
<label>
<input type="checkbox" bind:checked={visibleDoubleElements}>
Svelte transition
</label>
{#if visibleDoubleElements === true}
<input transition:slidefade type="text" placeholder="Double elements" />
{/if}
REPL:
https://svelte.dev/repl/da8880947eff4f32b740a8742d9f817e?version=3.38.3
It might be the easiest to stick with the first solution you already provided: adding a wrapper for each transition.
If you want to reuse a specific combination of transitions it might be worth it to write your own one. At this point you can try to use the implementation from Svelte: Here is an example for Slide + Fade
function fadeSlide(node, options) {
const slideTrans = slide(node, options)
return {
duration: options.duration,
css: t => `
${slideTrans.css(t)}
opacity: ${t};
`
};
}
https://svelte.dev/repl/f5c42c6dc6774f29ad9350cd2dc2d299?version=3.38.3
Generic Solution (Theoretical)
In Svelte the transitions itself don't rely on CSS-transitions. A Svelte transition only provides the style for each transition step. Therefore a generic solution would be to create a merge-transition that takes 2..N transition functions and puts the styles from the individual transition together. Unfortunately this is not always trivial due to conflict situations in CSS.
E.g. combining two transitions... one where the opacity should be 0 and the other with a target opacity of 0.5. Question is: What should the output look like? If 0 is expected then there must be some logic which converts "opacity: 0; opacity: 0.5;" to "opacity: 0;". And there are surely more complex cases.
I am trying to smooth transition between background linear gradients in vue js
Previously when I created this transition effect using plain js / HTML / css - i created a div for each color and just changed there opacity - fading one out to another
(you can see the effect i am trying to create here, just nav between different languages to see the effect) https://littleleo.dev/
in vue js -
the nav invokes a function when clicked (which gets the nav id)
<Navbar :articleData="articleData" #backgroundColor="background"/>
background(f) updates a watched property - using a list of colors
methods: {
background(id){
let bg_id = id-1 // -1 so the number is inline with the array starting at 0 not 1
this.backgroundColor = this.backgrounds[bg_id]
}
data(){return{
backgroundColor: null,
backgrounds:
[
'linear-gradient(45deg, #e96443, #904e95)',
'linear-gradient(45deg, #3a7bd5, #3a6073)',
'linear-gradient(45deg, #f7ff00, #db36a4)',
'linear-gradient(45deg, #B24592, #F15F79)',
'linear-gradient(45deg, #FFB75E, #ED8F03)',
'linear-gradient(45deg, #673AB7, #512DA8)',
'linear-gradient(45deg, #fc00ff, #00dbde)',
'linear-gradient(45deg, #00C9FF, #92FE9D)',
],
and the magic happens here: where the background is changed
// changes background color
watch: {
backgroundColor: function(){
document.querySelector('.topElements').style.background = this.backgroundColor
}
}
So currently this happens with no transition time between the background colors changing - i was wondering if anyone knew how to transition without creating a overlay div for each of the colors &/or there is some clever vue js magic that can solve this type of issue:
further details:
this is in App.vue and my template looks like this:
<template>
<div id="app">
<div class="topElements" :class="[noneSelected]"> // <<< COLOR APPLIED HERE
<!-- animation -->
<lottie :options="defaultOptions" :height="350" :width="350"/>
<Navbar :articleData="articleData" #backgroundColor="background"/>
<span class="breaker"></span>
<transition name="content-holder">
<router-view :key="$route.path"/>
</transition>
</div>
<Footer />
</div>
</template>
the background color is being applied to everything inside of the class="topElements"
any help or suggestions would be amazing -
thanks in advance -
Wally
As vue.js documentation says, the best way to make a similar effect is using absolute positioning.
https://v2.vuejs.org/v2/guide/transitions.html#Transition-Modes
This is the best way to deal with it.
Most tricks wich get the same effect has a much bigger code and it is not the right way.
To understand how vue.js transition works please visit https://alligator.io/vuejs/understanding-transitions/
https://medium.com/#msanathkumar/understanding-transitions-and-animations-in-vuejs-115a0f5c6fbc
I am working on a carousel that provides an alternating src value to an image. This does work, however I cannot get React CSSTransition to work, since technically it is the same element just with a changing src value that is bound to a state variable.
Here is the code:
<CSSTransition
classNames="carousel"
in={true}
appear={true}
timeout={1000}
>
<img
src={this.state.imgLink}
key={this.state.imgLink}
/>
</CSSTransition>
Any advice is greatly appreciated!
Cheers,
Gabe
If you're in a state managed component you could use a switch variable in your state to tell CSSTransition when the src attribute is changing (by putting it on true) and then putting it back to false when the first transition is completed (with a onEntered callback):
<CSSTransition
classNames="carousel"
in={this.state.changing}
onEntered={()=> this.setState({switch:false})}
appear={true}
timeout={1000}
>
<img
src={this.state.imgLink}
key={this.state.imgLink}
/>
</CSSTransition>
That would work nice with a fading effect.
But since your classNames is called carousel, I think you want to be able to move images around (like one exiting while another one entering). You won't be able to do that with only one <img> tag but you will by having one per image and unmounting them on exit (unmountOnExit prop) and managing them with an index variable on the state:
{pictures.map((p, i) => (
<CSSTransition
key={p.id}
in={i === this.state.index}
classNames="carousel"
timeout={1000}
>
<img
src={p.url}
className={i === this.state.index ? "fade-enter-done" : ""}
/>
</CSSTransition>
))}
This may not be the official way but I found that if you wrap CssTransition with TransitionGroup and move the key from img to CssTransition it will work as you want it to. The enter and exit animation will run at the same time, perfect for images to slide in and out at the same time or crossfade, etc.
<TransitionGroup component={null}>
<CSSTransition classNames="carousel" timeout={1000} key={this.state.imgLink}>
<img src={this.state.imgLink} />
</CSSTransition>
</TransitionGroup>
If you want one animation to start only after the other one has finished, wrap it with SwitchTransition.
Transition modes. out-in: Current element transitions out first, then when complete, the new element transitions in. in-out: New element transitions in first, then when complete, the current element transitions out.
type: 'out-in'|'in-out'
default: 'out-in'
<SwitchTransition mode={"out-in"}>
<CSSTransition classNames="carousel" timeout={1000} key={this.state.imgLink}>
<img src={this.state.imgLink} />
</CSSTransition>
</SwitchTransition>;
Say you got two divs like this on the component.html
<div *ngIf="htmlSegment == 'a" >
{{someMessage}}
</div>
<div *ngIf="htmlSegment == 'b" >
{{someMessage}}
</div>
When you set the htmlSegment to a or b, we know that the truthy value segment comes alive and the other would disappear. No issues there.
But, what does it take to make the appearance and the disappearance of those segments come alive with some transitioning effects; such as fading in or out etc?
And is it possible to make sure, the transition starts after the dom is done in the new htmlSegment? ( This is so that the user does not experience any flashing or page jerking except the awesome fade in and fade out only. )
The Animate module does not animate your HTML elements, but when Animate notice certain events, like hiding or showing of an HTML element, the element gets some pre-defined styles(or classes) which can be used to make animations.
Add animate module to your project:
import {
trigger,
state,
style,
animate,
transition
} from '#angular/animations';
Add animation property to your component:
animations: [
trigger('heroState', [
state('a', style({
backgroundColor: 'yellow',
opacity:0.5
})),
state('b', style({
backgroundColor: 'black',
opacity:1
})),
transition('a => b', animate('100ms ease-in')),
transition('b => a', animate('100ms ease-out'))
])
]
The trigger will be called when herostate change.
At the end, your HTML code will be:
<div *ngIf="htmlSegment == 'a"
[#heroState]="a">
{{someMessage}}
</div>
<div *ngIf="htmlSegment == 'b"
[#heroState]="b" >
{{someMessage}}
</div>
I can't create an Angular2 online sample of this code but I create an AngularJS online sample for sensing what exactly happen in animations.(for changing animation, you can change 'nga-slide-down' class to 'nga-fade' or your own class)
Check Online(AngularJS - check auto-run js at the right panel and click on change button)
I am using CSSTransitionGroup from react-transition-group and I have a page displaying several objects, which go through some filters (every filter changes the state of the array - cant do it otherwise). I want to activate leave transition only on one element (when the timer of the object gets to 0).
Is there a way to achieve it without activating the leave when i filter?
EDIT:
Each object in my array is an auction that contains several information including a timer. i want to avoid the activation of the leave animation when i search my db and activate it only when the timer finishes.
home component:
eachAuction(item, i) {
return <Auction key={i} index={i} auctionfinished={this.deleteAuction}
offerBid={this.offerBid} data={item} />
}
render:
<div>
<CSSTransitionGroup
transitionName="auction"
transitionAppear={true}
transitionAppearTimeout={700}
transitionEnterTimeout={700}
transitionLeaveTimeout={500}>
{this.state.auctionsArr.map(this.eachAuction)}
</CSSTransitionGroup>
</div>
EDIT: What about specify the leaving class inside the element?
eachAuction(item, i) {
return <Auction {timer===0 ?? 'className="leaving"'} ... />
}
Then you specify the CSS effects:
.auction-leaving div { display: none }
.auction-leaving div.leaving { /* animation start */ }
.auction-leaving-active div.leaving { /* animation end*/ }