How to make top to bottom animation with react styled components - css

I'm trying to recreate the slider on Gatsby's website but using the styled components library instead of the emotion library they used. The issue is the animation doesn't do anything and the list of strings I pass into the component get concatenated together.
Gatsbyjs.org
Code for their slider component
My slider.js:
import React from "react"
import styled, { keyframes } from "styled-components"
const topToBottom = keyframes`
0%: {
opacity: 0;
}
6%: {
opacity: 0;
transform: translateY(-30px);
}
10%: {
opacity: 1;
transform: translateY(0px);
}
25%: {
opacity: 1;
transform: translateY(0px);
}
29%: {
opacity: 0;
transform: translateY(30px);
}
80%: {
opacity: 0;
}
100%: {
opacity: 0;
}
`;
const SliderDiv = styled.div`
display: inline;
& span: {
animation: ${topToBottom} 10s linear infinite 0s;
opacity: 0;
position: absolute;
:nth-child(2) {
animation-delay: 2.5s;
}
:nth-child(3) {
animation-delay: 5s;
}
:nth-child(4) {
animation-delay: 7.5s;
}
}
`;
const Slider = ({ items, color }) => (
<SliderDiv>
{items.map(item => (
<span key={item} css={{ color }}>
{item}
</span>
))}
</SliderDiv>
)
export default Slider
Result:

