React animation failing to render underline animation

After tinkering with the underline animation while scrolling down on Codepen using Javascript, I successfully implemented it. You can check out the working version on Codepen. This animation utilizes Intersection Observer and a generated svg for the underlining effect.

Explanation of how it functions:

This visual effect is achieved by positioning an SVG relative to the text that needs emphasis and animating it in and out accordingly.

The targeted words are wrapped within a span element so that they can be accessed directly for styling purposes.

To enable CSS styling on the path element of the SVG, the SVG code itself is directly embedded within the HTML, specifically inside the span wrapping the text to be underlined.

A class name is assigned to the SVG element (squiggle) to facilitate CSS targeting.

The inline properties such as squiggle position, width, and height are directly set on the SVG element to ensure precise placement of the underline.

With these configurations in place, the underline appears correctly. The animation is executed by manipulating the stroke of an SVG path through a combination of stroke-dasharray and stroke-dashoffset properties.

For the initial setup, values for stroke-dasharray and stroke-dashoffset must be large enough to keep the stroke hidden initially.

.squiggle path {
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
}

When the container element comes into view, the animation properties like name, duration, iteration-count, and fill-mode can be specified:

.text-effect.in-view .squiggle path {
  animation-name: underline;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

An additional @keyframes underline-out rule controls the reverse animation when the element exits the viewport.

Attempting to replicate it in React Typescript:

I first tried replicating the animation in React Typescript as a styled component and then planned to integrate the Intersection Observer from the original Codepen demo. However, I faced challenges with the animation execution despite setting inline styles as required.

Here's a snippet of my App.tsx:

// Code snippet here

Answer №1

When working with React JSX or TSX, remember to use className instead of class.

The same applies to other attributes like for, which should be written as *htmlFor *

Answer №2

You're currently utilizing React in conjunction with Material UI and Styled-components, however, for this particular scenario, @Emotion proves to be superior to Styled-components. What makes it better?

Warning: Currently, using styled-components as an engine is causing issues when implemented in SSR (server-side rendering) projects. This is due to the babel-plugin-styled-components not properly detecting the use of the styled() utility within the @mui packages. For more information, refer to this issue. We strongly recommend utilizing emotion for SSR projects. Material UI

  1. Generating dummy scroll data
  2. Creating custom components from @mui/material components using @emotion
  3. Developing the Underline.tsx component separately. The svg path has been simplified for ease of use. Here, we establish our svg styles and interface (for typescript). 4 props have been assigned for customization:
  • a) color - stroke (color)
  • b) size: srtoke-width
  • c) time: transition-duration
  • d) trigger: activates stroke-dashoffset value to toggle
  1. Configuring our IterceptionObserver

App.tsx

import { useRef, useEffect, useState, useMemo } from "react";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
import styled from "@emotion/styled";

import { Underline } from "./Underline";

const LinkStyle = styled(Link)`
  text-decoration: none;
  color: inherit;
  position: relative;
`;

const TitleStyle = styled(Typography)`
  font-size: 42px;
  text-align: center;
`;

export default function App() {
  const [active, setActive] = useState(false);
  const ref = useRef<HTMLElement>(null);

  const callback = (entries: IntersectionObserverEntry[]) => {
    const [entry] = entries;
    if (entry.isIntersecting) {
      setActive(entry.isIntersecting);
      return;
    }
    setActive(false);
  };

  const options = useMemo(() => ({
      root: null,
      rootMargin: "0px",
      threshold: 0.75
    }), 
    []
  );

  useEffect(() => {
    const container = ref.current;
    // Observer with external callback function and options
    const observer = new IntersectionObserver(callback, options);
    if (container) observer.observe(container);

    //cleanup when a component unmounted
    return () => {
      if (container) observer.unobserve(container);
    };
  }, [ref, options]);

  // Dummy scroll data
  const scrollDown = new Array(9)
    .fill("SCROLL DOWN")
    .map((el, idx) => <TitleStyle key={idx}>{el}</TitleStyle>);

  const scrollUp = new Array(9)
    .fill("SCROLL UP")
    .map((el, idx) => <TitleStyle key={idx}>{el}</TitleStyle>);
  // ----

  return (
    <div className="App">
      {scrollDown}
      <Box>
        <TitleStyle ref={ref}>
          Double your efficiency,{" "}
          <LinkStyle href="#" color="inherit">
            guaranteed.{" "}
            <Underline color="#fb9f18" time={0.5} trigger={active} size={4} />
          </LinkStyle>
        </TitleStyle>
      </Box>
      {scrollUp}
    </div>
  );
}

Underline.tsx

import styled from "@emotion/styled";

export interface Props {
  color: string;
  time: number;
  trigger: boolean;
  size: number;
}

const SVGWrapper = styled.span`
  width: 110%;
  height: 20px;
  position: absolute;
  bottom: 5px;
  left: 0;
`;

const Path = styled.path<Props>`
  stroke: ${(props) => props.color};
  stroke-width: ${(props) => props.size};
  stroke-linecap: round;
  stroke-dashoffset: ${(props) => (props.trigger ? 60 : 390)};
  stroke-dasharray: 390;
  transition: all ${(props) => props.time}s;
`;

export const Underline = ({ color, size, trigger, time }: Props) => {
  return (
    <SVGWrapper>
      <svg viewBox="0 0 174 15" fill="none" xmlns="http://www.w3.org/2000/svg">
        <Path
          color={color}
          time={time}
          trigger={trigger}
          size={size}
          d="M2.184 7.404c17.923 0 24.462-2.474 40.922-2.943 8.028-.229 59.438-1.148 64.175-1.368 14.133-.657 36.013-1.744 62.608 4.311 8.092 1.843-25.485-.13-36.391 1.046-15.655 1.69-32.732 2.346-49.015 2.85-13.589.421-53.
558.512-64.376.786"
        />
      </svg>
    </SVGWrapper>
  );
};

