简体   繁体   English

动画下划线反应失败

[英]Animated underline failing in react

I modified this underline animation when scrolling down on Codepen in Javascript and it works.我在 Javascript 中的 Codepen 上向下滚动时修改了这个下划线 animation 并且它可以工作。 You can see it here in the Codepen It works with Intersection Observer and for the underline it uses a generated svg.您可以在Codepen中看到它。它与 Intersection Observer 一起使用,对于下划线,它使用生成的 svg。

Explanation how it works:解释它是如何工作的:

This effect is made by positioning an SVG relative the to the text one want to emphasize and animating it in and out.这种效果是通过将 SVG 相对于要强调的文本定位并使其进出动画来实现的。

The words are wrapped to be underlined in a span element in order to allow to target the words directly.单词被包裹在span元素中以加下划线,以允许直接定位单词。

In order to access the path element using CSS the SVG code is directly in the HTML.为了使用 CSS 访问路径元素,SVG 代码直接在 HTML 中。 Add it inside of the span element you wrapped the text you want to underline in.将它添加到您包装要下划线的文本的 span 元素内。

The SVG element has a class name so it can target it using CSS -> class="squiggle" . SVG 元素有一个 class 名称,因此它可以使用 CSS -> class="squiggle" squiggle" 来定位它。

The squiggle position as well as the width and height are set inline directly on the SVG, to get the line to appear exactly where I want it. squiggle position以及widthheight直接在 SVG 上直接设置,以使线条准确出现在我想要的位置。

Now the underline shows up in the right place.现在下划线出现在正确的位置。 The animation happens with the stroke of an SVG path by using a combination of the stroke-dasharray and stroke-dashoffset properties.通过使用stroke-dasharraystroke-dashoffset属性的组合,animation 与 SVG 路径的笔划一起发生。

For the beginning styles, the stroke-dasharray and stroke-dashoffset should be large enough that the stroke is not visible.对于开头的 styles, stroke-dasharraystroke-dashoffset应该足够大,以至于笔划不可见。

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

When the container element is in view, we can set the animation name, duration, iteration-count, and fill-mode:当容器元素在视图中时,我们可以设置 animation 名称、持续时间、迭代计数和填充模式:

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

To display the animation in reverse when the element is scrolled out of view the @keyframes underline-out does the opposite animation.要在元素滚动出视图时反向显示 animation, @keyframes underline-out执行相反的 animation。

My attempt to rebuild it in React Typescript:我尝试在 React Typescript 中重建它:

Now first I try only to achieve the animation React Typescript as styled component and then I try to add the Intersection Observer from the Codepen in a next step.现在首先我尝试仅实现 animation React Typescript 作为样式组件,然后我尝试在下一步中从 Codepen 添加 Intersection Observer。 But still I have problems with the animation.但是我仍然对 animation 有问题。 I declared my styles to use inline but I still having some difficulties to achieve the same result as it does not work.我宣布我的 styles 使用内联,但我仍然难以达到相同的结果,因为它不起作用。

My App.tsx我的App.tsx

import Typography from '@mui/material/Typography';
import { Box, keyframes, styled } from '@mui/material';
import React from 'react';

const Underline = keyframes`
{
   "from": {
     strokeDashoffset: "1600",
   },
   "to": {
     strokeDashoffset: "200",
   }
 }
`;

const styles = {
   textDecoration: {
       textDecoration: 'none',
       cursor: 'pointer',
       color: 'inherit',
   },

   textEffect: {
       strokeDasharray: '1600',
       strokeDashoffset: '200',
       animationName: 'none',
   },

   squiggle: {
       position: 'absolute',
       top: '90%',
       left: '-9px',
   },

   'squiggle path': {
       stroke: '#FB9F18',
       strokeWidth: '14px',
       strokeLinecap: 'round',
       strokeDasharray: '1600',
       strokeDashoffset: '1600',

       animationName: 'underline-out',
       animationDuration: '1s',
       animationIterationCount: '1',
   },

   'textEffect.in-view .squiggle path': {
       animationName: 'underline',
       animationDuration: '1s',
       animationIterationCount: '1',
       animationFillMode: 'forwards',
   },

   '@keyframes underline': {
       from: {
           strokeDashoffset: '1600',
       },
       to: {
           strokeDashoffset: '200',
       },
   },

   '@keyframes underline-out': {
       from: {
           strokeDashoffset: '200',
       },
       to: {
           strokeDashoffset: '1600',
       },
   },
};


