Jump to content
Search Community

GSAP Text Rotation not working after Deployment in React Application

sergej.reznik test
Moderator Tag

Recommended Posts

Hey,

 

I have kinda a big Issue - i've implemented a Text Rotation into a React Application.
Locally everything works completely fine. After building it and deploying it on a Server (as a static version) it crashes and is not rotating as it was rotating locally. When it arrives at the last step it scrolls all items down until the first. it's not rotating from the beginning.

 

Check for the Comment "  // Start Rotation Animation".

GSAP v3.8.0 is installed.

 

Check the WeTransfer link for demo videos of local and staging env. https://we.tl/t-9MCJFOHefo
Check the Demo Instance: https://gsap.elbcouture.com/ (maybe you have to reload it a few times, if the issue didn't happend)

 

/* global fullpage_api */

import React, { createRef, useEffect, useRef } from 'react';
import gsap, { Power0 } from 'gsap';
import styled from 'styled-components';

import Header from 'components/Header';
import Stage from 'components/Stage';
import Headline from 'components/Headline';
import ScrollToExplore from 'components/ScrollToExplore';

import xsmallVideo from '../../assets/video/intro/414x896.mp4';
import smallVideo from '../../assets/video/intro/768x1024.mp4';
import mediumVideo from '../../assets/video/intro/1024x768.mp4';
import largeVideo from '../../assets/video/intro/1920x1080.mp4';

const StyledIntro = styled(Stage)`
  padding: 0;

  &::before {
    z-index: 1;
    pointer-events: none;
  }
`;

const StyledHeader = styled(Header)`
  display: block;

  @media screen and (min-width: 1024px) {
    display: none;
  }
`;

const StyledContent = styled.div`
  position: absolute;
  z-index: 1;
  margin-top: 71px;
  padding-left: 15px;

  @media screen and (min-width: 414px) {
    margin-top: 115px;
  }

  @media screen and (min-width: 768px) {
    margin-top: 70px;
  }

  @media screen and (min-width: 1024px) {
    right: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    margin-top: 0;
    width: 50%;
    height: 100%;
  }

  @media screen and (min-width: 1440px) {
    right: 0;
    display: block;
    margin-top: 228px;
    width: 50%;
    height: 100%;
  }

  @media screen and (min-width: 1920px) {
    width: 46%;
  }

  @media screen and (min-width: 2560px) {
    margin-top: 15%;
  }
`;

const StyledMaskedText = styled(Headline)`
  font-size: 54px;
  line-height: 65px;

  ${({ backgroundImage }) =>
    backgroundImage?.small
      ? `background: url(${backgroundImage.small}) no-repeat;`
      : ''}

  background-size: cover;
  background-position: center;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;

  @media screen and (min-width: 414px) {
    font-size: 64px;
    line-height: 77px;
  }

  @media screen and (min-width: 768px) {
    font-size: 104px;
    line-height: 125px;
  }

  @media screen and (min-width: 1024px) {
    font-size: 74px;
    line-height: 88px;
  }

  @media screen and (min-width: 1440px) {
    font-size: 98px;
    line-height: 129px;
  }

  @media screen and (min-width: 1920px) {
    font-size: 124px;
    line-height: 124px;
  }

  @media screen and (min-width: 2560px) {
    font-size: 130px;
    line-height: 120px;
  }
`;

const StyledHeadline = styled(Headline)`
  height: 52px;
  font-size: 44px;
  line-height: 52px;
  color: #00917e;
  overflow: hidden;
  white-space: nowrap;

  @media screen and (min-width: 414px) {
    height: 62px;
    font-size: 51px;
    line-height: 62px;
  }

  @media screen and (min-width: 768px) {
    height: 100px;
    font-size: 83px;
    line-height: 100px;
  }

  @media screen and (min-width: 1024px) {
    height: 71px;
    font-size: 59px;
    line-height: 71px;
  }

  @media screen and (min-width: 1440px) {
    height: 103px;
    font-size: 86px;
    line-height: 103px;
  }

  @media screen and (min-width: 1920px) {
    height: 132px;
    font-size: 105px;
    line-height: 132px;
  }

  @media screen and (min-width: 2560px) {
    height: 160px;
    font-size: 140px;
    line-height: 160px;
  }
`;

