how do we add transition effects to *ngIf in Angular? - css

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)

Related

Unable to use percentages inside of my Keyframe animation transformations

Currently I'm trying to add an animation to a page of a few shooting stars flying across the screen. I want the star to appear and end at certain percentages of the available screen to make it responsive and visible on any device. However when I try to do this, my values seem to default to 0px rather than the percentage that I am passing into my tailwind.config.js from my React Component. The values work if I pass in pixels, but as I mentioned, I would rather do percentages so that it can work well on any screen.
Currently, I am passing in my desired percentage values using this method with an alteration to make it React compatible like this:
style={{"--startingX":props.startingX}}
I am currently using NextJS and TailwindCSS to attempt to make this.
Here are (what I believe are) the relevant files:
/components/shootingStar.js
export default function ShootingStar(props) {
return (
<div>
<div style={{"--percentage":props.startingX}} className="absolute animate-star">
<div className="transform absolute top-2/4 ..."></div>
<div className="absolute top-full left-2/4 ..."></div>
</div>
</div>
)
}
animation: 'star' is the animation in question, inside of keyframes I created my custom animation.
tailwind.config.js:
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
fontFamily: {
'Quicksand': ['Quicksand', ...defaultTheme.fontFamily.sans],
},
'animation': {
...
star : "star 3s linear infinite"
},
'keyframes': {
...
star: {
'0%' : {
transform : 'translateX(var(--percentage)) translateY(0px) rotate(235deg)',
'opacity': '50%'
},
'75%' : {
'opacity' : '100%'
},
'100%' : {
transform : 'translateX(350px) translateY(500px) rotate(235deg)',
'opacity' : '0%'
}
}
}
},
},
plugins: [],
}
index.js
return (
//This div is the parent div to the whole page which is why I included it.
<div className="bg-gradient-to-tl from-blue-900 to-black overflow-x-auto h-screen w-screen ">
<ShootingStar startingX={'25%'} startingY={'25%'} endingX={'500px'} endingY={'500px'}</ShootingStar>
{The remaining shooting stars would eventually get created here}
</div>
...
)
When the animation is at 0%, I would like my star component to start at a specified X% of my screen (for example if I pass in 25% to my percentage var, I would like it to start 1/4 of the way into the screen from the left hand side of the window.)
If this is possible, I plan to expand this for the starting Y value and the ending values of X & Y as well which is why I have those props passed in.
If I pass in 25% to my percentage var, my starting value is at the top left of the screen (or 0). and allows the animation to run. If I try to hardcode percentage values into my Keyframe animation, the animation does not occur since all of the values are technically defaulting to 0.
I am expecting the animation to start at 1/4 of the way in from the left hand side.
I feel as if I'm doing something slightly wrong that won't allow me to make this animation possible but I am not entirely sure what that would be.
I figured out why my animation was stuck in the top left after further research. Keyframe animations take the height and width specifications of the component that is being animated, and do not inherit their parent's height or width (as I initially thought they did). Because I had not specified a height or width for my component it could not move across the screen as I originally wanted it to.
For anyone who might run into this as I did make sure you are setting the height and width of your components to allow your animation to go through your component. Otherwise, if you want it to run through your entire viewport, use vh rather than % when setting your animation values.
Regardless, I decided to opt for viewport since it better fits my use case.

How to make smooth fade-in transition with tailwind HeadlessUI

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

How to use two svelte transitions on the same togglable element?

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.

smooth transition of background color / vue js

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

CSS Transitions don't survive React Component changing inner DOM structure

I have illustrated the problem in this CodePen
const Component = ({ structure }) => {
switch (structure) {
case 'nested':
return (
<div>
<AnimatedComponent />
</div>
);
case 'flat':
return
<AnimatedComponent />
;
}
};
There's some logic in AnimatedComponent that changes the styling of the Component in an animated fashion, e.g. change the background color from black to red over a duration of 1 second. The animation is started by changing a color class on AnimatedComponent. There is CSS to handle the animation given the changed class.
When changing the DOM structure from nested to flat, the HTML element is destroyed and recreated, the transition starting state is lost (aka the browser doesn't know which class was set before because the element was newly created).
What I want React to do is to change the DOM structure with moving elements in new positions, not destroying and recreating them.
Is this possible?
I tried to use the key props on <AnimatedComponent />, but it only fixes the flash of DOM change. Animation is skipped. See Codepen. Thanks Thomas Rooney for this suggestion.
Can I tell React to apply the class changes just one tick after the position of the DOM element was changed?
Can I tell React to apply the class changes just one tick after the position of the DOM element was changed?
Yes, this is precisely what the setTimeout function is for. Copying your second example, where you fixed the flickering, wrapping your color action dispatch with setTimeout (with no time value, which defaults to 0), seems to fix your issue.
onColorClick: () => {
setTimeout(() => {
dispatch({type: 'TOGGLE_COLOR'})
})
},
codepen
Update: I've noticed it's a bit more reliable to add some time before the color change (second argument in setTimeout, (fn, ms). I believe this is because setState is also happening asynchronously.
onColorClick: () => {
setTimeout(() => {
dispatch({type: 'TOGGLE_COLOR'})
}, 100) <-- play around with this value
},

Resources