加载中…
创作者 · MotionSites
查看原始来源 ↗
Prompt
构建一个独立的 React + TypeScript + Tailwind CSS 区块组件。这是一个左右两半的香水产品展示:LEFT 是循环播放的视频,RIGHT 是青柠绿色产品面板。在移动端,它会垂直堆叠,产品面板位于视频 ABOVE(通过 `flex-col-reverse` 实现)。以下每个值都必须精确一致。
## 技术栈
- React 18 + TypeScript
- Tailwind CSS 3(默认配置、默认断点:`sm:640px`、`md:768px`)
- Vite
- 不使用额外的包。不需要图标库。
## 常量
```ts
const TEXT_COLOR = '#000000';
const BG_LIME = '#BDE84F';
const EASE = 'cubic-bezier(0.22, 1, 0.36, 1)';
```
## 动画辅助函数
```ts
function anim(visible: boolean, delay: number, opts: { y?: number; x?: number; duration?: number } = {}) {
const { y = 20, x = 0, duration = 1600 } = opts;
const translateFrom = y !== 0 ? `translateY(${y}px)` : x !== 0 ? `translateX(${x}px)` : 'none';
return {
style: {
opacity: visible ? 1 : 0,
transform: visible ? 'translate(0,0)' : translateFrom,
transition: `opacity ${duration}ms ${EASE} ${delay}ms, transform ${duration}ms ${EASE} ${delay}ms`,
} as React.CSSProperties,
};
}
```
## 产品数据
```ts
const WILD_PRODUCT = {
name: 'Eau So Extra',
size: '100 ml / 3.3 oz',
image: 'https://images.higgs.ai/?default=1&output=webp&url=https%3A%2F%2Fd8j0ntlcm91z4.cloudfront.net%2Fuser_38xzZboKViGWJOttwIXH07lWA1P%2Fhf_20260511_151621_4fba6892-ed21-4c2e-8cb3-0bd2ec2abefa.png&w=1280&q=85',
notes: [
{ label: 'Top', ingredient: 'BANANA BLOSSOM ACCORD' },
{ label: 'Heart', ingredient: 'CHOCOLATE DAISY ACCORD' },
{ label: 'Base', ingredient: 'VETIVER OIL' },
],
};
```
## 视频 URL(精确、逐字一致)
```
https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260511_151818_65bb22c5-33ae-4e23-85ea-0a3dd89957c2.mp4
```
---
## 组件:`ProductPanel`
这是一个与 Section 2 共用的可复用组件。在此区块中,调用它时传入 `noteStyle="bold"`。
### Props
```ts
{
bg: string;
product: { name: string; size: string; image: string };
notes: { label: string; ingredient: string }[];
visible: boolean;
noteStyle?: 'normal' | 'bold'; // defaults to 'normal'
}
```
### 外层包装器
```jsx
<div
className="relative flex flex-col px-6 md:px-8 pt-6 md:pt-8 pb-8 md:pb-10"
style={{ backgroundColor: bg, minHeight: '100%' }}
>
```
### 1. 顶部标签行
```jsx
<div
className="flex items-start justify-between mb-auto"
{...anim(visible, 0, { y: 12, duration: 1400 })}
>
<span className="text-xs font-normal" style={{ color: TEXT_COLOR }}>
{noteStyle === 'bold' ? 'Daisy wild' : 'Daisy love'}
</span>
<span className="text-xs font-normal" style={{ color: TEXT_COLOR }}>
{noteStyle === 'bold' ? 'Playful' : 'Sweet'}
</span>
</div>
```
对于此区块(`noteStyle="bold"`),左侧标签显示 **"Daisy wild"**,右侧显示 **"Playful"**。
### 2. 产品图片块
```jsx
<div
className="flex flex-col items-center py-8"
style={{ flex: 1, justifyContent: 'center', ...anim(visible, 300, { y: 40, duration: 1800 }).style }}
>
```
#### 图片容器
```jsx
<div
className="overflow-hidden"
style={{
width: 'clamp(140px, 40%, 220px)',
aspectRatio: '220/340',
backgroundColor: '#D9D9D9',
borderRadius: '2px',
flexShrink: 0,
}}
>
<img
src={product.image}
alt={product.name}
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
/>
</div>
```
#### 说明文字(位于图片下方)
```jsx
<div className="text-center mt-4" {...anim(visible, 600, { y: 10, duration: 1400 })}>
<p className="text-sm font-normal" style={{ color: TEXT_COLOR }}>{product.name}</p>
<p className="text-xs font-normal mt-1" style={{ color: TEXT_COLOR }}>{product.size}</p>
</div>
```
### 3. 底部行——香调 + 按钮
```jsx
<div className="flex items-end justify-between gap-4 flex-wrap">
```
#### 香调列(左侧)
```jsx
<div className="flex flex-col gap-0.5" {...anim(visible, 900, { y: 16, duration: 1400 })}>
```
对于每个香调,渲染包含两个 `<p>` 的 `<div key={note.ingredient}>`:
- 标签:`<p className="text-xs leading-snug" style={{ color: TEXT_COLOR, fontWeight: noteStyle === 'bold' ? 700 : 400 }}>{note.label}</p>`
- 成分:`<p className="text-xs font-bold tracking-widest uppercase leading-snug" style={{ color: TEXT_COLOR }}>{note.ingredient}</p>`
对于此区块(`noteStyle="bold"`),香调标签("Top"、"Heart"、"Base")以 `fontWeight: 700` 渲染。无论如何,成分行始终使用 `font-bold`。
#### SHOP NOW 按钮(右侧)
```jsx
<button
className="text-xs font-bold tracking-widest uppercase border px-6 py-3 relative group shrink-0"
style={{
color: TEXT_COLOR,
borderColor: TEXT_COLOR,
backgroundColor: 'transparent',
...anim(visible, 1150, { y: 16, duration: 1400 }).style,
}}
>
<span className="relative z-10 group-hover:text-black transition-colors duration-500">SHOP NOW</span>
<span
className="absolute inset-0 origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-500 ease-out"
style={{ backgroundColor: '#ffffff' }}
/>
</button>
```
按钮:`1px` 实线边框,颜色为 `#000000`。hover 时,白色填充在 500ms 内从左侧缩放展开。文字保持在上层(`z-10`)。
---
## 组件:`WildScentSection`
### 可见性触发器
```ts
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting) setVisible(true); },
{ threshold: 0.15 }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
```
单次触发:一旦 15% 可见,`visible` 就会永久变为 `true`,触发所有错开的动画。
### 布局结构
```jsx
<section ref={ref} className="relative w-full">
<div className="flex flex-col-reverse md:grid md:min-h-screen" style={{ gridTemplateColumns: '1fr 1fr' }}>
```
**与 Section 2 的关键区别:**这里使用 `flex-col-reverse`(而不是 `flex-col`)。DOM 顺序是:先放视频 div,再放 ProductPanel。但在移动端,`flex-col-reverse` 会在视觉上将它们翻转,使产品面板出现在视频 ABOVE。
### 内部的三个子元素(按 DOM 顺序):
#### 子元素 1:桌面端视频面板(桌面端左半部分,在 `md` 以下隐藏)
```jsx
<div className="hidden md:block relative overflow-hidden" style={{ backgroundColor: '#111', minHeight: '100%' }}>
<video autoPlay muted loop playsInline className="absolute inset-0 w-full h-full object-cover">
<source src="https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260511_151818_65bb22c5-33ae-4e23-85ea-0a3dd89957c2.mp4" type="video/mp4" />
</video>
</div>
```
#### 子元素 2:移动端视频条(在 `md` 及以上隐藏)
```jsx
<div className="md:hidden relative overflow-hidden" style={{ height: '75vw', backgroundColor: '#111' }}>
<video autoPlay muted loop playsInline className="absolute inset-0 w-full h-full object-cover">
<source src="https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260511_151818_65bb22c5-33ae-4e23-85ea-0a3dd89957c2.mp4" type="video/mp4" />
</video>
</div>
```
#### 子元素 3:ProductPanel(桌面端右半部分,移动端视觉上位于顶部)
```jsx
<ProductPanel
bg={BG_LIME}
product={WILD_PRODUCT}
notes={WILD_PRODUCT.notes}
visible={visible}
noteStyle="bold"
/>
```
调用时传入 `noteStyle="bold"`,这会改变:
- 顶部标签:`"Daisy wild"` / `"Playful"`(而不是 `"Daisy love"` / `"Sweet"`)
- 香调标签:`fontWeight: 700`(而不是 `400`)
---
## 响应式行为
| Viewport | 布局 | 视觉顺序(从上到下 / 从左到右) |
|---|---|---|
| < 768px | `flex flex-col-reverse` | 产品面板(青柠绿色,全宽),然后是视频条(`height: 75vw`,全宽) |
| >= 768px | `grid 1fr 1fr`、`min-h-screen` | 视频(左半部分),然后是产品面板(右半部分,青柠绿色) |
`flex-col-reverse` 技巧:DOM 顺序是 [video-desktop, video-mobile, panel]。在移动端,`flex-col-reverse` 将视觉顺序反转为 [panel, video-mobile, video-desktop(hidden)]。在桌面端,`md:grid` 会覆盖 flex,网格按照 DOM 顺序从左到右放置它们:左侧视频,右侧面板。
## 动画错开时间线
所有动画都在该区块有 15% 滚动进入视口时触发。Easing:`cubic-bezier(0.22, 1, 0.36, 1)`。
| 元素 | 延迟 | 持续时间 | 方向 | 距离 |
|---|---|---|---|---|
| 顶部标签("Daisy wild" / "Playful") | 0ms | 1400ms | translateY | 12px |
| 产品图片块 | 300ms | 1800ms | translateY | 40px |
| 说明文字(名称 + 容量) | 600ms | 1400ms | translateY | 10px |
| 香调列 | 900ms | 1400ms | translateY | 16px |
| SHOP NOW 按钮 | 1150ms | 1400ms | translateY | 16px |
每个元素初始状态为 `opacity: 0` + 向下位移,随后过渡为 `opacity: 1` + `translate(0,0)`。
## 使用的颜色
- `#BDE84F` — 产品面板背景(青柠绿色)
- `#000000` — 所有文字、按钮边框
- `#D9D9D9` — 图片占位背景
- `#111` — 视频面板背景(加载期间)
- `#ffffff` — 按钮 hover 填充
## 字体
不使用自定义字体或 Google Fonts。所有文字均使用 Tailwind 默认的 sans-serif 系统字体栈。
## SVG / 图标
此区块中没有。
## 与 Section 2(ScentFinder)的关键区别
| 方面 | Section 2(ScentFinder) | Section 3(WildScent) |
|---|---|---|
| 背景颜色 | `#4BB3ED`(天蓝色) | `#BDE84F`(青柠绿色) |
| 面板位置(桌面端) | LEFT 半部分 | RIGHT 半部分 |
| 视频位置(桌面端) | RIGHT 半部分 | LEFT 半部分 |
| Flex 方向(移动端) | `flex-col`(面板在上,视频在下) | `flex-col-reverse`(通过反转使面板在上,视频在下) |
| 顶部标签 | "Daisy love" / "Sweet" | "Daisy wild" / "Playful" |
| 香调标签字重 | `fontWeight: 400`(normal) | `fontWeight: 700`(bold) |
| `noteStyle` prop | `'normal'`(默认) | `'bold'` |
| 产品名称 | Eau So Sweet | Eau So Extra |
| 产品容量 | 100 ml / 3.3 oz | 100 ml / 3.3 oz |
| 视频 URL | `...151802_1bbf9a81...` | `...151818_65bb22c5...` |
| 香调内容 | Fruity top / WHITE RASPBERRIES、Floral heart / DAISY TREE PETALS、Feminine base / SUGAR MUSKS | Top / BANANA BLOSSOM ACCORD、Heart / CHOCOLATE DAISY ACCORD、Base / VETIVER OIL |适用模型与稳定度
建议模型
继续探索
@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