Components not animating with react-transition-group, just updates instantly? - javascript

I'm trying to animate a sidebar component following the first section on this page. When I follow this the component doesn't animate, but simply mounts/unmounts.
The component SidePage is as follows:
import React from "react"
import { TransitionGroup, CSSTransition } from "react-transition-group"
import "./sidePage.css"
class SidePage extends React.Component {
componentWillMount() {
console.log("will mount")
}
componentDidMount() {
console.log("did mount")
}
componentWillUnmount() {
console.log("will unmount")
}
render() {
const { content, sidePageOpen } = this.props
return (
<TransitionGroup component={null}>
{sidePageOpen && (
<CSSTransition key={content.id} classNames="sidepage" timeout={2000}>
<div
key={content.id}
className="sidepage"
dangerouslySetInnerHTML={{ __html: content.html }}
/>
</CSSTransition>
)}
</TransitionGroup>
)
}
}
export default SidePage
and the css file:
.sidepage-enter {
opacity: 0;
}
.sidepage-enter-active {
opacity: 1;
transition: all 2s;
}
.sidepage-exit {
opacity: 1;
}
.sidepage-exit-active {
opacity: 0;
transition: all 2s;
}
.sidepage {
background: white;
padding: 10px;
height: 100%;
width: 90vw;
position: absolute;
top: 0;
right: 0;
z-index: 10;
opacity: 0.4;
transition: all 0.6s;
}
Basic stuff I think — the sidePageOpen is a boolean state passed down, I have a button on another page that toggles this state. If anyone has any ideas/suggestions that would be brilliant and appreciated.

Remove the opacity property from sidepage class.
.sidepage {
background: white;
padding: 10px;
height: 100%;
width: 90vw;
position: absolute;
top: 0;
right: 0;
z-index: 10;
opacity: 0.4; // remove me
transition: all 0.6s;
}
The element get's added with a class of sidepage which has a opacity of 0.4, thats whats breaking the animation. Working demo here

Eventually found the solution — I had a styled <Wrapper> div created using emotion.sh styled components, I was using this to contain all of my content, not sure why but this didn't allow any animations — changing this to a simple <div> seemed to fix it.
Edit: Probably because it was recreating the Wrapper component on every state change.

Related

Update contents of a div element with animation styling

I'm building a personal portfolio webpage with React and I'm trying to update the content of the div element but when the elements are updated, I want to involve styling, (like the old content to exit on the left and the new content to enter from the left) Sorry for my bad English.
This is the part I'm trying to update and add styling
I've tried to use the componentDidMount method with a function that changes the heading but I don't know how to add animations/styling...
export default class Home extends Component {
skills_section_update = () => {
let heading = document.getElementById('heading');
const headings = [
{
head: 'Web Development'
},
{
head: 'Game Development'
}
];
const update_head = (index) => {
heading.innerText = headings[index].head;
};
update_head(1);
}
componentDidMount = () => {
setInterval(this.skills_section_update, 2000);
};
I'm new to React so any help would be much appreciated.
:)
I don't know if there are special methods to do this using React but I know that you can't animate the content of a DOM element using CSS. What I would recommend is to add the updated content in another DOM element next to your current div and then animate the two to make a nice transition.
You could, for example, do something like that:
.container {
width: 400px;
height: 400px;
border: #000 solid 1px;
position: relative;
overflow: hidden;
}
.content {
width: 100%;
height: 100%;
transition: all .25s ease-in-out;
}
.container:hover .content {
transform: translateY(-100%);
transition: all .25s ease-in-out;
}
.original {
background-color: wheat;
}
.next {
background-color: teal;
}
<div class="container">
<div class="content original">
Content 1
</div>
<div class="content next">
New content
</div>
</div>

Why is my Swiper render buggy at the first time render using React

I'm using React and trying to fetch some of my anime into the home banner using Swiper
I don't know why when I refresh my page, it'll only render at half of the swiper.
Here's how it display:
However, if I press the next or back button, it'll display normally again.
Here's my code in my Home Component:
import { useState, useEffect } from "react"
import ReactHlsPlayer from 'react-hls-player';
import './home.css'
import { supabase } from '../../supabaseClient'
import { Swiper, SwiperSlide } from 'swiper/react'
import SwiperCore, { Pagination,Navigation } from 'swiper';
import 'swiper/css'
SwiperCore.use([Pagination,Navigation]);
function Home(){
useEffect(async ()=>{
fetchAnime()
}, [])
const [animeDetail, setAnimeDetail] = useState([])
async function fetchAnime() {
const { data } = await supabase
.from ('anime')
.select ()
setAnimeDetail(data)
}
return (
<>
<div className="spacer">
</div>
<div className="home-section">
<h2>Home Page</h2>
<Swiper
centeredSlides={true}
slidesPerView={7}
spaceBetween={10}
loop={true}
pagination={false}
navigation={true} className="mySwiper">
{animeDetail.map((element, i)=>
(
<SwiperSlide key = {i}><img src={element.anime_image}/></SwiperSlide>
)
)}
</Swiper>
</div>
</>
)
}
export default Home
And here's my Home CSS, sorry if it's a bit messy, I'm still trying it here and there, but I'm stuck.
.swiper {
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
.swiper-wrapper{
width: 100%
}
.swiper-slide {
text-align: center;
font-size: 18px;
display: flex;
justify-content: center;
align-items: center;
max-width: 280px;
}
.swiper-slide img {
display: block;
box-sizing: border-box;
border: none;
max-height: 350px;
min-height: 350px;
-o-object-fit: cover;
object-fit: cover;
transition: all .3s ease;
opacity: 0.5
}
.swiper-slide-active {
transform: scale(1.2);
z-index: 2
}
.swiper-slide-active img{
transition: all .3 ease;
opacity: 1
}
And if someone figures it out, please help me a bit, I tried to whenever the item in the middle is in active, it'll pop out bigger, but I can't do it with transform: scale(number) - I have tried it (It does get bigger when it's active but it doesn't display bigger at the height, I haven't figured it out some ways at the moment)
you have to set the initialSlide prop on the Swiper element to an index of the slide in the middle. so that the slider starts from there.
Also in this case, you can set the centeredSlides prop to true as the active slide remains in the middle.
Here's a possible fix for your issue and for everyone else that comes here looking for answers.
Use conditional rendering of the Swiper component, something like this:
{animeDetail.length>0 && <Swiper
centeredSlides={true}
slidesPerView={7}
spaceBetween={10}
loop={true}
pagination={false}
navigation={true} className="mySwiper">
{animeDetail.map((element, i)=>
(
<SwiperSlide key = {i}><img src={element.anime_image}/></SwiperSlide>
)
)}
</Swiper>}

Animations stop working after building and deploying to Firebase

I'm having a problem where my animations stop working once I npm run-script build and firebase deploy my react app to Firebase hosting.
No idea why this is happening, I've added every web browser compatible keyframes.
Here's what my app looks like when ran on localhost (npm start):
And then what it looks like hosted from firebase:
It's like it can't read my keyframe animations.
Here's index.js:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose, combineReducers } from "redux";
import thunk from "redux-thunk";
import * as serviceWorker from "./serviceWorker";
import userReducer from "./store/reducers/user";
import { WithClass } from "./hoc/WithClass";
import classes from "./index.module.css";
import App from "./App";
// Icons made by Freepik from www.flaticon.com
// Reducers
const rootReducer = combineReducers({
user: userReducer,
});
// Store
const store = createStore(rootReducer, applyMiddleware(thunk));
const app = (
<Provider store={store}>
<WithClass>
<BrowserRouter>
<App />
</BrowserRouter>
</WithClass>
</Provider>
);
ReactDOM.render(app, document.getElementById("root"));
App.js:
import React from "react";
import { Route, Switch, withRouter, Redirect } from "react-router-dom";
import HomePage from "./pages/HomePage/HomePage";
import AboutPage from "./pages/AboutPage/AboutPage";
import WorkPage from "./pages/WorkPage/WorkPage";
import PhotographyPage from "./pages/PhotographyPage/PhotographyPage";
import ContactPage from "./pages/ContactPage/ContactPage";
import { WithClass } from "./hoc/WithClass";
/**
* Contains switch routing to components
*
* Called by index.js in ReactDOM.render()
*/
const App = () => {
return (
<WithClass>
<Switch>
<Route path="/about" exact component={AboutPage} />
<Route path="/work" exact component={WorkPage} />
<Route path="/photography" exact component={PhotographyPage} />
<Route path="/contact" exact component={ContactPage} />
<Route path="/" exact component={HomePage} />
<Redirect to="/" />
{/* Redirect anything other than routes specified to "/" */}
</Switch>
</WithClass>
);
};
export default withRouter(App);
HomePage.js:
import React, { useEffect } from "react";
import AnimatedSlideShowText from "../../components/UI/AnimatedSlideShowText/AnimatedSlideShowText";
import HeaderAnimated from "../../components/UI/HeaderAnimated/HeaderAnimated";
import HeaderStatic from "../../components/UI/HeaderStatic/HeaderStatic";
import SocialMediaFooter from "../../components/UI/SocialMediaFooter/SocialMediaFooter";
import { useDispatch, useSelector } from "react-redux";
import { loadedOnce } from "../../store/actions/user";
import classes from "./HomePage.module.css";
const HomePage = () => {
const dispatch = useDispatch();
const didLoadOnce = useSelector((state) => state.user.loadedOnce);
useEffect(() => {
setTimeout(() => {
dispatch(loadedOnce());
}, 2000);
}, []);
return (
<div className={classes.MainContainer}>
<div className={classes.HeaderContainer}>
{didLoadOnce ? <HeaderStatic /> : <HeaderAnimated />}
</div>
<div className={classes.BodyContainer}>
<div className={classes.NameContainer}>
<AnimatedSlideShowText tag="h1">
Christian Nicoletti
</AnimatedSlideShowText>
<AnimatedSlideShowText
tag="h2"
mainTextStyle={classes.Title}
>
Software Engineer
</AnimatedSlideShowText>
<AnimatedSlideShowText
tag="h3"
mainTextStyle={classes.School}
>
University of California, Santa Cruz graduate
</AnimatedSlideShowText>
</div>
<div className={classes.FooterContainer}>
<SocialMediaFooter />
</div>
</div>
</div>
);
};
export default HomePage;
HomePage.module.css:
.MainContainer {
width: 100vw;
height: 100vh;
min-width: 1500px;
}
.BodyContainer {
display: flex;
height: 100%;
justify-content: center;
margin-left: 20%;
flex-direction: column;
}
.NameContainer {
display: flex;
height: 250px;
width: 500px;
}
.Title {
margin-top: 60px;
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s;
}
.School {
margin-top: 120px;
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
}
.HeaderContainer {
position: absolute;
right: 100px;
}
.FooterContainer {
width: 500px;
}
AnimatedSlideShowText.js:
import React from "react";
import classes from "./AnimatedSlideShowText.module.css";
const AnimatedSlideShowText = (props) => {
const CustomTag = `${props.tag}`;
return (
<CustomTag className={`${classes.MainText} ${props.mainTextStyle}`}>
{props.children}
</CustomTag>
);
};
export default AnimatedSlideShowText;
AnimatedSlideShowText.module.css:
.MainText {
color: white;
position: absolute;
opacity: 0;
margin-left: -10%;
font-family: Calibri;
font-weight: 300;
-webkit-animation: slide 0.5s forwards;
animation: slide 0.5s forwards;
}
#-o-keyframes slide {
100% {
margin-left: 0%;
opacity: 100%;
}
}
#-ms-keyframes slide {
100% {
margin-left: 0%;
opacity: 100%;
}
}
#-moz-keyframes slide {
100% {
margin-left: 0%;
opacity: 100%;
}
}
#-webkit-keyframes slide {
100% {
margin-left: 0%;
opacity: 100%;
}
}
#keyframes slide {
100% {
margin-left: 0%;
opacity: 100%;
}
}
#-webkit-keyframes show {
/* Chrome, Safari */
0% {
width: 100%;
}
100% {
width: 0%;
}
}
#-moz-keyframes show {
/* FF */
0% {
width: 100%;
}
100% {
width: 0%;
}
}
#-ms-keyframes show {
/* IE10 */
0% {
width: 100%;
}
100% {
width: 0%;
}
}
#-o-keyframes show {
/* Opera */
0% {
width: 100%;
}
100% {
width: 0%;
}
}
#keyframes show {
0% {
width: 100%;
}
100% {
width: 0%;
}
}
I'd add more component source code, but I think AnimatedSlideShowText is all that is needed to understand the problem.
So again, I'm just trying to get my animations to work when built and deployed. I'm not sure why they would stop working when built and deployed.
Is it possible that using module.css has an impact on animations when built/deployed? Any help would be super appreciated, and if you need more source code let me know.
I've had a similar problem before but I was using a CSS framework. The problem was with the build cache on my hosting provider. When using create-react-app (that uses Webpack), on the build stage happens the so called 'tree-shake'. It gets rid of unused styles, classes etc from your modules.
A module that works locally might not work on production because it was rid off on your first build and is then not used on the new build due to the build cache.
I don't know if it's the solution to your problem but I suggest you check it out since it worked for me in the past.
Ok so! I managed to fix it. I thought this was a lot more complex than it actually was.
Short answer: I had to change all my opacity: 100% to opacity: 1 and suddenly everything appeared and worked correctly.
Long answer: I had to dabble in the console for a bit, and realized that all my components and text were there, but just not showing up. I played with the animations by disabling and re-enabling, and stuff would flicker for a second. I realized that it was rendering the opacity as: opacity: 1% instead of opacity: 100%. Apparently, when building with npm run-script build, it acts as if 100% has trailing zeros(??).
Anyway, I appreciate the help, and everything works perfectly now.

