javascript - 在 React 中使用渐变 API 自定义下划线

标签 javascript css reactjs typescript material-ui

我想用 React 和 Typescript 从这个 Codepen 重新创建这个下划线效果
密码笔: https://codepen.io/krakruhahah/pen/jOzwXww
我认为下面代码中的问题是接口(interface),我开始声明我的类型,但它仍然无法识别它们。它说他们是任何。但是 max 被声明为 number 但仍显示为 any。我不确定为什么。这些功能被描述为注释。
tsx:

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

interface Props {
   max: number;
}

const styles = {
   body: {
       width: "80%",
       margin: "10vw auto",
     },

     heading: {
       fontFamily: "Playfair Display, serif",
       fontSize: "10vw",
     },
     
     "subheading": {
       fontFamily: "Open Sans, sans-serif",
       fontSize: "1em",
       lineHeight: "1.5",
     },
     
     "@media screen and (min-width: 40em)": {
       body: {
         width: "50%",
       },
       heading:{
         fontSize: "6vw",
       },
     
       subheading: {
         fontSize: "1.75vw",
       }
     },
     
     "underline--magical": {
       backgroundImage: "linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%)",
       backgroundRepeat: "no-repeat",
       backgroundSize: "100% 0.2em",
       backgroundPosition: "0 88%",
       transition: "backgroundSize 0.25s ease-in",
       "&:hover": {
         backgroundSize: "100% 88%",
       },
     },
};

function Effect(props: Props) {

   // VARIABLES
const magicalUnderlines = Array.from(document.querySelectorAll('.underline--magical'));

const gradientAPI = 'https://gist.githubusercontent.com/wking-io/3e116c0e5675c8bcad8b5a6dc6ca5344/raw/4e783ce3ad0bcd98811c6531e40256b8feeb8fc8/gradient.json';

// HELPER FUNCTIONS

// 1. Get random number in range. Used to get random index from array.
const randNumInRange = max => Math.floor(Math.random() * (max - 1));

// 2. Merge two separate array values at the same index to 
// be the same value in new array.
const mergeArrays = (arrOne, arrTwo) => arrOne
 .map((item, i) => `${item} ${arrTwo[i]}`)
 .join(', ');

// 3. Curried function to add a background to array of elms
const addBackground = (elms) => (color) => {
 elms.forEach(el => {
   el.style.backgroundImage = color;
 });
}
// 4. Function to get data from API
const getData = async(url): Promise<XMLHttpRequest> => {
 const response = await fetch(url);
 const data = await response.json();
 return data.data;
}

// 5. Partial Application of addBackground to always apply 
// background to the magicalUnderlines constant
const addBackgroundToUnderlines = addBackground(magicalUnderlines);

// GRADIENT FUNCTIONS

// 1. Build CSS formatted linear-gradient from API data
const buildGradient = (obj) => `linear-gradient(${obj.direction}, ${mergeArrays(obj.colors, obj.positions)})`;

// 2. Get single gradient from data pulled in array and
// apply single gradient to a callback function
const applyGradient = async(url, callback): Promise<XMLHttpRequest> => {
 const data = await getData(url);
 const gradient = buildGradient(data[randNumInRange(data.length)]);
 callback(gradient);
}

// RESULT
applyGradient(gradientAPI, addBackgroundToUnderlines);
   return (
       <Box>
           <Typography sx={styles.heading}>
               Look At This <span style={styles['underline--magical']}>Pretty</span> Underline
           </Typography>
           <Typography sx={styles.subheading}>
               Wow this one is super incredibly cool, and this{' '}
               <span style={styles['underline--magical']}>one is on Multiple Lines!</span> I wish I had found this like thirty
               projects ago when I was representing the lollipop guild.
           </Typography>
       </Box>
   );
}
export { Effect };