https://codesandbox.io/s/animated-underline-failing-in-react-09sstr?file=/src/App.tsx

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issues with monitoring multi-touch gesture events using Hammer JS

Can anyone figure out why this code, which utilizes hammer.js for multi-touch gesture events, is not functioning as expected? This code closely resembles one of the examples on Hammer's documentation. Despite setting up swipe events on specific elem ...

Velocity.js causing a slowdown in animated performance

Currently, I am attempting to animate spans, and while the animation is functional, it appears to be somewhat choppy and lacks smoothness. https://codepen.io/pokepim/pen/JBRoay My assumption is that this is due to my use of left/right for animation purpos ...

Import a picture file into Photoshop and position it at a designated location

I need assistance with developing a function that can load an image and position it at specific x, y coordinates in Photoshop. Below is the code I have so far: var docRef = app.activeDocument; function MoveLayerTo(fLayer, fX, fY) { var Position = fLaye ...

Input field in a tableview

I have a ListView that features a dropdown, 4 textboxes and buttons. My goal is to only show the fourth textbox (enclosed in a span) when the dropdown value in each row of the ListView is set to 2. For instance, if there are 5 records being displayed in t ...

Error in Next.js 11: Unable to access property 'canonicalBase' as it is undefined

Encountering an issue after upgrading from Next.js version 10.0.2 to 11.0.1. Since the update, I am unable to initiate a project due to the following error: Cannot read property 'canonicalBase' of undefined In my _app.tsx file, the Next imports ...

Transforming this JavaScript function using Template Strings into TypeScript

Is there anyone out there who can assist with the following query? I have a functional .js file that I need to integrate into a TypeScript program. import React from "react"; import styled, { css } from "styled-components"; const style = ({ theme, ...res ...

Is the original image source revealed when clicked?

I've implemented an expand and collapse feature using jQuery/JavaScript. Clicking on the DIV causes the image inside to change state. However, clicking on the same DIV again doesn't return the image to its original state; it remains stuck in the ...

Efficiently processing multiple Ajax requests with a single callback function

I am currently facing a situation where I need to make multiple AJAX requests and only proceed if all of them are successful. The typical usage of $.when($.ajax(), [...]).then(function(results){},[...]); doesn't quite fit my needs because the number o ...

Creating experiences for websites controlled by gestures

It's been a great experience working on a website that utilizes gesture-based technology. My inspiration for this project came from various sources, including this link. Despite researching extensively through websites, Google, Wikipedia, and GitHub, ...

Styling the arrow of a CSS select box

Is it possible to place a dropdown arrow inside a select box? I have tried, but the arrow always displays behind the select box. How can I modify the CSS code to position the arrow properly inside the select box? .form-item-custom-search-types::before { ...

How can I make the lines of my drawer thicker in Material UI?

Currently, I'm in the process of implementing a left-side navigation bar for a web application I'm developing. The main challenge I'm facing is customizing the styles as desired when using the Drawer component. Specifically, I'm aiming ...

What obstacles must be overcome when developing a CSS framework?

Currently, I am delving into the realm of popular CSS frameworks such as Bootstrap and Foundation. While studying them, I have identified aspects that I believe could be enhanced and customized to suit my own projects or potentially for free distribution i ...

Encountering problems when attempting to effectively filter a list of products using data

<div id="prod"> <div class="content" data-brand="Andrew" data-price="1000" data-store="JCPenny">Andrew</div><br /> <div class="content" data-brand="Hill" d ...

How can I write this to function on click events?

Could someone please help me with a simple question I have? I am struggling to properly write the code. $wish_1='<span onclick=alert("Register or Login Now");></span>'; The issue is that spaces are not working and using ' also ...

When the link_to element is clicked, use AJAX to replace the content of a specific

I was wondering about a modification to the link in view/users/show: <%= link_to "Photos (#{@user.photos.count})", user_path(@user), id: "photo_link", remote: true %> My goal is to dynamically change [1..6] to [1..-1] without having to rerender the ...

Transform form data into a specialized JSON structure

I have a form set up in the following way: <form id="myForm" ng-submit="submitForm()"> <select name="ItemName" ng-controller="ItemController"> <option value="" selected disabled>Select</option> <option ng-rep ...

Error: Unable to dispatch a Redux-thunk action with additional arguments in React

Currently, I am attempting to work with TypeScript alongside Redux-Thunk and Redux. My goal is to pass an object to any action (I am utilizing dependency injection and need to pass either an object service or a string). However, I encountered the followin ...

Efficient ways to enhance CSS animations (including scaling, rotating, and blurring)

In this HTML example, there are two animated pictures - one background and the other an object. Despite using scale and rotate animations, the performance is choppy on full HD monitors, especially in Firefox where it runs at about 20 fps. Initially, jQuer ...

Trouble with the filter function in the component array

I am facing an issue with creating and deleting multiple components. I have successfully created the components, but for some reason, I am unable to delete them when I click on the "delete" button. state = { data: '', todoCard: [], id ...

Text is overflowing from the table cell due to its length

UPDATE: I have included a screenshot for reference. My goal is to ensure that the text within the grid container does not scroll horizontally and instead fits within the available space. https://i.stack.imgur.com/CfFuy.png In my React Material-UI table, ...