加载中…
创作者 · MotionSites
查看原始来源 ↗
Prompt
**在 Vite + React + TypeScript + Tailwind CSS 项目中构建一个全屏 hero section。使用 `gsap` 和 `lucide-react`。不要使用其他 UI 库。**
### 字体(位于 `src/index.css`)
在 index.css 顶部、`
@tailwind
` 指令之前导入:
```css
@import
url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Barlow:wght@300;400;500;600&display=swap');
@font
-face {
font-family: 'Dirtyline';
src: url('https://fonts.cdnfonts.com/s/15011/Dirtyline36DaysofType.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
```
Body 字体:`'Barlow', sans-serif`,背景:`#000`。
### Tailwind 配置(`tailwind.config.js`)
```js
theme: {
extend: {
fontFamily: {
heading: ['Instrument Serif', 'serif'],
body: ['Barlow', 'sans-serif'],
dirtyline: ['Dirtyline', 'sans-serif'],
},
borderRadius: { DEFAULT: '9999px' },
},
},
```
### CSS(追加到 `src/index.css`)
```css
.liquid-glass {
background: rgba(255,255,255,0.01);
background-blend-mode: luminosity;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
border: none;
box-shadow: inset 0 1px 1px rgba(255,255,255,0.1);
position: relative;
overflow: hidden;
}
.liquid-glass::before {
content: "";
position: absolute; inset: 0;
border-radius: inherit;
padding: 1.4px;
background: linear-gradient(180deg,
rgba(255,255,255,0.45) 0%,
rgba(255,255,255,0.15) 20%,
rgba(255,255,255,0) 40%,
rgba(255,255,255,0) 60%,
rgba(255,255,255,0.15) 80%,
rgba(255,255,255,0.45) 100%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.liquid-glass-strong {
background: rgba(255,255,255,0.01);
background-blend-mode: luminosity;
backdrop-filter: blur(50px);
-webkit-backdrop-filter: blur(50px);
border: none;
box-shadow: 4px 4px 4px rgba(0,0,0,0.05), inset 0 1px 1px rgba(255,255,255,0.15);
position: relative;
overflow: hidden;
}
.liquid-glass-strong::before {
content: "";
position: absolute; inset: 0;
border-radius: inherit;
padding: 1.4px;
background: linear-gradient(180deg,
rgba(255,255,255,0.5) 0%,
rgba(255,255,255,0.2) 20%,
rgba(255,255,255,0) 40%,
rgba(255,255,255,0) 60%,
rgba(255,255,255,0.2) 80%,
rgba(255,255,255,0.5) 100%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.hero-title {
font-family: 'Instrument Serif', serif;
font-style: italic;
font-size: clamp(96px, 18vw, 280px);
line-height: 0.92;
letter-spacing: -0.02em;
color: white;
text-align: center;
}
```
### 组件(`src/App.tsx`)
**常量:**
- `NAV_LINKS = ['Gallery', 'Styles', 'API', 'Pricing', 'Blog']`
- `VIDEO_SRC = 'https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260511_080827_a9e5ad52-b6ee-4e79-b393-d936f179cfd7.mp4'`
**LogoMark** — inline SVG,44x26,viewBox `0 0 44 26`,包含三个白色 rect,x=0/16/30,y=3,宽度分别为 14/12/14,高度为 20,rx=3。
**状态/引用:**
- `mounted`(boolean,在挂载 effect 中设为 true,用于淡入)。
- `videoRef`(HTMLVideoElement)、`videoBgRef`(HTMLDivElement)、`displayCanvasRef`(HTMLCanvasElement)。
- `framesReady` boolean 状态,`framesRef` = `useRef<HTMLCanvasElement[]>([])`。
**Effect 1 — 帧捕获(boomerang 设置):**
- 挂载时获取 `videoRef.current`。设置 `capturing = true`、`lastTime = -1`、`MAX_WIDTH = 960`、`frames: HTMLCanvasElement[] = []`。
- `captureFrame()`:如果 `!capturing`、`readyState < 2` 或 `currentTime === lastTime`,则直接返回。更新 `lastTime`。Scale = `min(1, 960/videoWidth)`。创建尺寸为缩放后 w/h 的 offscreen canvas,执行 `ctx.drawImage(video, 0, 0, w, h)`,然后推入 frames。
- 可用时使用 `requestVideoFrameCallback`,否则使用 `requestAnimationFrame` fallback。
- 在 `loadedmetadata` 时:调用 `http://video.play().catch(()=>{})`,然后启动捕获循环。
- 在 `ended` 时:设置 `capturing = false`,将 frames 存入 `framesRef.current`,并执行 `setFramesReady(true)`。
- 如果 `readyState >= 1`,立即调用 `onLoaded()`。
- 清理:取消 raf 并移除监听器。
**Effect 2 — Boomerang 渲染:**
- 当 `framesReady` 为 true 时,获取 `displayCanvasRef`,根据 `frames[0]` 设置其 `width/height`。
- 变量:`index = 0`、`direction = 1`、`last = http://performance.now()`、`interval = 1000/30`。
- 在 `requestAnimationFrame(render)` 循环中:如果 `now - last >= interval`,绘制 `frames[index]`,然后执行 `index += direction`。当 `index >= frames.length - 1` 时,将其限制在边界并切换为 `-1`。当 `index <= 0` 时,将其限制在边界并切换为 `+1`。
- 清理:cancelAnimationFrame。
**Effect 3 — 视差鼠标追踪(gsap):**
- `strength = 20`。追踪 `targetX/Y`,每帧通过 `currentX/Y += (target - current) * 0.06` 平滑 lerp。
- 在 `mousemove` 时:`targetX = ((clientX - cx)/cx) * strength`(Y 同理)。
- 每帧执行:`gsap.set(videoBgRef.current, { x: currentX, y: currentY })`。
**JSX 结构:**
根节点:`<div className="min-h-screen bg-black text-white font-body overflow-x-hidden">`
1. **视频背景层:** `<div ref={videoBgRef} className="fixed top-0 left-0 w-full h-full z-0 scale-[1.08] origin-center">`,其中包含:
- `<video>`,设置 `src={VIDEO_SRC}`、`muted`、`playsInline`、`preload="auto"`、`crossOrigin="anonymous"`、`className="w-full h-full object-cover"`、`style={{ display: framesReady ? 'none' : 'block' }}`。
- `<canvas ref={displayCanvasRef} className="w-full h-full object-cover" style={{ display: framesReady ? 'block' : 'none' }}>`。
2. **Hero 标题:** fixed div,`left-0 right-0 z-20 w-full px-4`,`style={{ top: '126px' }}`,根据 `mounted`,通过 `transition-all duration-1000` 在 `opacity-100 translate-y-0` 与 `opacity-0 translate-y-6` 之间切换,实现淡入。内部:`<h1 className="hero-title select-none">MicroVisuals</h1>`。
3. **导航:** `<nav className="fixed top-5 left-1/2 -translate-x-1/2 z-50 whitespace-nowrap">`,其中包含一个 `liquid-glass flex items-center gap-6 rounded px-4 py-2.5` pill:
- `<LogoMark />`
- `<div className="flex items-center gap-5">`,将 `NAV_LINKS` 渲染为 `<a>`,类为 `text-sm font-body font-light text-white/70 hover:text-white transition-colors duration-200`。
- 右侧组合 `flex items-center gap-3 ml-4`:"Sign in" 链接(样式相同),然后是 "Try it free",使用 `liquid-glass-strong text-sm font-body font-medium text-white rounded px-4 py-1.5 transition-all duration-200 hover:scale-[1.04] hover:shadow-[0_0_16px_2px_rgba(255,255,255,0.12)] active:scale-[0.97]`。
4. **底部行:** fixed,`bottom-12 left-0 right-0 px-10 flex items-end justify-between z-20`,使用 `transition-all duration-1000 delay-300` 淡入。
- 左侧 `<p>`:`text-sm font-body font-light text-white/75 max-w-[220px] leading-relaxed`,文本:"Forma 的 AI 像创意总监一样理解语境、构图和风格。"
- 居中的 absolute 容器 `left-1/2 -translate-x-1/2 bottom-0 flex items-center gap-3`,包含两个按钮:
- 主按钮:`group relative bg-white text-black text-sm font-body font-medium rounded px-6 py-3 overflow-hidden active:scale-[0.97] transition-all duration-200 shadow-[0_0_0_0_rgba(255,255,255,0)] hover:shadow-[0_0_24px_4px_rgba(255,255,255,0.25)] hover:scale-[1.03]`。内容:`<span className="relative z-10">开始生成</span>` + overlay `<span className="absolute inset-0 bg-gradient-to-b from-white to-white/85 opacity-0 group-hover:opacity-100 transition-opacity duration-200" />`。
- 次按钮:`liquid-glass group text-white text-sm font-body font-medium rounded px-6 py-3 active:scale-[0.97] transition-all duration-200 hover:scale-[1.03] hover:shadow-[inset_0_1px_1px_rgba(255,255,255,0.2),0_0_20px_2px_rgba(255,255,255,0.07)]` — 标签 "查看模板"。
- 右侧 `<p>`:类与左侧相同,另加 `text-right`,文本:"描述你脑海中的画面——获得真正符合想象的图像。"
### 注意事项
- Tailwind 默认 border-radius 被覆盖为 `9999px`(完整 pill)——markup 中的每个 `rounded` 都会生成 pill corners。
- 不要使用 `video.currentTime` 进行反向播放——boomerang 仅使用捕获的 `frames[]` 数组。
- video element 保持 mounted(`framesReady` 后隐藏),以便 canvas 持续绘制 snapshots。适用模型与稳定度
建议模型
继续探索
@Oluwaphilemon1 使用 Claude Fable 5 · Three.js · Blender · GSAP完成的网页案例,包含公开结果、完整 Prompt 与原始来源。
提示语
Claude Fable 5 和 GPT-5.6 很强🥵🥵🥵 下面是我如何使用 Claude 构建的 👇 提示词:「设计一个机构网站,把服务以 3D 超市商品包装的形式呈现」 使用 Three.js 和 WebGL 在浏览器中实时渲染所有产品 使用 Blender 制作包装模型,然后导出为 GLTF 以加载到 Three.js 中 使用 GSAP 实现流畅的产品旋转和悬停交互 如果你想做一个让客户感觉像是在购买高级商品的作品集,请保存这条 🛒
复制这段 TSX 组件源码,交给 Cursor、v0、Claude 等 AI 编程工具,说明这是「3D Animated Pin」的 Aceternity UI 组件实现,即可让它接入你的 React 项目,再按需替换文案、配色与触发参数复用。
提示语
// components/ui/3d-pin.tsx "use client"; import React, { useState } from "react"; import { motion } from "motion/react"; import { cn } from "@/lib/utils"; export const PinContainer = ({ children, title, href, className, containerClassName…

提示语
首先搜索 "/unlazy" skill,完整阅读它,并在整个任务中持续使用。 将此视为一项至少持续 {argument name="minimum duration" default="5 小时"} 的大型视觉构建。如果环境允许,最好进行 {argument name="optimal duration" default="8–24 小时"} 有实质意义的实现和迭代。不要在制作出一个还不错的场景后就停下。 在浏览器中创建一个细节极其丰富的实时 {argument nam