最佳答案

  • 要生成随机梯度,您有两个数组 颜色位置 , 但对于 linear-gradient我们需要一串元组。我创建了一个辅助函数 生成梯度范围数组 得到这个字符串。
  • 由于我们使用 React,我们可以创建一个钩子(Hook) ( useGradient ) 从外部获取数据并在此处对其进行转换。
  • 使用 @情感 和 Material UI 组件,我们将额外的属性添加到 邮箱 排版
  • 最后我们创建 下划线 组件,检查渐变属性,并将 Prop 传递给样式。

  • 应用程序.tsx
    import styled from "@emotion/styled";
    import Typography from "@mui/material/Typography";
    import Box from "@mui/material/Box";
    
    import { Underline } from "./Underline";
    import useGradient from "./useGradient";
    
    const Heading = styled(Typography)`
      font-family: "Playfair Display", serif;
      font-size: 10vw;
      @media screen and (min-width: 40em) {
        font-size: 6vw;
      }
    `;
    const Subheading = styled(Typography)`
      font-family: "Open Sans", sans-serif;
      font-size: 1em;
      @media screen and (min-width: 40em) {
        font-size: 1.75vw;
      }
    `;
    
    export default function App() {
      const gradient = useGradient();
      return (
        <div className="App">
          <Box>
            <Heading>
              Look At This <Underline gradient={gradient}>Pretty</Underline>{" "}
              Underline
            </Heading>
            <Subheading>
              Wow this one is super incredibly cool, and this{" "}
              <Underline gradient={gradient}>one is on Multiple Lines!</Underline> I
              wish I had found this like thirty projects ago when I was representing
              the lollipop guild.
            </Subheading>
          </Box>
        </div>
      );
    }
    
    使用渐变.ts
    import { useEffect, useState } from "react";
    import { generateGradientRangeArray, randNumInRange } from "./Helpers";
    import { GradientType, IResult } from "./types";
    
    const GRADIENT_API =
      "https://gist.githubusercontent.com/wking-io/3e116c0e5675c8bcad8b5a6dc6ca5344/raw/4e783ce3ad0bcd98811c6531e40256b8feeb8fc8/gradient.json";
    
    const useGradient = () => {
      const [gradients, setGradients] = useState<GradientType>();
    
      useEffect(() => {
        const getData = async (url: string): Promise<void> => {
          // get the data from the api
          const response = await fetch(url);
          const result = (await response.json()) as IResult;
          // Get single gradient from data pulled in array
          const gradient = result.data[randNumInRange(result.data.length)];
          const transform = generateGradientRangeArray(gradient);
          // set state with the result
          setGradients(transform);
        };
        // Catching errors
        getData(GRADIENT_API).catch(console.error);
      }, []);
    
      return gradients;
    };
    
    export default useGradient;
    
    下划线.tsx
    import styled from "@emotion/styled";
    import { Props, GradientType } from "./types";
    
    const UnderlineStyle = styled.span<{ gradient: GradientType }>`
      background-image: linear-gradient(
        ${(props) => props.gradient.direction},
        ${(props) => props.gradient.range}
      );
      background-repeat: no-repeat;
      background-size: 100% 0.2em;
      background-position: 0 88%;
      transition: background-size 0.25s ease-in;
      &:hover {
        background-size: 100% 88%;
      }
    `;
    
    export const Underline = ({ children, gradient }: Props) => {
      if (!gradient) return null;
      return <UnderlineStyle gradient={gradient}>{children}</UnderlineStyle>;
    };
    
    助手.ts
    import { IGradient, GradientType } from "./types";
    /// HELPER FUNCTIONS
    
    // Get random number in range. Used to get random index from array.
    export const randNumInRange = (max: number) =>
      Math.floor(Math.random() * (max - 1));
    
    // Create range of colors for gradient
    export const generateGradientRangeArray = (data: IGradient): GradientType => {
      const { colors, direction, name, positions } = data;
      const rangeColorsArray: string[] = [];
    
      for (const clr in colors) {
        for (const pos in positions) {
          if (clr === pos) {
            rangeColorsArray.push(`${colors[clr]} ${positions[pos]},`);
          }
        }
      }
      const createGradientString = rangeColorsArray.join(" ").slice(0, -1);
    
      return { name, direction, range: createGradientString };
    };
    
    类型.ts
    import { ReactNode } from "react";
    
    export interface IGradient {
      name: string;
      direction: string;
      colors: string[];
      positions: string[];
    }
    
    export type GradientType = { range: string } & Pick<
      IGradient,
      "name" | "direction"
    >;
    
    export interface IResult {
      data: IGradient[];
    }
    
    export interface Props {
      children: ReactNode;
      gradient?: GradientType;
    }
    
    Edit dazziling-code

    关于javascript - 在 React 中使用渐变 API 自定义下划线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73116434/

    相关文章:

    javascript - 无需单击或 Ctrl 单击即可同时拖动多个 DIV

    responsive-design - 如何在响应式设计中将最大高度设置为图像的原始高度?

    javascript - 将 JSON 文件从 LocalStorage 解析为 react-native

    javascript - 如何按数据属性对 div 顺序位置进行排序

    javascript - “let block”仅在 Mozilla JavaScript 扩展中可用(使用 moz 选项)

    javascript - 从 create-react-app 公共(public)文件夹中读取 JSON 文件

    css - MudBlazor:如何对齐 MudIcon 以使其与 MudText 对齐?

    css - 我可以在 css 中指定特定行的样式吗?

    reactjs - react 故事书插件旋钮不显示

    android - React Native 中 View 的网格布局