Jump to content
Search Community

How to change the box background and split text effect with mouse hover

Daniel Silva test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hello,

I have a question about how to replicate the hover effect in boxes like this site (https://www.vovi.studio/).
My question is how to have the box background changed to black but in a smooth way like the example website and split the text.

I managed to do something with the code below, but it wasn't as smooth as on the example site.

 

// react box component
import { useRef } from 'react';
import gsap from 'gsap';
import { ArrowCircleRight } from 'phosphor-react';

import { Flex, SplitElement } from '@/components';

import * as S from './styles';
import { DefaultTheme } from '@/styles';

interface IGridBoxProps {
  alignWrapper?: 'center' | 'flex-end';
  justifyChildren?: 'center' | 'space-between';
}

const GridBox = ({
  alignWrapper = 'center',
  justifyChildren = 'space-between'
}: IGridBoxProps) => {
  const splitElementRef = useRef<HTMLDivElement>(null);
  const gridBoxRef = useRef<HTMLDivElement>(null);

  const tl = gsap.timeline({
    defaults: { duration: 0.2, ease: 'power1.inOut' }
  });

  const onEnter = () => {
    if (splitElementRef.current && gridBoxRef.current) {
      const splitChildren = gsap.utils.toArray(
        splitElementRef.current.children
      );

      tl.to(gridBoxRef.current, {
        background: DefaultTheme.colors.text.heading,
        duration: 0.3,
        stagger: -0.05
      });

      tl.to(splitChildren, {
        yPercent: -100,
        stagger: -0.05
      });
    }
  };

  const onLeave = () => {
    if (splitElementRef.current && gridBoxRef.current) {
      const splitChildren = gsap.utils.toArray(
        splitElementRef.current.children
      );

      tl.to(splitChildren, {
        yPercent: 0
      });

      tl.to(gridBoxRef.current, {
        background: DefaultTheme.colors.white,
        duration: 0.15
      });
    }
  };
  return (
    <S.GridBoxWrapper
      align={alignWrapper}
      ref={gridBoxRef}
      onMouseEnter={onEnter}
      onMouseLeave={onLeave}
    >
      <Flex
        align="flex-end"
        justify={justifyChildren}
        style={{
          width: '100%'
        }}
      >
        <SplitElement
          firstChild="firstChild"
          lastChild="lastChild"
          ref={splitElementRef}
        />
        <ArrowCircleRight size={20} weight="bold" />
      </Flex>
    </S.GridBoxWrapper>
  );
};

export default GridBox;

//styles box component
import styled, { css } from 'styled-components';

import { Flex } from '..';

export const GridBoxWrapper = styled(Flex)`
  ${({ theme }) => css`
    width: 100%;
    height: 200px;
    border: 1px solid ${theme.colors.gray[50]};
    padding: 3.2rem;

    svg {
      color: ${theme.colors.gray[200]};
    }
  `}
`;

//react split component
import {
  ForwardRefRenderFunction,
  HTMLAttributes,
  ReactNode,
  forwardRef
} from 'react';

import * as S from './styles';

type SlitWrapperDivType = HTMLAttributes<HTMLDivElement>;

interface ISplitElementProps extends SlitWrapperDivType {
  firstChild: ReactNode;
  lastChild: ReactNode;
}

const SplitElement: ForwardRefRenderFunction<
  HTMLDivElement,
  ISplitElementProps
> = ({ firstChild, lastChild, ...props }, ref) => {
  return (
    <S.SplitWrapper {...props} ref={ref}>
      <S.SplitChildren>{firstChild}</S.SplitChildren>
      <S.SplitChildren>{lastChild}</S.SplitChildren>
    </S.SplitWrapper>
  );
};

export default forwardRef(SplitElement);

//styles split component
import styled, { css } from 'styled-components';

export const SplitWrapper = styled.div`
  width: fit-content;
  height: 2em;
  overflow: hidden;

  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
`;

export const SplitChildren = styled.span`
  ${({ theme }) => css`
    color: ${theme.colors.gray[200]};
    font-size: 2em;
    line-height: 1;
    cursor: pointer;

    display: flex;
    align-items: center;
    justify-content: center;
  `}
`;

 

Can anyone help me improve this gsap code and make the animation as smooth as possible?

Thank you all very much in advance.

Link to comment
Share on other sites

Hi,

 

I'm afraid that there is not a lot we can do with just a code snippet. Please do your best to create a minimal demo that clearly illustrates the issue your having without copying all your project or entire sections of it. Smaller the demo, faster we can help you.

 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

Also I see that you are using React but you're not using GSAP Context:

https://gsap.com/docs/v3/GSAP/gsap.context()

 

Always use GSAP Context in React/Next projects and also take a look at the resources in this page:

https://gsap.com/resources/React

 

Happy Tweening!

Link to comment
Share on other sites

  • Solution

Hi,

 

Maybe something like this:

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

 

There is no need to run all that logic on your enter and leave handlers. Keep in mind that running this:

const onEnter = () => {
  if (splitElementRef.current && gridBoxRef.current) {
    const splitChildren = gsap.utils.toArray(
      splitElementRef.current.children
    );

    tl.to(gridBoxRef.current, {
      background: "#212226",
      duration: 0.3,
      stagger: -0.05
    });

    tl.to(splitChildren, {
      yPercent: -100,
      stagger: -0.05
    });
  }
};

Adds a those instances to the timeline each time the mouse enters, making the timeline longer and longer, which could cause issues down the line. For this cases is always better to create a single Tween/Timeline and play/reverse it based on the event type.

 

Also you weren't using GSAP Context as I advice you to do. Always use GSAP Context in react and revert it during the cleanup phase of the component.

 

Finally read the resources in this page:

https://gsap.com/resources/React

 

Happy Tweening!

  • Like 1
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...