React CSS Transition Inconsistency Through Updates

The below snippet has four boxes. The purpose is that these boxes order will be shuffled and a transition animation occurs as they go to a new location. Each box's key corresponds with a color value from the source array in useState. Each update via the shuffle button, the source array's values are shuffled. Then I map through the array in the return function. I set 2 classNames for each box. One classname corresponds with the index and is for positioning. The other classname corresponds with the source array value and is always in unison with the key for that box.
My issue is that react seems to randomly be deciding what keys to pay attention to and reconcile, and what keys to disregard and just remount those elements. You can see here, some elements properly transition while others just jump to their target location. I'm at a loss as to why this is occuring. Can someone help?
EDIT: I don't believe this is a reconcile issue with respect to unwanted remounting. React is properly respecting the keys and not remounting any. So the issue is with how React handles CSS transition classes added during updates. Some transitions work and others don't. It may just be a limitation of the engine, but if anyone has any further incite please share.
const {useState} = React;
function App() {
const [state, setState] = useState(['Red', 'Green', 'Blue', 'Black'])
function handleShuffle() {
const newState = _.shuffle(state)
setState(newState)
}
return (
<div className="App">
{state.map((sourceValue, index) => {
return (
<div className={
'box positionAt' + index + ' sourceValue' + sourceValue
}
key={sourceValue} ></div>
)
})}
<button id="shuffle" onClick={handleShuffle}> shuffle < /button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( <
App / > ,
rootElement
);
.App {
position: relative;
width: 200px;
height: 200px;
background-color: gray;
}
.box {
width: 25px;
height: 25px;
position: absolute;
transition: transform 1s;
}
.positionAt0 {
transform: translate(0px, 0px);
}
.positionAt1 {
transform: translate(175px, 0px);
}
.positionAt2 {
transform: translate(0px, 175px);
}
.positionAt3 {
transform: translate(175px, 175px);
}
.sourceValueGreen {
background-color: green;
}
.sourceValueBlue {
background-color: blue;
}
.sourceValueRed {
background-color: red;
}
.sourceValueBlack {
background-color: black;
}
#shuffle {
position: absolute;
top: 0px;
left: 75px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<div id="root"></div>
Took me a while to figure it out, because setting the correct keys for the boxes seemed to be just the right thing.
I verified with the developer tools that the keys work, by inspecting a box and storing it in a variable (b = $0), shuffling, re-inspecting the box with the same key (color) and comparing it with the stored node ($0 === b, was true). So The DOM nodes for each key are stable.
But it's not sufficient for CSS transitions because the way browsers are changing the order of elements in the DOM.
You can see it here in a minimized example for efficiently reordering elements in the DOM (I assume that React does similar things internally when elements have to be reordered):
function reorder() {
const list = document.querySelector("ul");
list.appendChild(list.firstElementChild);
}
<ul>
<li>List-item #1</li>
<li>List-item #2</li>
</ul>
<button onclick="reorder()">reorder!</button>
Run the example and set a DOM breakpoint on the resulting <ul> DOM node for "Subtree modification", see screenshot.
If you click on "reorder!", the browser breaks first on the removal of a <li>. If you continue, and immediately after continuing (Firefox: <F8>) the browser breaks again with an insertion of a <li>.
(In my tests, the information Chrome gave about the breaks was a bit misleading, Firefox was better at that)
So the browsers implement reordering technically as "remove and insert", which breaks CSS transitions.
With that knowledge the code can easily be fixed by having fixed order of the boxes in the DOM (The order in DOM doesn't need to be changed, because the position is only set via classes):
(Note: *HTML and CSS unchanged, changes in JavaScript are marked with NEW or CHANGE *)
const {useState} = React;
// NEW: List of boxes for a stable order when rendering to the DOM:
const boxes = ['Red', 'Green', 'Blue', 'Black'];
function App() {
const [state, setState] = useState(boxes); // CHANGE: reuse boxes here
function handleShuffle() {
const newState = _.shuffle(state)
setState(newState)
}
return (
<div className="App">
{/* CHANGE: loop over boxes, not state and lookup position, which is used for the positionAt... class */
boxes.map((sourceValue, index) => {
const position = state.indexOf(sourceValue);
return (
<div className={
'box positionAt' + position + ' sourceValue' + sourceValue
}
key={sourceValue} ></div>
)
})}
<button id="shuffle" onClick={handleShuffle}> shuffle < /button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( <
App / > ,
rootElement
);
.App {
position: relative;
width: 200px;
height: 200px;
background-color: gray;
}
.box {
width: 25px;
height: 25px;
position: absolute;
transition: transform 1s;
}
.positionAt0 {
transform: translate(0px, 0px);
}
.positionAt1 {
transform: translate(175px, 0px);
}
.positionAt2 {
transform: translate(0px, 175px);
}
.positionAt3 {
transform: translate(175px, 175px);
}
.sourceValueGreen {
background-color: green;
}
.sourceValueBlue {
background-color: blue;
}
.sourceValueRed {
background-color: red;
}
.sourceValueBlack {
background-color: black;
}
#shuffle {
position: absolute;
top: 0px;
left: 75px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<div id="root"></div>
Note: it now works even if no key is set.

Changing elements (like fixed logo) color, depending on the section in React

I want to achieve an effect like this one
in a React webpage but without using jQuery. I've looked for alternatives to that library, but without results. I've seen a lot of similar questions, but each of them are answered using jQuery.
The effect is basically changing the color of the logo (and other elements in the page) as I scroll down through different sections.
Does anyone know a way to achieve this?
A way this could be done is by centering the logo's to their own containers dynamically, kinda like simulating position fixed, but using position absolute, so each logo is contained in their own section, and not globally like position fixed would do.
This way when you scroll to the next section, the second section covers the first section making it look like its transitioning.
I created a proof of concept here:
https://codesandbox.io/s/9k4o3zoo
NOTE: this demo is a proof of concept, it could be improved in performance by using something like request animation frame, and throttling.
Code:
class App extends React.Component {
state = {};
handleScroll = e => {
if (!this.logo1) return;
const pageY = e.pageY;
// 600 is the height of each section
this.setState(prevState => ({
y: Math.abs(pageY),
y2: Math.abs(pageY) - 600
}));
};
componentDidMount() {
window.addEventListener("scroll", this.handleScroll);
}
render() {
const { y, y2 } = this.state;
return (
<div>
<section className="first">
<h1
className="logo"
style={{ transform: `translateY(${y}px)` }}
ref={logo => {
this.logo1 = logo;
}}
>
YOUR LOGO
</h1>
</section>
<section className="second">
<h1
className="logo"
style={{ transform: `translateY(${y2}px)` }}
ref={logo => {
this.logo2 = logo;
}}
>
YOUR LOGO
</h1>
</section>
</div>
);
}
}
CSS would be:
section {
height: 600px;
width: 100%;
position: relative;
font-family: helvetica, arial;
font-size: 25px;
overflow: hidden;
}
.first {
background: salmon;
z-index: 1;
}
.first .logo {
color: black;
}
.second {
background: royalBlue;
z-index: 2;
}
.second .logo {
color: red;
}
.logo {
position: absolute;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 230px;
height: 30px;
}

Categories