构建一个酷炫的前端生成器

Avatar of John Polacek
John Polacek

DigitalOcean 提供适用于您旅程每个阶段的云产品。 立即开始使用 200 美元的免费积分!

无论您是前端新手还是经验丰富的前端开发人员,构建一个能够生成酷炫前端效果的工具都能帮助您学习新知识、提升技能,甚至可能让您小有名气。

您可能遇到过一些流行的在线生成器

多年来,我自己也玩得很开心,构建了 一些 这些 生成器。基本上,只要您遇到任何酷炫的前端效果,都可能有机会为其创建一个交互式生成器。

在本例中,我们将创建一个 动画背景渐变生成器

在 Next 中搭建项目

这些项目的好处在于,它们完全属于您。 选择您想要的任何技术栈并开始构建。 我非常喜欢 Next.js,所以对于这个项目,我将从一个基本的 Create Next App 项目开始。

npx create-next-app animated-gradient-background-generator

这将生成我们开始所需的所有文件。 我们可以编辑 pages/index.js 文件,将其作为项目的框架。

import Head from "next/head"
import Image from "next/image"
export default function Home() {
  return (
    <>
      <Head>
        <title>Animated CSS Gradient Background Generator</title>
        <meta name="description" content="A tool for creating animated background gradients in pure CSS." />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1>
          Animated CSS Gradient Background Generator
        </h1>
      </main>
    </>
  )
}

动画渐变?

在我写这篇文章的时候,如果您搜索“动画 CSS 渐变背景”,第一个结果是 这个 CodePen,由 Manuel Pinto 创建。

让我们看一下 CSS 代码

body {
  background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
  background-size: 400% 400%;
  animation: gradient 15s ease infinite;
}

