Skip to main content

首页特效-极光背景

首页展示极光动画历程


tip

该效果来源自 Aceternity UI , 作者秀肌肉的一个舞台, 效果是怎么炫酷怎么来, 没有统一的设计语言, 没有组件分类……

作为前端小白的笔者, 移植时属实踩了不少坑, 下面的顺序是最后整理的正确顺序

废话不多说, 先看效果图

组件依赖

  1. Tailwind CSS: 非常流行的可插件样式的CSS框架
  2. Framer motion: 基于React的用来高效实现各种交互和动画效果的开源库

Tailwind CSS配置

https://ui.shadcn.com/docs/installation/manual#add-tailwind-css

danger

注意, 每次修改配置文件后, 启动前执行清除缓存, 否则可能导致配置不生效 npm cache clear --force

1. 安装依赖

pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react

// 样式配置文件初始化
pnpm tailwindcss init -p

2. 路径配置

@site: 表示项目根目录

@site/tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
  • 别名@是首选项, 也可以根据需要使用其他别名

3. 基础配置

@site/tailwind.config.js
/** @type {import('tailwindcss').Config} */
const {
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");

module.exports = {
content: [
"./src/**/*.{md,mdx,js,jsx,ts,tsx}",
"./docs/**/*.{md,mdx,js,jsx,ts,tsx}",
"./blog/**/*.{md,mdx,js,jsx,ts,tsx}",
],
darkMode: "class",
theme: {
extend: {
animation: {
aurora: "aurora 60s linear infinite",
},
keyframes: {
aurora: {
from: {
backgroundPosition: "50% 50%, 50% 50%",
},
to: {
backgroundPosition: "350% 50%, 350% 50%",
},
},
},
},
},
plugins: [
addVariablesForColors,
require("tailwindcss-animate"),
],
corePlugins: {
preflight: false, // 禁用 Tailwind 的基础样式重置
},
// important: '#tailwind-wrapper', // 增加所有 Tailwind 类的优先级
}

function addVariablesForColors({ addBase, theme }: any) {
let allColors = flattenColorPalette(theme("colors"));
let newVars = Object.fromEntries(
Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
);

addBase({
":root": newVars,
});
}
tip

corePlugins:{preflight: false,}, - 禁用 Tailwind 的基础样式重置

这里需要强调一下, 是解决 TailWind 基础样式与脚手架样式冲突的关键点

4. 全局样式配置

learn more about using CSS variables for theming in the theming section.

@site/src/css/custom.css
@tailwind screens;
@tailwind base;

.tailwind {
@tailwind components !important;
@tailwind utilities;
}
  • @tailwind base; - 引入基础样式
  • @tailwind components !important; - 引入组件样式, 且样式优先级最高
  • @tailwind utilities; - 引入实用工具类
  • @tailwind screens; - 引入屏幕断点, 用于相应式设计
  • .tailwind 类选择器, 主要是想用容器包裹这些样式, 便于部分组件单独使用样式, 比如我的友链和导航

5. 创建文件

@site/components.json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

Framer motion

pnpm add framer-motion

组件实现

配置postcss引入插件

引入 tailwindcssautoprefixer 插件

可以便捷地应用 Tailwind CSS 进行样式设计,并确保生成的 CSS 在不同浏览器中的兼容性

postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
};

创建组件

修改文件@site/src/components/HomepageFeatures/index.tsx
import React, { ReactNode } from "react";
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { motion } from "framer-motion";

import styles from './styles.module.css';

// 用于合并和处理 CSS 类名, 通过其参数可以动态生成背景样式
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

export const AuroraBackground = ({
className,
children,
showRadialGradient = true,
...props
}) => {
return (
(<main>
<div
className={cn(
// "relative flex flex-col h-[100vh] items-center justify-center bg-zinc-50 dark:bg-zinc-900 text-slate-950 transition-bg", // 暗色控制后的样式是否与下方一致??
"relative flex flex-col h-[100vh] items-center justify-center dark:bg-zinc-900 text-slate-950 transition-bg",
className
)}
{...props}>
<div className="absolute inset-0 overflow-hidden">
<div
// I'm sorry but this is what peak developer performance looks like // trigger warning
className={cn(`
[--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)]
[--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)]
[--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)]
[background-image:var(--white-gradient),var(--aurora)]
dark:[background-image:var(--dark-gradient),var(--aurora)]
[background-size:300%,_200%]
[background-position:50%_50%,50%_50%]
filter blur-[10px] invert dark:invert-0
after:content-[""] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)]
after:dark:[background-image:var(--dark-gradient),var(--aurora)]
after:[background-size:200%,_100%]
after:animate-aurora after:[background-attachment:fixed] after:mix-blend-difference
pointer-events-none
absolute -inset-[10px] opacity-50 will-change-transform`, showRadialGradient &&
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`)}></div>
</div>
{children}
</div>
</main>)
);
}

export default function HomepageFeatures(): JSX.Element {
return (
<div className="tailwind">
<AuroraBackground className={styles.features}>
<motion.div
initial={{ opacity: 0.3, y: 200 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{
delay: 0,
duration: 1,
ease: "easeInOut",
}}
className="relative flex flex-col gap-4 items-center justify-center px-4"
>
<div className="text-3xl md:text-7xl font-bold dark:text-white text-center">
Background lights are cool you know.
</div>
{/* <div className="font-extralight text-base md:text-4xl dark:text-neutral-200 py-4">
And this, is chemical burn.
</div>
<button className="bg-black dark:bg-white rounded-full w-fit text-white dark:text-black px-4 py-2">
Debug now
</button> */}
</motion.div>
</AuroraBackground>
</div>
);
}
PropTypeDefaultDescription
childrenReactNodeN/AAuroraBackground 组件要显示的内容
classNamestringN/A附加的样式
showRadialGradientbooleantrue是否展示渐变效果
...propsobjectN/A传递过来的属性

引入组件

@site/src/pages/index.tsx
export default function Home(): JSX.Element {
const {siteConfig} = useDocusaurusContext();
return (
<Layout
// title={`Hello from ${siteConfig.title}`}
description="Description will go into a meta tag in <head />">
{/* <HomepageHeader /> */}
<main>
<HomepageFeatures />
</main>
</Layout>
);
}

运行结果