我在Javascript中向下滚动Codepen时修改了这个下划线动画,效果很好.您可以在这里的Codepen中看到它与交叉点观察者一起工作,对于下划线,它使用生成的svg.

Explanation how it works:

这种效果是通过相对于要强调的文本定位SVG,并在其中设置动画来实现的.

单词被包装成在span个元素中加下划线,以便直接定位单词.

为了使用CSS访问path元素,SVG代码直接位于HTML中.将其添加到要在其中添加下划线的文本所包装的span元素中.

SVG元素有一个类名,因此可以使用CSS将其作为目标->class="squiggle"

squiggle position以及widthheight直接在线设置在SVG上,以使行显示在我想要的位置.

现在下划线显示在正确的位置.通过使用stroke-dasharraystroke-dashoffset属性的组合,可以通过SVG路径的笔划进行动画.

对于开始样式,stroke-dasharraystroke-dashoffset应该足够大,使笔划不可见.

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

当容器元素在视图中时,我们可以设置动画名称、持续时间、迭代计数和填充模式:

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

要在元素从视图中滚出时反向显示动画,@keyframes underline-out会执行相反的动画.

My attempt to rebuild it in React Typescript:

现在,首先,我只try 实现动画React Typescript作为样式化组件,然后在下一步中try 从代码笔添加交点观察者.

我的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 };

推荐答案

您正在使用React with Material UI和样式化组件,但对于此解决方案,@Mootion优于样式化组件,为什么?

警告:在SSR项目中使用样式化组件作为引擎目前不起作用.原因是babel插件样式的组件没有正确识别@mui包中styled()实用程序的用法.有关更多详细信息,请查看此问题.我们强烈建议在SSR项目中使用情感.Material UI

  1. 创建虚拟滚动数据
  2. 使用@emotion@mui/material个组件创建自定义组件
  3. 分别创建Underline.tsx个组件.我重新绘制了svg路径以简化svg本身.在这里,我们定义了svg样式和接口(用于typescript).我分配了4个props 来定制它:
  • a) colored颜色 -笔划( colored颜色 )
  • b)尺寸:srtoke宽度
  • c) 时间:过渡持续时间
  • d) 触发器:激活要切换的笔划dashoffset值
  1. 设置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 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

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>
  );
};

Edit dazziling-code

Javascript相关问答推荐

过滤对象数组并动态将属性放入新数组

防止用户在selectizeInput中取消 Select 选项

浮动Div的淡出模糊效果

加载背景图像时同步旋转不显示的问题

当使用';字母而不是与';var#39;一起使用时,访问窗口为什么返回未定义的?

JQuery Click事件不适用于动态创建的按钮

本地库中的chartjs-4.4.2和chartjs-plugin-注解

如何创建返回不带`new`关键字的实例的类

400 bad request error posting through node-fetch

如何在DYGRAPS中更改鼠标事件和键盘输入

为什么延迟在我的laravel项目中不起作用?

Jexl to LowerCase()和Replace()

使用CEPRESS截取时,cy.Wait()在等待5000ms的第一个路由请求时超时

如何使用[ModelJSON,ArrayBuffer]调用tf.loadGraphModelSync

脚本语法错误只是一个字符串,而不是一个对象?

使用jQuery每隔几秒钟突出显示列表中的不同单词

检测带有委托的元素内部的点击,以及元素何时按其类名被选中

单击时同时 Select 和展开可访问的行

如何在底部重叠多个div?

打字脚本中的函数包装键入