React styled-components fade in/fade out - reactjs

I am trying to build a React component to handle fading in and fading out. In the following code, if I pass out as a prop to the component, it is disaplayed as hidden before animating out. I'm trying to have it fade in by default, then fade out when I pass in the out prop. Anyone see a solution to this problem?
import React from 'react';
import styled, { keyframes } from 'styled-components';
const fadeIn = keyframes`
from {
transform: scale(.25);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
`;
const fadeOut = keyframes`
from {
transform: scale(1);
opacity: 0;
}
to {
transform: scale(.25);
opacity: 1;
}
`;
const Fade = styled.div`
${props => props.out ?
`display: none;`
: `display: inline-block;`
}
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear infinite;
`;
function App() {
return (
<div>
<Fade><💅test></Fade>
</div>
);
}
export default App;
WebpackBin running example

The issue with your code is that you're setting the display property to none when props.out is true. That's why you're not seeing any animation, because before that can even start you've already hidden the component!
The way to do a fade out animation is to use the visibility property instead and transition that for the same amount of time as the animation takes. (see this old SO answer)
Something like this should solve your issues:
const Fade = styled.default.div`
display: inline-block;
visibility: ${props => props.out ? 'hidden' : 'visible'};
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear;
transition: visibility 1s linear;
`;
const fadeIn = styled.keyframes`
from {
transform: scale(.25);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
`;
const fadeOut = styled.keyframes`
from {
transform: scale(1);
opacity: 1;
}
to {
transform: scale(.25);
opacity: 0;
}
`;
const Fade = styled.default.div`
display: inline-block;
visibility: ${props => props.out ? 'hidden' : 'visible'};
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear;
transition: visibility 1s linear;
`;
class App extends React.Component {
constructor() {
super()
this.state = {
visible: true,
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
visible: false,
})
}, 1000)
}
render() {
return (
<div>
<Fade out={!this.state.visible}><💅test></Fade>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="root" />
Note: Your fadeOut animation also went from 0 to 1 opacity, instead of the other way around. I've fixed that in the snippet too.

Related

How to loop a Draggable slider in React

I have a draggable horizontal slider in my current project and I would like to setting up it also to loop continuously. By loop continuously I mean it should respond to the process of showing images one after another when dragging? Right now, I do have only 3 images in my slider and when I drag slider to the left, slider with its 3rd image and a blank white space starts showing just after. Here at this point I want images to get start again continuously from the very beginning i.e. from the 1st image with aim to cover the white blank space.
Apart, one error I'm getting with my existing code is that when I start to drag slider to right side, suddenly a scroll comes up on browser and keep going in never ending state. By never ending state, I mean it still remain on screen when I drag all my 3 images fully in right direction.
So these are the two things I want to apply and want to resolve in my current project. I'm sharing my code below.
src > Routes > Home > Components > Carousel > Components > SliderDataItems > index.js
import React, { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { ZoomInOutlined } from '#ant-design/icons'
import { Images } from '../../../../../../Shared/Assets';
import ImagesIcon from '../../../../../../Components/Cells/ImagesIcon'
gsap.registerPlugin(Draggable);
const pictures = [
{
img: Images.xgallery1,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery2,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery4,
icon: <ZoomInOutlined />
},
];
const Slide = ({ img, icon }) => {
return (
<div className="slide">
<div className="image">
<ImagesIcon src={img} />
<div className="icon">
{icon}
</div>
</div>
</div>
);
};
export const Slider = () => {
const sliderRef = useRef(null);
useEffect(() => {
Draggable.create(sliderRef.current, {
type: "x"
});
}, []);
return (
<div className="slider" ref={sliderRef}>
{pictures.map((item, index) => {
return (
<Slide key={index} img={item.img} icon={item.icon} />
);
})}
</div>
);
};
export default Slider;
src > Routes > Home > Components > Carousel > style.scss
.slider {
display: flex;
cursor: unset !important;
overflow: hidden !important;
.slide {
.image {
position: relative;
img {
width: 100% !important;
height: auto !important;
object-fit: cover;
}
.icon {
transition: 0.5s ease;
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
text-align: center;
span {
svg {
font-size: 30px;
color: #fff;
}
}
}
}
}
.image:hover .icon {
opacity: 1;
}
}
.image:after {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(211, 208, 208, 0.6);
opacity: 0;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.image:hover:after {
opacity: 1;
}
Here's the link of demo (kindly see just above the footer section) for your reference.
Thank you for any help.
For draggle Slider there is a very lightweight JS Carousel package - siema
It is a great, lightweight carousel that is made with JS. There are also other packages built on top of this purely made for React.
In your case, I would offer to try out react-siema.
With it, you can simply use the carousel like that and it will be draggable by default. Plus, no need to load any css.

React useState with className and styled components hover out

I want to implement an animation both on the hover in and out. For hover in I use a normal css :hover. For hover out I tried to make a default state outside of the :hover but this means that when the page loads it already applies it. This is what I tried.
const Container = styled.div`
animation-name: move-card-down;
animation-duration: 0.5s;
animation-fill-mode: forwards;
:hover {
animation-name: move-card-up;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
#keyframes move-card-up {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-1rem);
}
}
#keyframes move-card-down {
0% {
transform: translateY(-1rem);
}
100% {
transform: translateY(0);
}
}
`
So I figured I could instead apply a class to the div whenever a user hovers over it for the first time instead on a page load.
const [hover, setHover] = useState(false);
const onMouseOverCard = (): void => {
setHover(true);
};
const hovered = true === hover ? 'hovered' : '';
<Container onMouseOver={() => onMouseOverCard()} className={`${hovered}`}>
</Container>
const Container = styled.div`
.hovered {
animation-name: move-card-down;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
`
It applies my class to the section but it doesn't load the styles for it. Can someone tell me why this is?
I'm not sure about the transition part, but the toggling of classes should work fine, example: https://codesandbox.io/s/silent-butterfly-qocvd?file=/src/App.js:0-291
import { useState } from "react";
import styled from "styled-components";
import "./styles.css";
const MyDiv = styled.div`
color: red;
&.foobar {
outline: dashed;
}
`;
export default function App() {
const [klass, setKlass] = useState("");
return (
<MyDiv
className={klass}
onMouseEnter={() => setKlass("foobar")}
onMouseLeave={() => setKlass("")}
>
<h1>Hello CodeSandbox</h1>
</MyDiv>
);
}

How do I use React's CSSTransitionGroup to fade-in/out my alert message?

I'm trying to build a React 16.13.0 Flash component, that I would like to fade-in and fade-out an alert message (for instance, to tell the user something has saved successfully). I'm using the CSSTransitionGroup to try and do this. I built this Flash component
import React, { useEffect, useState } from "react";
import Bus from "../Utils/Bus";
import { CSSTransition } from "react-transition-group";
import "./index.css";
export const Flash = () => {
let [visibility, setVisibility] = useState(false);
let [message, setMessage] = useState("");
let [type, setType] = useState("");
useEffect(() => {
Bus.addListener("flash", ({ message, type }) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
}, []);
useEffect(() => {
if (document.querySelector(".close") !== null) {
document
.querySelector(".close")
.addEventListener("click", () => setVisibility(false));
}
});
return (
visibility && (
<CSSTransition in={visibility} timeout={300} classNames="sample">
<div className={`alert alert-${type}`}>
<span className="close">
<strong>X</strong>
</span>
<p>{message}</p>
</div>
</CSSTransition>
)
);
};
and am using the following CSS ...
.alert {
color: white;
border-radius: 10px;
position: absolute;
top: 50px;
padding: 20px;
width: 100%;
display: flex;
align-items: center;
z-index: 1111;
}
.alert p {
margin: 0;
}
.alert-error {
background: lightcoral;
}
.alert-success {
background: lightgreen;
}
.close {
color: white;
cursor: pointer;
margin-right: 10px;
}
.sample-enter {
opacity: 0;
}
.sample-enter-active {
opacity: 0.5;
transition: opacity 300ms;
}
.sample-enter-done {
opacity: 1;
}
.sample-exit {
opacity: 1;
}
.sample-exit-active {
opacity: 0.5;
transition: opacity 300ms;
}
.sample-exit-done {
opacity: 0;
}
However, the message appears without a fade-in and then disappears without a fade-out. I'm not sure what else I'm doing wrong or need to add.
I'm not sure if the key is at the -appear css selector but the following one works on my component (styled with class fade)
.fade-appear {
opacity: 0;
z-index: 1;
}
.fade-appear.fade-appear-active {
opacity: 1;
transition: opacity 1000ms linear;
}
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 5000ms linear 5000ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit.fade-exit-active {
opacity: 0;
transition: opacity 5000ms linear;
}
.fade-exit-done {
opacity: 0;
}
using version 4.3.0
My exact component is this
<CSSTransition in={true}
appear={true}
timeout={500}
classNames="fade"
unmountOnExit>
Remove visibility && from your return statement:
return (
<CSSTransition in={visibility} timeout={300} classNames="sample">
<div className={`alert alert-${type}`}>
<span className="close">
<strong>X</strong>
</span>
<p>{message}</p>
</div>
</CSSTransition>
)
The in prop controls the enter / exit transition. Exit transition is triggered only when in is false. There is no scenario in your code when this happens.

React CSS transition not working

I have a tic tac toe board, and want to add a transition an element on a click.
For demo, I have a board with some cells filled. On click on the board, a new cell is filled. I wanted this new cell to come with a transition.
I have followed the React Transition Group doc. On click on any cell of the board, a circle appears on 2rd row 3rd col. This should appear with a Fade-in transition. I have added CSSTransition group, but transition is not happening. Something is amiss.
Following is the codesandbox:
Some sample code from the snippet
tic-tac-toe-board.js
import React from "react";
import "./tictactoe.css";
import { CSSTransition } from 'react-transition-group';
const Fade = ({ children, ...props }) => (
<CSSTransition {...props} timeout={1000} classNames="fade">
{children}
</CSSTransition>
);
class TicTacToeBoard extends React.Component {
constructor(props) {
super(props);
this.state = {
board: [
[0, 1, 0],
[2, 0, 0],
[0, 0, 0]
]
};
}
handleBoardClick = () => {
let newBoard = [
[...this.state.board[0]],
[...this.state.board[1]],
[...this.state.board[2]]
];
newBoard[1][2] = 1;
this.setState({board: newBoard});
}
render() {
return (
<div className="board" onClick={this.handleBoardClick}>
{this.state.board.map((row, rowIndex) =>
row.map((cell, colIndex) => {
return (
<div className="cell" key={`${rowIndex}-${colIndex}`}>
{cell === 1 && (
<Fade in={true}>
<img
src="https://image.ibb.co/nDDDuw/circle_outline.png"
alt=""
className="cell-content"
/>
</Fade>
)}
{cell === 2 && (
<Fade in={true}>
<img
src="https://image.ibb.co/jY0nMb/close.png"
alt=""
className="cell-content"
/>
</Fade>
)}
</div>
);
})
)}
</div>
);
}
}
export default TicTacToeBoard;
tictactoe.css
* {
box-sizing: border-box;
}
.board {
width: 90vmin;
height: 90vmin;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
margin-left: auto;
margin-right: auto;
box-shadow: 5px 5px 10px #aaa;
}
.cell {
width: 30vmin;
height: 30vmin;
border: 1px solid lightgrey;
}
.cell-content {
width: 80%;
height: 80%;
margin: 10%;
}
.fade-enter {
opacity: 0.01;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 1000ms ease-in;
}
I had a similar issue but solved when I enclosed the CSSTransition inside the TransitionGroup
const Fade = ({ children, ...props }) => (
<TransitionGroup>
<CSSTransition {...props} timeout={1000} classNames="fade">
{children}
</CSSTransition>
</TransitionGroup>);
And update the style something like this
.fade-enter {
opacity: 0.01;
}
.fade-enter-active {
opacity: 1;
transition: opacity 1000ms ease-in;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0.01;
transition: opacity 1s ease-out;
}

simple css animation not working on dynamic reactjs element

Check the snippet at codepen http://codepen.io/anon/pen/EZJjNO
Click on Add button, it will add another item but its appearing immediately without any fade effect.
JS:
class App extends React.Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
items: [1,2,3]
}
}
addItem() {
var items = this.state.items;
items.push(4);
this.setState({
items: items
})
}
render() {
return (
<div className="App">
{
this.state.items.map(function(i) {
return <div className="item fade">Testing</div>
})
}
<button onClick={this.addItem}>Add</button>
</div>
);
}
}
React.render(<App />, document.body);
CSS:
.App {
background-color: rgba(0,0,0, 0.5);
text-align: center;
height: 100vh;
}
div.item {
border: 1px solid #ccc;
padding: 10px;
background: #123456;
color: #fff;
opacity: 0;
transition: all 0.4s ease-out;
}
.fade {
opacity: 1 !important;
}
Since the fade class is added by default, you don't get the transition effect. If you open your browser's developer tools and remove the class, you'll see it fade away nicely.
There's a few ways to get what you want, but I'd just use a keyframe CSS animation like so:
.fade {
animation: 0.4s ease-out fadeIn 1;
}
#keyframes fadeIn {
0% {
opacity: 0;
visibility: hidden;
}
100% {
opacity: 1;
visibility: visible;
}
}
Here's a fork of your code pen showing how it works :)

Resources