Your code works as expected if you remove the : from the css code inside the Styled Component:
span {
// not
span : {
and
0% {
// not
0% : {
I've tested the code in a Codesandbox
import React from "react";
import styled, { keyframes } from "styled-components";
const topToBottom = keyframes`
0% {
opacity: 0;
}
6% {
opacity: 0;
transform: translateY(-30px);
}
10% {
opacity: 1;
transform: translateY(0px);
}
25% {
opacity: 1;
transform: translateY(0px);
}
29% {
opacity: 0;
transform: translateY(30px);
}
80% {
opacity: 0;
}
100% {
opacity: 0;
}
`;
const SliderDiv = styled.div`
display: inline;
& span {
animation: ${topToBottom} 10s linear infinite 0s;
opacity: 0;
position: absolute;
:nth-child(2) {
animation-delay: 2.5s;
}
:nth-child(3) {
animation-delay: 5s;
}
:nth-child(4) {
animation-delay: 7.5s;
}
}
`;
const Slider = ({ items, color }) => (
<SliderDiv>
{items.map(item => (
<span key={item} css={{ color }}>
{item}
</span>
))}
</SliderDiv>
);
export default Slider;
I know I looked for this errors a couple of times :)
To make it more clear, in styled-components you write css, not css-in-js
Hope it helps!

Related

how to disable animation when rendering a page?

It is necessary that when the button is pressed, there is an animation of the appearance and hiding of the text. I wrote the following code, but when the page renders, the text hide animation is triggered.
Can I turn off the animation that fires when the page is rendered? Sample code below.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
let [vision, setVision] = useState(true);
let hangler = () => {
setVision(!vision);
};
return (
<div className="App">
<h1 className={vision ? "Text hidden" : "Text shown"}>Hello World</h1>
<button onClick={hangler}>click</button>
</div>
);
}
Css:
.App {
font-family: sans-serif;
text-align: center;
}
.Text {
color: blue;
}
.hidden {
animation: fadeOut ease 1.5s;
animation-fill-mode: forwards;
}
.shown {
animation: fadeIn ease 1.5s;
animation-fill-mode: forwards;
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
visibility: hidden;
}
}
This code is at this link
Your classes are inverted, it should be vision ? "Text shown" : "Text hidden".
This will still animate the header to fade in. If it's ok then you are done.
However, if you don't want the initial animation. You can set animation-duration: 0s; to short-circuit it and remove the property after the load but this is not elegant.
For something that simple I would use transitions instead.
.hidden {
opacity: 0;
visibility: hidden;
transition: opacity 1.5s, visibility 1.5s;
}
.shown {
opacity: 1;
visibility: visible;
transition: opacity 1.5s, visibility 1.5s;
}

Styled components keyframe animation not applying on React component

I have the following react component
import React from "react"
import styled from "styled-components"
import { moveInLeft } from '../../styles/Animations'
const StyledHeadingOne = styled.h1`
font-size: 6rem;
text-transform: uppercase;
color: #fff;
font-weight: 300;
letter-spacing: 0.5rem;
animation: ${moveInLeft} 1s ease-in-out .3s both;
`
export default function HeadingOne({ children }) {
return <StyledHeadingOne>{children}</StyledHeadingOne>
}
I add my animation moveInLeft which I import from my file:
import { keyframes} from "styled-components"
export const moveInLeft = keyframes`
#keyframes moveInLeft {
0% {
opacity: 0;
transform: translateX(-10rem);
}
100% {
opacity: 1;
transform: translate(0);
}
}
`
But for some reason the animation does not apply. Can anyone figure out why?
I believe you need to remove the #keyframes from your animation as you are already using the styled components keyframe helper.
const moveInLeft = keyframes`
0% {
opacity: 0;
transform: translateX(-10rem);
}
100% {
opacity: 1;
transform: translate(0);
}
`
https://codepen.io/A-G/pen/mdVoxPo

How to reverse animation when prop is false styled components

I am passing a open prop to styled component to create animations for the hamburger icon.
This is the code
const StyledBurger = styled.button`
display: flex;
flex-direction: column;
justify-content: center;
border: 0;
background-color: ${colors.cobaltBlue};
border-radius: 2.7px;
cursor: pointer;
div {
width: 27px;
height: 3px;
margin: 1.5px;
transition: all 0.2s linear;
border-radius: 1.4px;
background-color: ${colors.white};
:first-child {
${({ open }) => open && firstOpenAnimation};
}
:nth-child(2) {
opacity: ${({ open }) => (open ? '0' : '1')};
}
:nth-child(3) {
${({ open }) => open && seconOpenAnimation}
}
}
`;
const firstOpenKeyframe = keyframes`
50% {
transform: translateY(6px) rotate(0);
}
100% {
transform: translateY(6px) rotate(45deg);
}
`;
const secondOpenKeyframe = keyframes`
50% {
transform: translateY(-6px) rotate(0);
}
100% {
transform: translateY(-6px) rotate(-45deg);
}
`;
const firstCloseKeyFrame = keyframes`
50% {
transform:translateY(0) rotate(-45deg);
}
100% {
transform:translateY(-6px) rotate(-45deg) ;
}
`;
const firstOpenAnimation = css`
animation: 0.3s linear ${firstOpenKeyframe} forwards;
`;
const seconOpenAnimation = css`
animation: 0.3s linear ${secondOpenKeyframe} forwards;
`;
const firstCloseAnimation = css`
animation: 0.3s linear ${firstCloseKeyFrame} forwards;
`;
export default StyledBurger;
Basically what I want is if the menu is not open to reverse the animation that was created after the first click.
I tried doing a conditional render of animation keyframe based on the prop open but what happens is when the page loads it immediately creates the animation of not is opened because it satisfies false.
What can I do to fix this and create the opposite animation when unClicked
Make few corrections and it should all work properly.
use state to toggle open and send it as prop to your styled component
use ternary for animation (not just &&) ${({ open }) => (open ? firstOpenAnimation : firstCloseAnimation)}.
implement missing close second animation
working copy of your code is here
Working code snippet
const StyledBurger = styled.button`
display: flex;
flex-direction: column;
justify-content: center;
border: 0;
background-color: red;
border-radius: 2.7px;
cursor: pointer;
height: 30px;
div {
width: 27px;
height: 3px;
margin: 1.5px;
transition: all 0.2s linear;
border-radius: 1.4px;
background-color: white;
:first-child {
${({ open }) =>
open !== null && (open ? firstOpenAnimation : firstCloseAnimation)}
}
:nth-child(2) {
opacity: ${({ open }) => (open ? "0" : "1")};
}
:nth-child(3) {
${({ open }) =>
open !== null && (open ? seconOpenAnimation : secondCloseAnimation)}
}
}
`;
const firstOpenKeyframe = keyframes`
50% {
transform: translateY(6px) rotate(0);
}
100% {
transform: translateY(6px) rotate(45deg);
}
`;
const secondOpenKeyframe = keyframes`
50% {
transform: translateY(-6px) rotate(0);
}
100% {
transform: translateY(-6px) rotate(-45deg);
}
`;
const firstCloseKeyFrame = keyframes`
50% {
transform:translateY(0) rotate(-45deg);
}
100% {
transform:translateY(0) rotate(0) ;
}
`;
const secondCloseKeyFrame = keyframes`
50% {
transform:translateY(0) rotate(-45deg);
}
100% {
transform:translateY(0) rotate(0) ;
}
`;
const firstOpenAnimation = css`
animation: 0.3s linear ${firstOpenKeyframe} forwards;
`;
const seconOpenAnimation = css`
animation: 0.3s linear ${secondOpenKeyframe} forwards;
`;
const secondCloseAnimation = css`
animation: 0.3s linear ${secondCloseKeyFrame} forwards;
`;
const firstCloseAnimation = css`
animation: 0.3s linear ${firstCloseKeyFrame} forwards;
`;
export default function App() {
const [open, setOpen] = useState(null);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<StyledBurger onClick={() => setOpen(prev => !prev)} open={open}>
<div />
<div />
<div />
</StyledBurger>
</div>
);
}

