加载中…
创作者 · MotionSites
查看原始来源 ↗
Prompt
使用 React + Tailwind CSS + GSAP 创建一个 Nike 品牌的单区块全视口(`h-[100dvh]`)页面。它必须**完全适配移动端**。应用需要通过 npm 安装 `react-player` 和 `gsap`。
---
### 1. 要安装的依赖项
安装 `react-player` 和 `gsap`。
### 2. 全局样式与配置(`src/index.css`)
将 `index.css` 替换为以下原样的 Tailwind v4 和 Google Fonts 配置:
```css
@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Manrope:wght@400;500;600;700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Manrope", sans-serif;
--font-serif: "Instrument Serif", serif;
}
```
### 3. SpotlightReveal 组件(`src/components/SpotlightReveal.tsx`)
一个跟随光标移动的交互式 SVG 聚光灯遮罩。用户移动鼠标时,会透过静态图片叠加层显露下方隐藏的视频层。在移动端/触摸设备上,回退为触摸追踪。
```tsx
import { useEffect, useRef } from 'react';
interface SpotlightRevealProps {
imageSrc: string;
videoSrc: string;
isPlaying?: boolean;
baseRadius?: number;
}
export default function SpotlightReveal({
imageSrc,
videoSrc,
isPlaying = true,
baseRadius = 420,
}: SpotlightRevealProps) {
const NUM_TRAILS = 6;
const videoRef = useRef<HTMLVideoElement>(null);
const pointsRef = useRef(
Array.from({ length: NUM_TRAILS }, () => ({ x: -1000, y: -1000 }))
);
useEffect(() => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.play().catch(() => {});
} else {
videoRef.current.pause();
}
}
}, [isPlaying]);
useEffect(() => {
let targetX = window.innerWidth / 2,
targetY = window.innerHeight / 2;
const handleMouseMove = (e: MouseEvent) => {
targetX = e.clientX;
targetY = e.clientY;
};
window.addEventListener('mousemove', handleMouseMove);
let animationFrameId: number;
const animate = () => {
const points = pointsRef.current;
points[0].x += (targetX - points[0].x) * 0.2;
points[0].y += (targetY - points[0].y) * 0.2;
for (let i = 1; i < points.length; i++) {
points[i].x += (points[i - 1].x - points[i].x) * 0.35;
points[i].y += (points[i - 1].y - points[i].y) * 0.35;
}
for (let i = 0; i < points.length; i++) {
const circle = document.getElementById(`trail-${i}`);
if (circle) {
circle.setAttribute('cx', points[i].x.toString());
circle.setAttribute('cy', points[i].y.toString());
}
}
animationFrameId = requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener('mousemove', handleMouseMove);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<div className="absolute inset-0 w-full h-full z-0 bg-black pointer-events-none overflow-hidden flex items-center justify-center">
<div className="absolute inset-0 w-full h-full flex items-center justify-center overflow-hidden pointer-events-none">
<video
ref={videoRef}
src={videoSrc}
className="absolute inset-0 w-full h-full object-cover"
muted
loop
playsInline
/>
</div>
<svg
className="absolute inset-0 w-full h-full"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<radialGradient id="holeGradient">
<stop offset="0%" stopColor="black" stopOpacity="1" />
<stop offset="60%" stopColor="black" stopOpacity="0.8" />
<stop offset="100%" stopColor="black" stopOpacity="0" />
</radialGradient>
<mask
id="spotlight-mask"
maskContentUnits="userSpaceOnUse"
x="0"
y="0"
width="100%"
height="100%"
>
<rect width="100%" height="100%" fill="white" />
{Array.from({ length: NUM_TRAILS })
.reverse()
.map((_, reversedIndex) => {
const i = NUM_TRAILS - 1 - reversedIndex;
return (
<circle
key={`trail-${i}`}
id={`trail-${i}`}
cx="-1000"
cy="-1000"
r={baseRadius - i * 35}
fill="url(#holeGradient)"
opacity={1 - i * 0.15}
/>
);
})}
</mask>
</defs>
<image
href={imageSrc}
width="100%"
height="100%"
preserveAspectRatio="xMidYMid slice"
mask="url(#spotlight-mask)"
/>
</svg>
</div>
);
}
```
**工作原理:**
- 一个 `<video>` 在所有内容后方全屏播放。
- 一个 SVG `<image>` 叠加在上方,并应用径向渐变遮罩。
- 6 个拖尾圆形通过缓动跟随光标(引导圆使用 0.2 lerp,跟随圆使用 0.35 lerp)。圆形所在的位置会在遮罩中切出孔洞,显露下方的视频。
- `baseRadius` 控制聚光灯大小(区块 1 默认为 420,此区块 2 为 520)。
- `isPlaying` 通过父组件中定义的悬停区域切换视频的播放/暂停。
---
### 4. 区块 2 布局(`src/App.tsx`)
**指定素材:**
- **图片叠加层(静态):** `https://github.com/dsMagnatov/Acreage-landing-assets/blob/main/02604201313.png?raw=true`
- **视频(悬停时显露):** `https://pikaso.cdnpk.net/private/production/4024859125/d070ae9c-55df-47aa-acbe-4ee66337855c-0.mp4?token=exp=1777075200~hmac=4202c1d0ec90137eb6dffa8e0db93ed7569a68b2016165d8b1b567f888869ff5`
- **SpotlightReveal baseRadius:** `520`
**区块容器:**
```tsx
<section
className="relative z-10 w-full h-[100dvh] overflow-hidden bg-black text-white"
style={{ boxShadow: '0 -20px 50px rgba(0,0,0,0.5)' }}
>
```
- 全视口高度、黑色背景、白色文字,并在顶部添加内收阴影,以便滚动时营造深度感。
**元素 1——SpotlightReveal 背景:**
```tsx
<SpotlightReveal
imageSrc="https://github.com/dsMagnatov/Acreage-landing-assets/blob/main/02604201313.png?raw=true"
videoSrc="https://pikaso.cdnpk.net/private/production/4024859125/d070ae9c-55df-47aa-acbe-4ee66337855c-0.mp4?token=exp=1777075200~hmac=4202c1d0ec90137eb6dffa8e0db93ed7569a68b2016165d8b1b567f888869ff5"
isPlaying={isSecondVideoPlaying}
baseRadius={520}
/>
```
**元素 2——两个不可见的悬停触发区(切换视频播放/暂停):**
```tsx
{/* Right-side hover zone */}
<div
className="absolute right-[calc(8%+100px)] bottom-[12%] w-[calc(50%-50px)] h-[calc(50%+230px)] z-30"
onMouseEnter={() => setIsSecondVideoPlaying(true)}
onMouseLeave={() => setIsSecondVideoPlaying(false)}
/>
{/* Left-side hover zone */}
<div
className="absolute left-[calc(8%+200px)] top-[calc(20%+190px)] w-[calc(15%+250px)] h-[calc(22.5%+130px)] -translate-y-full z-30"
onMouseEnter={() => setIsSecondVideoPlaying(true)}
onMouseLeave={() => setIsSecondVideoPlaying(false)}
/>
```
这些是触发视频的透明交互区域。让它们支持响应式:在移动端,简化为单个全宽触摸区域,或自动播放视频。
**元素 3——数据卡片(左上区域):**
定位为 `absolute left-[calc(8%+200px)] top-[20%] z-20`。宽度 `320px`。采用玻璃拟态效果的卡片,具体为:
- `background: rgba(0, 0, 0, 0.16)`、`backdrop-filter: blur(80px)`、`border: 1px solid rgba(255,255,255,0.1)`、`border-radius: 2px (rounded-sm)`。
- 内边距:`px-8 py-6`。
卡片内容:
1. **醒目数据:** `78%`,使用 `font-serif italic`,颜色 `#DA3A16`,字号 `72px`,`leading-[80px]`,`tracking-tight`。
2. **内联 SVG 图表**,位于数据旁边(置于一个 `w-[11px]` 包装容器中,但 SVG 本身为 `width: 160px, height: 80px`)。图表是一条 `#DA3A16` 色的波浪线,并带有相同橙红色的投影滤镜。指定 SVG 路径:
```svg
<svg style="width:160px;height:80px" viewBox="0 0 289 138" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_878_28499)">
<path d="M22.5 48.7306C39.7833 48.7306 49.34 54.94 63.1667 69.2965C76.9933 83.653 86.55 110.5 103.833 110.5C121.117 110.5 130.673 84.2876 144.5 59.2856C158.327 34.2837 167.883 19.5573 185.167 19.5573C202.45 19.5573 208.55 57.6673 225.833 57.6673C243.117 57.6673 249.217 19.5 266.5 19.5" stroke="#DA3A16" stroke-width="2"/>
</g>
<defs>
<filter id="filter0_d_878_28499" x="0" y="0" width="289" height="138" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="11.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.854902 0 0 0 0 0.227451 0 0 0 0 0.0862745 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_878_28499"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_878_28499" result="shape"/>
</filter>
</defs>
</svg>
```
3. **标题:** `"新一代缓震架构"`——`font-serif`、白色、`text-[15px]`、`tracking-[0.02em]`、大写、`mb-2`、`leading-tight`。
4. **副标题:** `"冲击吸收与能量回馈动力学"`——`font-serif`、`text-white/60`、`text-[13px]`。
移动端:将此卡片重新定位到 `left-4 top-[15%]` 或 `top-auto bottom-[55%]`,宽度缩减为 `w-[280px]`,数据字号缩放为 `text-[48px]`。
**元素 4——主视觉标题(左下):**
定位为 `absolute left-[8%] bottom-[12%] z-20`,`max-w-[500px]`。
```html
<h2 class="text-[44px] leading-[1.05] tracking-tight flex flex-col">
<span class="font-sans font-medium">Bringing Aerospace-</span>
<span class="font-sans font-medium">Grade Infrastructure</span>
<span class="font-serif font-normal pt-1">
<span class="not-italic">Directly To Your </span>
<span class="italic">Everyday</span>
</span>
<span class="font-serif italic font-normal">Urban Exploration</span>
</h2>
```
- 第 1–2 行使用 `font-sans`(Manrope)和 `font-medium`。
- 第 3–4 行使用 `font-serif`(Instrument Serif)。第 3 行混合使用非斜体的“直接融入你的”和斜体的“日常”。第 4 行全部使用斜体。
- 移动端:缩小为 `text-[24px] sm:text-[32px] md:text-[44px]`,定位为 `left-4 bottom-[8%]`,`max-w-[90%]`。
**元素 5——Nike 品牌 CTA 模块(右下):**
定位为 `absolute right-[calc(8%+100px)] bottom-[12%] z-20`,使用 `flex flex-col items-center` 垂直堆叠。
两个上下堆叠的方框,每个均为 `w-[180px]`:
1. **顶部方框(白色):** `bg-white`、`py-[6px]`,文字居中:`"冲击控制科学"`,样式为 `text-black font-serif text-[10px] uppercase font-bold tracking-[0.08em] leading-[16px]`。
2. **底部方框(Nike 红):** `bg-[#DA3A16]`、`h-[100px]`,内部居中放置白色 Nike swoosh SVG,`width="86"`。指定 swoosh 路径:
```svg
<svg width="86" viewBox="135.5 361.38 420.32 149.8" fill="white" xmlns="http://www.w3.org/2000/svg">
<path d="m181.86 511.11c-12.524-0.49755-22.77-3.9244-30.782-10.289-1.529-1.2159-5.1725-4.8616-6.3949-6.3992-3.2489-4.0853-5.4578-8.0611-6.931-12.472-4.5334-13.579-2.2002-31.397 6.6737-50.953 7.5979-16.742 19.322-33.347 39.776-56.344 3.013-3.384 11.986-13.281 12.043-13.281 0.0216 0-0.46749 0.84706-1.083 1.8786-5.3183 8.9082-9.8689 19.401-12.348 28.485-3.9823 14.576-3.502 27.085 1.4068 36.784 3.3862 6.6822 9.1913 12.47 15.719 15.67 11.428 5.5993 28.159 6.0625 48.592 1.3554 1.4068-0.32599 71.116-18.831 154.91-41.123 83.794-22.294 152.36-40.52 152.37-40.505 0.0237 0.0193-194.68 83.333-295.75 126.56-16.007 6.8431-20.287 8.5715-27.812 11.214-19.236 6.7551-36.467 9.9783-50.396 9.4251z"/>
</svg>
```
移动端:重新定位为 `right-4 bottom-[8%]`,宽度缩减为 `w-[140px]`,方框高度缩减为 `h-[80px]`。
---
### 5. 配色方案
| Token | 值 | 用途 |
|---|---|---|
| 背景 | `#000000` | 区块背景 |
| Nike 红/橙色 | `#DA3A16` | 数据文字、图表描边、图表发光阴影、Nike 标志方框 |
| 主要文字 | `#FFFFFF` | 标题、卡片标题 |
| 次要文字 | `rgba(255,255,255,0.6)` | 卡片副标题(`text-white/60`) |
| 卡片背景 | `rgba(0,0,0,0.16)` | 玻璃拟态卡片 |
| 卡片边框 | `rgba(255,255,255,0.1)` | 卡片边框 |
| CTA 顶部方框 | `#FFFFFF` 背景 / `#000000` 文字 | 标签方框 |
### 6. 排版规则
| 元素 | 字体 | 字重 | 字号 | 样式 |
|---|---|---|---|---|
| 标题第 1–2 行 | Manrope(`font-sans`) | 500(中等) | 44px | 常规 |
| 标题第 3–4 行 | Instrument Serif(`font-serif`) | 400(常规) | 44px | 斜体(第 3 行混合样式) |
| 数据数字 | Instrument Serif | 400 | 72px | 斜体 |
| 卡片标题 | Instrument Serif | 400 | 15px | 常规、大写 |
| 卡片副标题 | Instrument Serif | 400 | 13px | 常规 |
| CTA 标签 | Instrument Serif | 700(粗体) | 10px | 常规、大写 |
### 7. 移动端响应式要求
实现以下断点:
- **< 640px(移动端):** 垂直堆叠元素。数据卡片移至顶部居中并缩小尺寸。标题缩小为 `text-[24px]`,定位到 `left-4 bottom-[30%]`。Nike CTA 模块移至底部居中。将悬停区域改为单个覆盖整个区域的触摸区。由于移动端没有悬停,可考虑自动播放视频。将移动端的 `baseRadius` 缩小为 `280`。
- **640px-1024px(平板端):** 数据卡片调整为 `left-[5%] top-[18%]`,标题调整为 `text-[32px]`。CTA 模块调整为 `right-[5%]`。
- **> 1024px(桌面端):** 原样使用上述指定的桌面端位置。
### 8. 状态管理
```tsx
const [isSecondVideoPlaying, setIsSecondVideoPlaying] = useState(false);
```
由不可见的悬停区域控制。在移动端,默认设为 `true`(自动播放),或使用 `onTouchStart`/`onTouchEnd`。
---适用模型与稳定度
建议模型
继续探索
@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