Not able to do rendering delay in css-grid-animation? - css

I am using animate-css-grid and React library to build my portfolio in a grid design, I kind of achieved what I wanted here is the pen link.
But when I click any of the boxes, the text which should go with the box rendered instantaneously. I want it to render after the box get scaled up.
this is piece code which I am using in rendering the text.
class Card extends React.Component {
state = { expanded: false };
randomNumber = Math.floor(Math.random() * 5) + 1;
render() {
return (
<div
style={{ backgroundColor: this.props.color }}
class={`card card--${this.randomNumber} ${
this.state.expanded ? "card--expanded" : ""
}`}
onClick={() => {
this.setState({ expanded: !this.state.expanded });
}}
>{this.state.expanded ? <Text /> :
(<div>
{/* <div className="card__avatar" />
<div className="card__title" /> */}
<div className="card__description">
{this.props.subject}</div>
</div>)}
</div>
);
}
}
I am rendering the Text component at the time it gets the expanded state true.
I don't know how to delay it. The animate-css-grid library provides custom configuration but I am unable to understand how to use it. Can anybody who is familiar with CSS animations help me out here, please.

I added a className around what <Text /> returns and trigger an animation to reveal the text when the --card-expanded className is present. The delay of 500ms feels good to me, but it's, of course, adjustable.
.card-text {
opacity: 0;
}
.card--expanded .card-text {
animation: 1s show-text forwards 500ms;
}
#keyframes show-text {
to {
opacity: 1;
}
}
CodePen

Related

Transition css animation works only once

I have a problem with css animation in react app triggering by useState. Animation between width changes works only once on one component. (I have array of tags on upper level)
...
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
console.log(isMounted);
setIsMounted(true);
// setIsMounted(false);
return () => {
console.log('There is');
setIsMounted(false);
}
}, []);
...
<div
id={item.label}
onKeyDown={onKeyDown}
className={isMounted ? 'i-tag' : 'no-tag' }
> {`some text`}
</div>
and scss here
.i-tag {
width: 100px;
position: relative;
max-width: max-content;
transition: width 1s ease-in;
}
.no-tag {
#extend .i-tag;
width: 0;
}
You can use TransitionGroup with CSSTransition for animating array of components.
Actually there are 2 ways to achieve animation of array of components in React,
By your own code which is bit trickier to do it perfectly
use some packages like 'react-transition-group' which is much easier to integrate
If you would like to use the package 'react-transition-group', here is the codesandbox on how to use it
<TransitionGroup component="ul">
{todos.map((todo) => (
<CSSTransition key={todo} timeout={700} classNames="item">
<li>
{todo}{" "}
<button
onClick={() => {
setTodos(todos.filter((t) => t !== todo));
}}
>
Delete
</button>
</li>
</CSSTransition>
))}
</TransitionGroup>
Courtesy: https://typeofnan.dev/how-to-animate-items-out-of-an-array-in-react/

Hide nav button in react-material-ui-carousel

I've just implemented the react material ui carousel, and it was pretty straightforward, the only thing i didn't catch, is how to hide buttons and show them only on over.
I noticed the props navButtonsAlwaysVisible and set it to false but it isn't enough.
Should i implement my own logic for that, or maybe I'm just missing something?
here's the component code:
import styles from '../../styles/Testimonial.module.scss'
import Image from 'next/image'
import Carousel from 'react-material-ui-carousel'
const Testimonial = _ => {
const items = [
{
imageUrl: "/png/image0.webp",
feedback: "feedback0",
name: "name0",
location: "location0"
},
{
imageUrl: "/png/image1.jpeg",
feedback: "feedback1",
name: "name1",
location: "location1"
}
]
return (
<div id="customers" className={`section ${styles.testimonial}`}>
<h2 className={`title ${styles.title}`}>Clientes Felizes</h2>
<span className={"separator"}> </span>
<Carousel
className={styles.carousel}
autoPlay={true}
stopAutoPlayOnHover={true}
interval={5000}
animation={"slide"}
swipe={true}
navButtonsAlwaysVisible={false}
navButtonsProps={{
style: {
backgroundColor: "#8f34eb",
opacity: 0.4
}
}}
>
{
items.map( (item, i) => <Item key={i} item={item} /> )
}
</Carousel>
</div>
)
}
function Item(props)
{
return (
<article className={styles.testimonial__card}>
<div className={styles.testimonial__photo_container}>
<Image
className={styles.testimonial__photo}
src={props.item.imageUrl}
alt="Testimonial"
width={312}
height={300}
/>
</div>
<p className={styles.testimonial__copy}>{props.item.feedback}</p>
<span className={styles.testimonial__name}>{props.item.name}</span>
<span className={styles.testimonial__city}>{props.item.location}</span>
</article>
)
}
export default Testimonial;
there's a prop called navButtonsAlwaysInvisible
navButtonsAlwaysInvisible={true}
You can try using Custom CSS for your purpose. Based on the current rendered markup,
.jss6 {
opacity: 0;
transition: all ease 1000ms; /* So that it does not disappear quickly */
}
You can define the hover for the parent so that it displays only when the parent container is hovered on:
.jss1.Testimonial_carousel__3rny3:hover .jss6 {
opacity: 1;
}
This is how it works now:

