I have a React application. I am using React Spring for overall animations. I am not able to animate 2 things -
The animation I am experimenting with is a simple opacity animation.
import { useSpring, animated } from "react-spring";
/***
Some code
***/
const styleProps = useSpring({
to: { opacity: 1 },
from: { opacity: 0 }
});
1) Is conditional elements. Please refer code below -
<section>
{!flag ? (
<animated.div style={styleProps}>
Some random text
</animated.div>
) : (
<animated.div style={styleProps}>
To appear with animation
</animated.div>
)
}
</section>
The issue is that the animated.div of react-spring does not animate the same. What is the right way? Is there a way to animate the same without react-spring?
2) I have a conditional bootstrap className attached based on a flag. I want to animate the same
<animated.div style={styleProps} className={classnames({
"col-lg-6": !flag,
"col-lg-12": flag
})}
>
Random Content
</animated.div>
For this also, the issue is that it is not animating. What is the right way?
Yo have a lot of question. I can answer part of it and maybe you will understand it better.
Your example of useSpring animation is triggered only once. And when you switch between components with the conditional render it will no longer animate.
But you can re-trigger the animation in useSpring, if you change the 'to' parameter conditionally (and leave the render to react-spring).
const styleProps1 = useSpring({
to: { opacity: flag ? 1 : 0 },
from: { opacity: 0 }
});
const styleProps2 = useSpring({
to: { opacity: flag ? 0 : 1 },
from: { opacity: 0 }
});
<section>
<>
<animated.div style={styleProps1}>
Some random text
</animated.div>
<animated.div style={styleProps2}>
To appear with animation
</animated.div>
</>
</section>
You have to use absolute positioning if you want the element to appear in the same place.
You can achieve similar effect with useTranstion also with absolute positioning. In this case the element dismounted at the end of animation. So if you have mouse click problems with the useSpring method you can try to switch to useTransition.
Maybe it also answer your second questiona as well. I am not familiar with bootstrap.
Related
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
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 want to create a smooth transition between 2 images with a legend.
The images come from an object-array of images.
Because works only on single tags and components, I've created a component to define the image+legend.
<transition>
<home-image :slide="slide" :key="slide"></home-image>
</transition>
The classes I define are like this
.v-enter-active,
.v-leave-active {
transition: opacity 2s ease-in-out;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
.v-enter,
.v-leave-to {
opacity: 0;
}
The new image is returned by a method
updateSlide() {
this.slide = this.entries[ Math.floor( Math.random() * this.entries.length ) ];
}
where entries is my array defined in data
this.slide is updated in regular intervals, every 10seconds like this, which is defined in the created() section
this.updateSlide();
this.uSlide = setInterval( this.updateSlide, 10000);
The code works, in the sense that a new image is loaded in this.slide every 10 seconds.
However, the transitions work only "half-way".
There is no transition fading out: the "old image" disappears and makes way for the new image fading in.
However, what I'd like is a smooth transition from one to the other.
I've tried more than a couple of ideas including using mode="out-in" and "in-out" but nothing works as I want.
What am I overlooking?
I'm surprised by the issue im having with this simple animation procedure. I have a ternary that displays one series of components or another based on a condition. My first thought was to wrap them in a div that animates them once they appear. Unfortunately, its only animating the first condition on page load, switching tabs has no effect. Any thoughts?
const dashboard = () => {
const [pane, setPane] = useState(0);
const props = useSpring({ opacity: 1, from: { opacity: 0 } });
return (
...
<MainContent>
{pane === 0 ? (
<animated.div style={props}>
<LastModuleWidget {...mockLastModule} />
<BookmarkedModules />
<LatestActivity />
</animated.div>
) : (
<animated.div style={props}>
<StatsModule />
<PreviouslyCompleted />
</animated.div>
)}
</MainContent>
...
)};
Moving the animation wrapper immediately outside of the ternary had no effect.
I've tried it with Springs (above) and my own implementation, no effect.
Ok, so the core issue is that my MainContent container did not have a key prop.
<MainContent key={pane}>
{pane === 0 ? (
<_AnimateFadeIn>
<LastModuleWidget {...mockLastModule} />
<BookmarkedModules />
<LatestActivity />
</_AnimateFadeIn>
) : (
<_AnimateFadeIn>
<BookmarkedModules />
<LatestActivity />
</_AnimateFadeIn>
)}
</MainContent>
also, for simplicity i reverted back to using my own animation since its a simple fade in:
const _AnimateFadeIn = styled.div`
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
animation: fadeIn 0.7s;
`;
I'm using styled-components. If you want to recreate this in CSS just write something like:
div className="_animateFadeIn" or whatever. Same thing.
const props = useSpring({
transform: toggle ? "translateX(-200px)" : "translateX(0)",
opacity: toggle ? 1 : 0
});
I currently have a div with above styles and opacity works fine with the onclicks, but i'm having trouble with translation.
Basically i want to animate sort of a slide out div. What would be the correct way of going about it?