@keyframes gradient {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

这是一个很好的示例,我们可以将其作为生成动画的基础。

描述动画渐变的 React 组件

我们可以为生成器分解一些可配置选项

  • 一个渐变颜色的数组
  • 渐变的角度
  • 动画的速度

为了更好地理解,我们想要使用一个高阶组件 context/SettingsContext.js 将这些设置作为数据提供给整个应用程序,并提供一些默认值。

import React, { useState, createContext } from "react"

const SettingsContext = createContext({ colorSelection: [] })

const SettingsProvider = ({ children }) => {
  const [colorSelection, setColorSelection] = useState([
    "deepskyblue",
    "darkviolet",
    "blue",
  ])
  const [angle, setAngle] = useState(300)
  const [speed, setSpeed] = useState(5)
  
  return (
    <SettingsContext.Provider
      value={{
        colorSelection,
        setColorSelection,
        angle,
        setAngle,
        speed,
        setSpeed,
      }}
    >
      {children}
    </SettingsContext.Provider>
  )
}

export { SettingsContext, SettingsProvider }

对于生成器的组件,我们想要创建

  • 用于调整这些设置的控制组件。
  • 用于显示生成动画渐变的视觉组件。
  • 用于显示 CSS 代码输出的组件。

让我们从一个 Controls 组件开始,它包含用于调整设置的各种输入。

import Colors from "./Colors"

const Controls = (props) => (
  <>
    <Colors />
  </>
)

export default Controls

我们可以将 SettingsProviderControls 组件添加到 pages/index.js 文件中。

import Head from "next/head"
import Image from "next/image"
import { SettingsProvider } from "../context/SettingsContext"
import Controls from "../components/Controls"
import Output from "../components/Output"

export default function Home() {
  return (
    <>
      <Head>
        ...
      </Head>

      <SettingsProvider>
        <main style={{ textAlign: "center", padding: "64px" }}>
          <h1>Animated CSS Gradient Background Generator</h1>
          <Controls />
          <Output />
        </main>
      </SettingsProvider>
    </>
  )
}

我们的 SettingsProvider 以 CodePen 示例中的三个颜色作为默认值开始。 我们可以通过一个新的 Colors 组件验证是否通过 SettingsContext 获取了颜色设置。

import React, { useContext } from "react"
import { SettingsContext } from "../context/SettingsContext"

const Colors = () => {
  const { colorSelection } = useContext(SettingsContext)
  return (
    <>
      {colorSelection.map((color) => (
        <div>{color}</div>
      ))}
    </>
  )
}

export default Colors

让我们使用 Colors 组件显示单个颜色色块,并使用一个小的按钮通过 SettingsContext 删除它们。

import React, { useContext } from "react"
import { SettingsContext } from "../context/SettingsContext"

const Colors = () => {
  const { colorSelection, setColorSelection } = useContext(SettingsContext)

  const onDelete = (deleteColor) => {
    setColorSelection(colorSelection.filter((color) => color !== deleteColor))
  }

  return (
    <div>
      {colorSelection.map((color) => (
        <div
          key={color}
          style={{
            background: color,
            display: "inline-block",
            padding: "32px",
            margin: "16px",
            position: "relative",
            borderRadius: "4px",
          }}
        >
          <button
            onClick={() => onDelete(color)}
            style={{
              background: "crimson",
              color: "white",
              display: "inline-block",
              borderRadius: "50%",
              position: "absolute",
              top: "-8px",
              right: "-8px",
              border: "none",
              fontSize: "18px",
              lineHeight: 1,
              width: "24px",
              height: "24px",
              cursor: "pointer",
              boxShadow: "0 0 1px #000",
            }}
          >
            ×
          </button>
        </div>
      ))}
    </div>
  )
}

export default Colors

您可能会注意到,到目前为止我们一直在使用内联样式来编写 CSS。 这些都不重要! 我们在这里玩得开心,所以我们可以随心所欲地做任何事情。

处理颜色

接下来,我们创建一个 AddColor 组件,它包含一个按钮,用于打开一个颜色选择器,用于向渐变添加更多颜色。

对于颜色选择器,我们将安装 react-color 并使用 ChromePicker 选项。

npm install react-color

再次,我们将使用 SettingsContext 更新渐变颜色选择。

import React, { useState, useContext } from "react"
import { ChromePicker } from "react-color"
import { SettingsContext } from "../context/SettingsContext"

const AddColor = () => {
  const [color, setColor] = useState("white")
  const { colorSelection, setColorSelection } = useContext(SettingsContext)

  return (
    <>
      <div style={{ display: "inline-block", paddingBottom: "32px" }}>
        <ChromePicker
          header="Pick Colors"
          color={color}
          onChange={(newColor) => {
            setColor(newColor.hex)
          }}
        />
      </div>
      <div>
        <button
          onClick={() => {
            setColorSelection([...colorSelection, color])
          }}
          style={{
            background: "royalblue",
            color: "white",
            padding: "12px 16px",
            borderRadius: "8px",
            border: "none",
            fontSize: "16px",
            cursor: "pointer",
            lineHeight: 1,
          }}
        >
          + Add Color
        </button>
      </div>
    </>
  )
}

export default AddColor

处理角度和速度

现在颜色控制已经完成,让我们添加一些包含范围输入的组件,用于设置角度和动画速度。

以下是 AngleRange 组件的代码,SpeedRange 组件与此非常相似。

import React, { useContext } from "react"
import { SettingsContext } from "../context/SettingsContext"

const AngleRange = () => {
  const { angle, setAngle } = useContext(SettingsContext)

  return (
    <div style={{ padding: "32px 0", fontSize: "18px" }}>
      <label
        style={{
          display: "inline-block",
          fontWeight: "bold",
          width: "100px",
          textAlign: "right",
        }}
        htmlFor="angle"
      >
        Angle
      </label>
      <input
        type="range"
        id="angle"
        name="angle"
        min="-180"
        max="180"
        value={angle}
        onChange={(e) => {
          setAngle(e.target.value)
        }}
        style={{
          margin: "0 16px",
          width: "180px",
          position: "relative",
          top: "2px",
        }}
      />
      <span
        style={{
          fontSize: "14px",
          padding: "0 8px",
          position: "relative",
          top: "-2px",
          width: "120px",
          display: "inline-block",
        }}
      >
        {angle} degrees
      </span>
    </div>
  )
}

export default AngleRange

现在到了最有趣的部分:渲染动画背景。 让我们使用一个 AnimatedBackground 包装组件将动画应用到页面的整个背景。

import React, { useContext } from "react"
import { SettingsContext } from "../context/SettingsContext"
const AnimatedBackground = ({ children }) => {
  const { colorSelection, speed, angle } = useContext(SettingsContext)
const background =
  "linear-gradient(" + angle + "deg, " + colorSelection.toString() + ")"
const backgroundSize =
  colorSelection.length * 60 + "%" + " " + colorSelection.length * 60 + "%"
const animation =
  "gradient-animation " +
  colorSelection.length * Math.abs(speed - 11) +
  "s ease infinite"
return (
  <div style={{ background, "background-size": backgroundSize, animation, color: "white" }}>
    {children}
  </div>
  )
}
export default AnimatedBackground

我们正在调用渐变的 CSS 动画 gradient-animation。 我们需要将其添加到 styles/globals.css 文件中才能触发动画。

@keyframes gradient-animation {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

让它对用户有用

接下来,让我们添加一些代码输出,以便用户可以复制粘贴生成的 CSS 代码并将其用在自己的项目中。

import React, { useContext, useState } from "react"
import { SettingsContext } from "../context/SettingsContext"
const Output = () => {
  const [copied, setCopied] = useState(false)
const { colorSelection, speed, angle } = useContext(SettingsContext)
const background =
  "linear-gradient(" + angle + "deg," + colorSelection.toString() + ")"
const backgroundSize =
  colorSelection.length * 60 + "%" + " " + colorSelection.length * 60 + "%"
const animation =
  "gradient-animation " +
  colorSelection.length * Math.abs(speed - 11) +
  "s ease infinite"
const code = `.gradient-background {
  background: ${background};
  background-size: ${backgroundSize};
  animation: ${animation};
}
@keyframes gradient-animation {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}`
return (
    <div
      style={{ position: "relative", maxWidth: "640px", margin: "64px auto" }}
    >
      <pre
        style={{
          background: "#fff",
          color: "#222",
          padding: "32px",
          width: "100%",
          borderRadius: "4px",
          textAlign: "left",
          whiteSpace: "pre",
          boxShadow: "0 2px 8px rgba(0,0,0,.33)",
          overflowX: "scroll",
        }}
      >
        <code>{code}</code>
        <button
          style={{
            position: "absolute",
            top: "8px",
            right: "8px",
            background: "royalblue",
            color: "white",
            padding: "8px 12px",
            borderRadius: "8px",
            border: "none",
            fontSize: "16px",
            cursor: "pointer",
            lineHeight: 1,
          }}
          onClick={() => {
            setCopied(true)
            navigator.clipboard.writeText(code)
          }}
        >
          {copied ? "copied" : "copy"}
        </button>
      </pre>
    </div>
  )
}
export default Output

让它有趣

有时添加一个按钮来设置随机值会很有趣(也很有用),就像这样。 这让用户可以快速尝试,看看他们可以从该工具中获得哪些结果。 它也是一个学习如何 生成随机十六进制颜色 的好机会。

import React, { useContext } from "react"
import { SettingsContext } from "../context/SettingsContext"

const Random = () => {
  const { setColorSelection, setAngle, setSpeed } = useContext(SettingsContext)

  const goRandom = () => {
    const numColors = 3 + Math.round(Math.random() * 3)
    const colors = [...Array(numColors)].map(() => {
      return "#" + Math.floor(Math.random() * 16777215).toString(16)
    })
    setColorSelection(colors)
    setAngle(Math.floor(Math.random() * 361))
    setSpeed(Math.floor(Math.random() * 10) + 1)
  }

  return (
    <div style={{ padding: "48px 0 16px" }}>
      <button
        onClick={goRandom}
        style={{
          fontSize: "24px",
          fontWeight: 200,
          background: "rgba(255,255,255,.9)",
          color: "blue",
          padding: "24px 48px",
          borderRadius: "8px",
          cursor: "pointer",
          boxShadow: "0 0 4px #000",
          border: "none",
        }}
      >
        RANDOM
      </button>
    </div>
  )
}

export default Random

总结

在发布项目之前,您可能还需要进行一些收尾工作

  • 使用您的项目信息更新 package.json 文件。
  • 添加一些指向您个人网站、项目代码库的链接,并对贡献者表示感谢。
  • 更新 README.md 文件,该文件由 Create Next App 生成,包含默认内容。

就这样! 我们已经准备好发布我们的新酷炫前端生成器,并收获等待我们的名利双收了!

您可以在 GitHub 上 查看此项目的代码,演示已 部署到 Netlify