【译】教你构建一个很酷的前端事物生成器(笔记)

122 阅读6分钟

无论你是刚开始做前端,还是已经做了很久,建立一个可以生成一些很酷的前端魔法的工具可以帮助你学习新的东西,发展你的技能,甚至可能让你得到一点名声。

你可能已经碰到了这些流行的在线生成器。

这些年来,我自己也制作了一些 这样的东西,很有乐趣。基本上,当你遇到一些很酷的前端东西时,可能会有机会为该东西制作一个交互式生成器。

在这种情况下,我们将制作一个动画背景梯度发生器

在Next中构建项目的脚手架

这些项目的一个好处是,它们都是你的。选择你想要的任何堆栈,然后开始行动。我是Next.js的忠实粉丝,所以对于这个项目,我打算从一个基本的创建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梯度背景,第一个结果就是Manuel Pinto的这个Pen

让我们来看看这个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
  • 添加一些链接到你的个人网站和项目的资源库,并给予其应有的荣誉。
  • 更新由Create Next App生成的带有默认内容的README.md 文件。

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

你可以在GitHub上看到这个项目的代码,而演示则托管在Netlify上


The postBuilding a Cool Front End Thing Generatorappeared first onCSS-Tricks.你可以通过成为MVP支持者来支持CSS-Tricks。