React Transition Group: CSSTransition works on enter, not on exit

I have a React app that is using the CSSTransition component from react-transition-group: when the component appears, everything behaves as expected (a 0.5s transition from opacity: 0 to opacity: 1), however when the component exits, the transition is not applied and it just immediately disappears. Can anybody help me figure out why?
Render method in component:
render(){
const countries = geoUrl.objects.ne_50m_admin_0_countries.geometries;
const { handleEnter, handleList, list } = this.props;
return (
<CSSTransition
classNames="transition"
transitionAppearTimeout={50000}
timeout={500000}
key={ list }
in={ list } // this is a boolean value passed from the parent component, it is initially set to false but changes to true when this component is rendered
unmountOnExit
appear
>
<div className="overlay" id="list">
<div className="wrapper" ref={this.setWrapperRef}>
<aside className="list">
<a className="close" href="#home" onClick={handleList}>×</a>
<ul className="countryList">
{ countries.sort((a, b) => (a.properties.NAME > b.properties.NAME) ? 1 : -1).map(geo =>
geo.properties.COUNTRY ?
<li className="listItem" key={ `${geo.properties.ISO_A3}${geo.properties.name}` }><a href="#country" onClick={() => {
const { NAME, DISH, DESCRIPTION, PHOTO, RECIPE } = geo.properties;
handleEnter(NAME, DISH, DESCRIPTION, PHOTO, RECIPE);
}}>{ geo.properties.NAME }</a></li>
: null
)}
</ul>
</aside>
</div>
</div>
</CSSTransition>
);
}
CSS:
.transition-appear {
opacity: 0.01;
}
.transition-appear.transition-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.transition-enter {
opacity: 0;
}
.transition-enter-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.transition-exit {
opacity: 1;
}
.transition-exit-active {
opacity: 0;
transition: opacity .5s ease-in;
}
I also struggled with the transition out, but have come up with a solution. I can't tell what your final result should look like in the UI, whether you want to transition in between conditionally rendered elements on the page, or just a transition when the component mounts and unmounts, but I think you want to do the former.
To transition between two or more elements on the same page, you will need to wrap your <CSSTransition> tags in <TransitionGroup> tags (and import it alongside CSSTransition). When you do this, you will need to provide a unique key property to the <CSSTransition> tag, it can't just be a boolean like it looks like you have. You also will need to slightly modify your CSS.
.transition-enter {
opacity: 0;
}
.transition-enter.transition-enter-active {
opacity: 1;
transition: opacity 0.5s ease-in;
}
.transition-enter-done {
opacity: 1;
}
.transition-exit {
opacity: 1;
}
.transition-exit.transition-exit-active {
opacity: 0;
transition: opacity 0.5s ease-in;
}
.transition-exit-done {
opacity: 0;
}
And the Transition tags:
import {CSSTransition, TransitionGroup} from "react-transition-group"
//...the rest of your code
return (
<TransitionGroup>
<CSSTransition
key={foo} //something unique to the element being transitioned
classNames="transition"
timeout={500}
>
<div className="overlay" id="list">
<div className="wrapper" ref={this.setWrapperRef}>
<aside className="list">
<a className="close" href="#home" onClick={handleList}>×</a>
<ul className="countryList">
{ countries.sort((a, b) => (a.properties.NAME > b.properties.NAME) ? 1 : -1).map(geo =>
geo.properties.COUNTRY ?
<li className="listItem" key={ `${geo.properties.ISO_A3}${geo.properties.name}` }><a href="#country" onClick={() => {
const { NAME, DISH, DESCRIPTION, PHOTO, RECIPE } = geo.properties;
handleEnter(NAME, DISH, DESCRIPTION, PHOTO, RECIPE);
}}>{ geo.properties.NAME }</a></li>
: null
)}
</ul>
</aside>
</div>
</div>
</CSSTransition>
</TransitionGroup>
)
Please note: You do not need appear, in, or unmountOnExit properties for this method, but you will have issues with duplicate elements appearing in the DOM during the transition, becuase react-transition-group actually clones the element and deletes the old one (which is why it needs a unique key). The only way to achieve a cross-fade transition is to take your elements out of the document flow with position absolute, so they overlap as one transitions in, and the other out.
I can't test your exact code, because there is not enough information, but I put together a very basic codesandbox that demonstrates the method with 2 conditionally rendered elements:
https://codesandbox.io/s/long-sea-9rtw9?file=/src/Test.js