const StyledList = styled.div`
  list-style-type: none;
  padding: 0;
  margin: 0;
`;

const StyledVideo = styled.video`
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;

  @media screen and (min-width: 1024px) {
    position: static;
    display: block;
    height: 100vh;
  }
`;

const StyledHeadlineItem = styled.span`
  display: block;
`;

const Intro = ({ backgroundImage, switchingHeadline, active, lastActive, text, scrollTo }) => {
  const stageRef = useRef(null);
  const videoRef = useRef(null);
  const overlineRef = useRef(null);
  const headlineRef = useRef(null);
  const sublineRef = useRef(null);
  const listRef = useRef(null);
  const listItemRef = useRef([]);
  const scrollToExploreRef = useRef();

  useEffect(() => {
    switchingHeadline.push(switchingHeadline[0]);

    if (listItemRef.current.length !== switchingHeadline.length) {
      // add or remove refs
      listItemRef.current = Array(switchingHeadline.length)
        .fill()
        .map((_, i) => listItemRef.current[i] || createRef());
    }
  }, [switchingHeadline])

 

  const getVideoSrc = (width) => {
    if (width >= 1920) return largeVideo;
    if (width >= 1024) return mediumVideo;
    if (width >= 768) return smallVideo;
    return xsmallVideo;
  };
  
  // Start Rotation Animation
  const vSlide = gsap.timeline({
    repeat: -1,
    paused: true,
  });

  useEffect(() => {
    console.log('el', listItemRef.current)

    listItemRef.current.forEach((_slide, index) => {
      const label = `slide${index}`;
      const lineHeight = headlineRef.current.clientHeight;
      if (active) {
        if (index === 0) {
          vSlide.to(
            listRef?.current,
            {
              delay: 0,
              duration: 0.4,
              y: index * -1 * lineHeight,
              ease: Power0.ease,
            },
            label
          );
        }

        if (index > 0) {
          vSlide.to(
            listRef?.current,
            {
              delay: 3,
              duration: 0.4,
              y: index * -1 * lineHeight,
              ease: Power0.ease,
            },
            label
          );
        }
        vSlide.play();
      }
      vSlide.add(label);
    });
  }, [vSlide, active]);

  // End Rotation Animation

  const [isVideoLoaded, setIsVideoLoaded] = React.useState(false);
  const src = getVideoSrc(window.innerWidth);
  const onLoadedData = () => {
    setIsVideoLoaded(true);
  };

  useEffect(() => {
    if (active) {
      videoRef.current.currentTime = 0;
      gsap.set([overlineRef.current, headlineRef.current, sublineRef.current], {
        y: 0,
        opacity: 1,
      });

      gsap.set(scrollToExploreRef.current, {
        opacity: 1,
      });
    }
    if (lastActive) {
      videoRef.current.play();
      gsap.to(overlineRef.current, {
        opacity: 0,
        delay: 0,
        duration: 0.25,
        ease: Power0.in,
      });
      gsap.to(headlineRef.current, {
        opacity: 0,
        delay: 0,
        duration: 0.25,
        ease: Power0.in,
      });
      gsap.to(sublineRef.current, {
        opacity: 0,
        delay: 0,
        duration: 0.25,
        ease: Power0.in,
      });

      gsap.to(scrollToExploreRef.current, {
        opacity: 0,
        delay: 0,
        duration: 0.25,
        ease: Power0.in,
      });
      vSlide.pause();
    }
  }, [active, lastActive, vSlide]);

  return (
    <StyledIntro className="fp-noscroll" ref={stageRef}>
      <StyledHeader />
      <StyledContent>
        <StyledMaskedText ref={overlineRef} backgroundImage={backgroundImage}>
          {text?.[0]}
        </StyledMaskedText>
          {switchingHeadline && (
            <StyledHeadline ref={headlineRef}>
              <StyledList ref={listRef}>
                {switchingHeadline?.map((item, index) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <StyledHeadlineItem key={`${item}-${index}`} ref={listItemRef.current[index]}>
                    {item}
                  </StyledHeadlineItem>
                ))}
              </StyledList>
            </StyledHeadline>
          )}
        <StyledMaskedText ref={sublineRef} backgroundImage={backgroundImage}>
          {text?.[1]}
        </StyledMaskedText>
      </StyledContent>
      <StyledVideo
        ref={videoRef}
        playsInline
        muted
        src={src}
        onLoadedData={onLoadedData}
        style={{ opacity: isVideoLoaded ? 1 : 0 }}
      />
      <ScrollToExplore
        className="scroll-to-explore"
        isLight
        label={scrollTo}
        dataLabel={scrollTo}
        ref={scrollToExploreRef}
        onClick={() => (
          fullpage_api.moveSectionDown()
        )}/>
    </StyledIntro>
  );
};