How to animate when user scrolls to view

I have started building a website using ReactJS, I want to animate div elements into view when the view in scrolled to.
I have used CSS keyframes to add animations like so;
.animateMe {
animation: IntroWelcomeImageAnimation 3s 0.2s forwards cubic-bezier(0.2, 0.8, 0.2, 1);
}
#keyframes IntroLeftAnimation {
0% {
opacity: 0;
transform: translateX(-200px)
}
100% {
opacity: 1;
transform: translateY(0px)
}
}
#keyframes IntroRightAnimation {
0% {
opacity: 0;
transform: translateX(200px)
}
100% {
opacity: 1;
transform: translateY(0px)
}
}
#keyframes IntroWelcomeImageAnimation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
JS File
import React from 'react'
import '../../layout/intro.css';
const IntroPage = () => {
return (
<section className="Intro-Page">
<div className="animateme">
</div>
</section>
)
}
export default IntroPage
However the problem is that the animations only occur when the page is loaded as that is when the elements are too. How can I have it that they transition into view on scroll without using JQuery ?
Look at this library https://www.react-reveal.com/
there are many animations that can be triggered on scroll

page transitions without React-Router

This should be so simple, but I've been breaking my head over this for days. I'm trying to animate my page transitions. The problem is the docs SUCK. I've followed them over and over and tried every which way, but can't get it to work.
What I want to do is slide my pages gracefully either right or left, and fade the one that is unmounting gracefully out behind it. Simple right? I am NOT using React Router for my pages.
I've tried a variety of solutions for this, but the problem seems to be on the unmounting. When the page is replaced, the existing page gets unmounted before it can transition out. I'm posting my attempt with react-transition-group, though at this point, I'll accept any other solution that works. I'm not sure react-transition-group is being actively maintained actually, because there are numerous other postings for help with 0 responses.
So on my app container I want to put something like this:
<App>
<PageSlider>
<Page key={uniqueId} /> <==this will be "swapped" through (Redux) props
</PageSlider>
So, from what I've read, I have to use a TransitionGroup container as my PageSlider for this, so that it will manage the entering and exiting of my page. So here goes:
class PageSlider extends Component {
constructor(props) {
super(props);
}
render() {
return (
<TransitionGroup
component="div"
id="page-slider"
childFactory={child => React.cloneElement(child,
{classNames: `page-${this.props.fromDir}`, timeout: 500}
)}
>
{this.props.children}
</TransitionGroup>
);
}
}
I also read I need to do a "child Factory" to enable the exiting stuff. There was absolutely no example of this I could find in the docs. Since the pages will come from different directions, I will pass to this the direction from which I want to slide the page, which will tell the page what class it gets.
Now, as for the page itself, I have wrapped it in a CSSTransition like so. There were no good examples in the docs of how this all gets passed down, so I'm really confused what to do here:
class Page extends Component {
constructor(props) {
super(props);
}
render() {
return (
<CSSTransition> <==????????
{this.props.children} Do props get passed down?
</CSSTransition> Which ones?
); Does "in" get taken care of?
}
}
And just to finish the styles will be applied in CSS something like this:
.page {
display: flex;
flex-direction: column;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
-webkit-transition: all 500ms ease-in-out;
transition: all 500ms ;
}
//go from this
.page-right-enter {
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
//to this
.page-right-enter-active {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
//exiting go from this
.page-right-exit {
opacity: 1;
}
//to this
.page-right-exit-active {
opacity: 0;
}
All of these components will be connected through Redux so they know when a new page has been triggered and which direction has been called.
Can someone PLEEEEASE help me on this? I've literally spent days and tried every library out there. I'm not wedded to react-transition-group! Any library that works on the unmount I'll try. Why is this not easier?
OK. Well, I struggled with this for WAAAAY too long. I finally dumped react-transition-group, and went pure CSS. Here's my solution.
PageSlider.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
require('./transitions.scss');
const BlankPage = (props) => <div className="placeholder"></div>;
class PageSlider extends Component {
constructor(props) {
super(props);
this.state = {
nextRoute: props.page,
pages: {
A: {key: 'A', component: BlankPage, className: 'placeholder'},
B: {key: 'B', component: BlankPage, className: 'placeholder'},
},
currentPage: 'A'
};
}
componentDidMount() {
//start initial animation of incoming
let B = {key: 'b', component: this.state.nextRoute, className: 'slideFromRight'}; //new one
let A = Object.assign({}, this.state.pages.A, {className: 'slideOutLeft'}); //exiting
this.setState({pages: {A: A, B: B}, currentPage: 'B'});
}
componentWillReceiveProps(nextProps) {
if (nextProps.page != this.state.nextRoute) {
this.transition(nextProps.page, nextProps.fromDir);
}
}
transition = (Page, fromDir) => {
if (this.state.nextRoute != Page) {
let leavingClass, enteringClass;
let pages = Object.assign({}, this.state.pages);
const current = this.state.currentPage;
const next = (current == 'A' ? 'B' : 'A');
if (fromDir == "right") {
enteringClass = 'slideFromRight';
leavingClass = 'slideOutLeft';
} else {
enteringClass = 'slideFromLeft';
leavingClass = 'slideOutRight';
}
pages[next] = {key: 'unique', component: Page, className: enteringClass};
pages[current].className = leavingClass;
this.setState({pages: pages, nextRoute: Page, currentPage: next});
}
}
render() {
return (
<div id="container" style={{
position: 'relative',
minHeight: '100vh',
overflow: 'hidden'
}}>
{React.createElement('div', {key: 'A', className: this.state.pages.A.className}, <this.state.pages.A.component />)}
{React.createElement('div', {key: 'B', className: this.state.pages.B.className} , <this.state.pages.B.component />)}
</div>
);
}
}
PageSlider.propTypes = {
page: PropTypes.func.isRequired,
fromDir: PropTypes.string.isRequired
};
export default PageSlider;
transition.scss
.placeholder {
position: absolute;
left: 0;
width: 100vw;
height: 100vh;
background: transparent;
-webkit-animation: slideoutleft 0.5s forwards;
-webkit-animation-delay: 10;
animation: slideoutleft 0.5s forwards;
animation-delay: 10;
}
.slideFromLeft {
position: absolute;
left: -100vw;
width: 100vw;
height: 100vh;
-webkit-animation: slidein 0.5s forwards;
-webkit-animation-delay: 10;
animation: slidein 0.5s forwards;
animation-delay: 10;
}
.slideFromRight {
position: absolute;
left: 100vw;
width: 100vw;
height: 100vh;
-webkit-animation: slidein 0.5s forwards;
-webkit-animation-delay: 10;
animation: slidein 0.5s forwards;
animation-delay: 10;;
}
.slideOutLeft {
position: absolute;
left: 0;
width: 100vw;
height: 100vh;
-webkit-animation: slideoutleft 0.5s forwards;
-webkit-animation-delay: 10;
animation: slideoutleft 0.5s forwards;
animation-delay: 10;
}
.slideOutRight {
position: absolute;
left: 0;
width: 100vw;
height: 100vh;
-webkit-animation: slideoutright 0.5s forwards;
-webkit-animation-delay: 10;
animation: slideoutright 0.5s forwards;
animation-delay: 10;
}
#-webkit-keyframes slidein {
100% { left: 0; }
}
#keyframes slidein {
100% { left: 0; }
}
#-webkit-keyframes slideoutleft {
100% { left: -100vw; opacity: 0 }
}
#keyframes slideoutleft {
100% { left: -100vw; opacity: 0}
}
#-webkit-keyframes slideoutright {
100% { left: 100vw; opacity: 0}
}
#keyframes slideoutright {
100% { left: 100vw; opacity: 0}
}
Passing in the next component, which is my react Page component, called like so:
app.js
<div id="app">
<PageSlider page={this.state.nextRoute} fromDir={this.state.fromDir}/>
</div>

Resources