Vue lazyloading, remove spinner when image loads

I have a vue Nuxt project where I explore lazyloading with lazysizes package.
I created a spinner component (html css only) who should be visible only while the image is loading.
I also created an ImageItem component who includes the spinner component and it looks like this:
< script >
import spinner from '~/components/spinner.vue'
export default {
components: {
spinner,
},
props: {
source: {
type: String,
required: true,
}
},
} <
/script>
<style lang="scss" scoped>.imageItem {
position: relative;
.image {
z-index: 2;
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: all 1s linear 0.35s;
}
}
}
</style>
<template>
<div class="imageItem">
<spinner />
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
To explain my code, I have props: source where In parent component i pass the image i want to lazyload. Also in the CSS while the image is loading, the image has .lazyloading class and when is loaded .lazyloaded class. Right now when Image is loaded i put it on top of the spinner.
My problem is, when I load the image I want to hide or destroy the spinner element since I think just putting the image on top is not the best way to do it. Can someone give me direction how should I properly hide the spinner when the image is loaded ?
Lazysizes fires an event when loading the image is finished : lazyloaded event, So you can do this :
<template>
<div class="imageItem">
<spinner v-if="lazyloading"/>
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
<script>
import spinner from '~/components/spinner.vue'
export default {
data(){
return {
lazyloading
}
},
mounted(){
document.addEventListener('lazyloaded', (e) => {
this.lazyloading = false;
}
});
}
}
</script>

React Transition Group: Offset animation start timing

I just started playing around with React Animations, and I seem to be stuck.
I have a series of Cards that appear (animate-in). Right now, they're all coming in at the same time (the animation plays at the same time). Is there a way to add an offset to the animation so that they play one after the other?
Animate = React.addons.CSSTransitionGroup
<Animate transitionName="cardAnim" transitionAppear={true}>
{
courses.map(function(element, index){
return (
<Card style={CardStyle} key={index}>
<CardMedia>
<img src={"img/courses/" + index + ".png"} />
</CardMedia>
<CardTitle>
{element}
</CardTitle>
</Card>
)
})
}
</Animate>
My aim: I want each Card to animate in separately, as in, the second card animates in after the first one has entered, the third animates after the second one is done, and so on.
Can anyone help? Thanks :)
All right, so I figured it out.
The key was the animation-fill-mode CSS property. I completely removed all React Animations and proceeded to figure it out using CSS only.
Here's the code, if anyone is interested:
CSS:
#keyframes flyInFromBottom {
0%{
opacity: 0;
transform: translateY(100vh);
}
100%{
opacity: 1;
}
}
JSX:
<Card style={{
animation: 'flyInFromBottom 0.3s ease ' + (index+1)*0.1 + 's',
float: 'left',
width: 'calc(50% - 4px)',
margin: '2px',
opacity: '0',
animationFillMode: 'forwards'
}}
key={index} onTouchTap={this.goToCourse}>
<CardMedia>
<img src={"img/courses/" + index + ".png"} />
</CardMedia>
<CardTitle>
{element}
</CardTitle>
</Card>
Essentially, I changed properties using CSS and persisted them using animation-fill-mode.

Resources