export default Intro;


 

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

If you're using something like React/Next/Nuxt/Gatsby or some other framework, you may find CodeSandbox easier to use. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

If you can't recreate this locally It's very hard for us to help I'm afraid

 

what's the difference between your local environment and your production environment - that's going to be the key to figuring this out.

The only other thing I can suggest is updating to the latest version of GSAP.

Link to comment
Share on other sites

5 hours ago, Cassie said:

If you can't recreate this locally It's very hard for us to help I'm afraid

 

what's the difference between your local environment and your production environment - that's going to be the key to figuring this out.

The only other thing I can suggest is updating to the latest version of GSAP.

This is just a basic PHP Server.

I've tried this on several Server and getting the same issue there.

Link to comment
Share on other sites

I'm afraid if it works locally but not once you've deployed it it's unlikely to be an issue with GSAP itself and more likely to be something going wrong with your build.

 

You've got a fullpage.js error in the console, but no GSAP errors.

 

Link to comment
Share on other sites

  • 2 weeks later...

The build task looks like this.

 

webpack.common.js
 

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  entry: {
    app: path.resolve(__dirname, './src/index.js'),
  },
  // Where files should be sent once they are bundled
  output: {
    path: path.join(__dirname, '/build'),
    filename: '[name].[contenthash].bundle.js',
    chunkFilename: 'chunk.[contenthash].js',
    clean: true,
  },
  // Rules of how webpack will take our files, complie & bundle them for the browser
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.(jpe?g|png|webp|mp4|woff|woff2|wov)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: false,
              encoding: false,
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    alias: {
      components: path.resolve(__dirname, 'src/components/'),
      lib: path.resolve(__dirname, 'src/lib/'),
      utils: path.resolve(__dirname, 'src/utils/'),
      assets: path.resolve(__dirname, 'src/assets/'),
    },
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new CopyWebpackPlugin({
      patterns: [
        { from: 'src/assets/img', to: 'img' },
        { from: 'src/assets/video', to: 'video' },
        { from: 'src/assets/audio', to: 'audio' },
      ],
    }),
  ],
};

 

webpack.prod.js

 

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production',
  // Where files should be sent once they are bundled
  output: {
    publicPath: '/',
  },
  // optimize the bundles and split it into chunks
  optimization: {
    minimize: true,
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'initial',
        },
      },
    },
  },
});
Link to comment
Share on other sites

Hey again.

 

If it is an issue with your build that's not really something we can help with here. We can answer GSAP specific questions but I'm not really sure how to advise on webpack!

 

As I mentioned before -  If you can't recreate this in a demo It's very hard for us to help. I would start by looking at the fullpage js errors.

image.png

Link to comment
Share on other sites

Is the issue a GSAP issue though? Because the fact that it's working for you locally and there aren't any GSAP errors in the console would indicate that it isn't.

Sorry, I know this isn't hugely helpful but we don't really have the resources to look through entire react projects to hunt for bugs. We're happy to help with GSAP specific issues but I have no idea why it would be working locally and not once it's deployed. There's certainly no GSAP reason for that, the exact same JS will be run locally as it would once the site is deployed.

Maybe a way forward is for you to isolate the bit that isn't working for you when it's deployed (just the bare minimum) in a codepen or codesandbox to verify it works. Then try deploying that minimal example to see if that breaks?


I can't even scroll down in your demo link or see a broken animation so I'm not quite certain how to move forward here.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...