function UnderlineEffect() {

   return (
       <Box style={styles.textEffect}>
               <Typography>
                   Double your efficiency,{' '}
                   <span class="underline">
                       <a style={styles.textDecoration} href="www.somewebsite.com">
                           guaranteed.
                       </a>
                       <svg
                           width="215px"
                           height="25px"
                           style={styles.squiggle}
                           viewBox="0 0 466 29"
                           version="1.1"
                           xmlns="http://www.w3.org/2000/svg"
                           xmlns:xlink="http://www.w3.org/1999/xlink"
                           xml:space="preserve"
                           xmlns:serif="http://www.serif.com/"
                           style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
                           <g transform="matrix(1,0,0,1,-4055,-7503)">
                               <g transform="matrix(1,0,0,1,2942.74,0)">
                                   <g
                                       id="squiggle-underline"
                                       transform="matrix(0.59639,0.0106812,-0.0121867,0.680453,651.002,2140.64)">
                                       <path
                                           d="M976.992,7882.9C1019.57,7882.9 1065.7,7874.34 1108.59,7871.65C1246.32,7863.04 1383.5,7857.74 1521.52,7857.74C1541.4,7857.74 1709,7857.97 1714.47,7868.91C1715.77,7871.51 1710.77,7870.69 1710.29,7870.73C1695.62,7871.88 1681.88,7871.57 1667.06,7871.96C1646.09,7872.51 1625.12,7872.92 1604.15,7873.36C1564.57,7874.19 1525.13,7876.05 1485.69,7879.23C1411.5,7885.22 1337.41,7889.35 1263.06,7892.45C1196.87,7895.22 1130.52,7894 1064.4,7897.12C1021.44,7899.14 978.37,7907.57 935.437,7907.57"
                                           style="fill:none;"
                                       />
                                   </g>
                               </g>
                           </g>
                       </svg>
                   </span>
               </Typography>
       </Box>
   );
}

export { UnderlineEffect };

You are using React with Material UI and Styled-components, but for this solution, @Emotion is the better than Styled-components, why?您正在使用带有 Material UI 和 Styled-components 的 React,但是对于这个解决方案,@Emotion 比 Styled-components 更好,为什么?

Warning: Using styled-components as an engine at this moment is not working when used in a SSR projects.警告:当在 SSR 项目中使用时,此时使用 styled-components 作为引擎是行不通的。 The reason is that the babel-plugin-styled-components is not picking up correctly the usages of the styled() utility inside the @mui packages.原因是 babel-plugin-styled-components 没有正确识别 @mui 包中 styled() 实用程序的用法。 For more details, take a look at this issue.有关更多详细信息,请查看此问题。 We strongly recommend using emotion for SSR projects.我们强烈建议在 SSR 项目中使用情感。 Material UI材质界面

  1. Creating dummy scroll data创建虚拟滚动数据
  2. Creating custom components from @mui/material components using @emotion使用@emotion@mui/material组件创建自定义组件
  3. Creating the Underline.tsx component separately.单独创建Underline.tsx组件。 I redrawn the svg path to simplify svg itself.我重绘了 svg 路径以简化 svg 本身。 Right here we define our svg styles and interface (for typescript).在这里,我们定义了 svg styles 和接口(用于打字稿)。 I assigned 4 props to customize it:我分配了 4 个道具来定制它:
  • a) color - stroke (color) a) 颜色 - 描边(颜色)
  • b) size: srtoke-width b) 尺寸:srtoke-width
  • c) time: transition-duration c) 时间:transition-duration
  • d) trigger: activates stroke-dashoffset value to toggle d) 触发器:激活 stroke-dashoffset 值以切换
  1. Setting up our IterceptionObserver设置我们的 IterceptionObserver

App.tsx应用程序.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 DONW")
    .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下划线.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>
  );
};

编辑令人眼花缭乱的代码

In react jsx or tsx you should be using className not class.在 react jsx 或 tsx 中,您应该使用 className 而不是 class。

Same goes for other attributes like for which should be written as *htmlFor *其他属性也一样应该写为 *htmlFor *

Maybe you could create a new component that does just that?也许你可以创建一个新的组件来做到这一点?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM