motionsites中文站 / Plush Sky Den
Plush Sky Den
aadmin2026-10-26 发布未分类5 阅读

Plush Sky Den

类型: · 收录:2026-10-26 23:38:09 · 源码:会员可见

[hidecontent type="vip1"]
Build a single standalone index.html (all HTML, CSS and JS inline, no build step) for a full-screen hero section called "Plush Sky Den". It must match the spec below exactly, including assets, layout, fonts, colours, animations, interaction physics and mobile behaviour.

======================================================================
1. CONCEPT
======================================================================
A full-viewport hero. The background is a ring of thick, fluffy, photoreal hot-pink fur with a clean white circular hole in the middle; on wide screens the fur runs off the left edge and fades into wispy tips over white on the right. Inside the hole floats a cute clay-style (plasticine) 3D hot air balloon: pink and cream checkerboard envelope, terracotta woven basket, a white clay bunny inside, and little pink clay sandbag pouches hanging on both sides of the basket. Three separate clay clouds float beside the balloon, not attached to it. The fur is a photo texture that a WebGL shader warps live, so it sways and waves like wind is blowing through it. Moving the mouse creates "wind": the fur parts and swirls into a vortex, the balloon drifts and turns, the basket swings like a real pendulum, and the clouds drift. There is no custom cursor; the normal system cursor stays.

======================================================================
2. ASSETS (use these exact CloudFront URLs, all are Higgsfield outputs)
======================================================================
Balloon 3D model (GLB, includes bunny + sandbag pouches, no clouds):
https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142632_f6127438-07b8-4bac-a44c-339350c784ef.glb

Cloud 3D model (GLB, single clay cloud; instance it 3 times):
https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142633_0a4b8bb1-d46d-4219-88a0-f7de74f38bc2.glb

Balloon fallback poster (PNG, shown as a flat billboard only if the balloon GLB fails):
https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142548_11a2ad58-f5fe-4ec7-862b-7008c5bfe161.png

Fur ring, WIDE framing (landscape, 1344x752):
  light: https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142546_67100789-2dce-48c8-a30b-29dc20fe45ff.png
  4K:    https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142701_6b1600f1-ad5e-4dbf-a3f6-b3186f58acb1.png
  aspect = 1344/752, hole centre cx = 0.4915 (from left), cy = 0.474 (from top), hole radius r = 0.2404 (fraction of image height)

Fur ring, TALL framing (portrait, 752x1344):
  light: https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142547_a91c910a-50bb-46de-a05b-93dccbcc5651.png
  4K:    https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20261017_142708_58aef7b4-c406-4f7b-9d93-c9c9790eeb6f.png
  aspect = 752/1344, hole centre cx = 0.498, cy = 0.3424, hole radius r = 0.1313

Progressive loading: always load the light image first and show it immediately. If (drawn image height in px × min(devicePixelRatio, 2)) > (native image height × 1.15), also load the 4K version and swap it in when it arrives. Don't draw the fur pass until a texture has loaded (the page shows plain white until then).

======================================================================
3. LIBRARIES AND FONTS
======================================================================
three.js 0.160.0 through an import map:
  "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
Use GLTFLoader and RoomEnvironment from the addons.

Google Fonts (preconnect to fonts.googleapis.com and fonts.gstatic.com):
https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@125,900&family=Outfit:wght@300;400;500&display=swap
- Display: "Archivo", weight 900, font-stretch 125%, font-variation-settings "wdth" 125, uppercase, letter-spacing -0.01em, line-height 0.86
- Body/UI: "Outfit", system-ui, sans-serif

======================================================================
4. HTML (exact structure and copy)
======================================================================
<title>Plush Sky Den — Drift up high</title>
<meta name="description" content="Plush Sky Den — a cosy small studio for whimsical 3D skies.">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">

<main class="hero" id="hero">
  <canvas id="stage" aria-hidden="true"></canvas>

  <nav class="nav" aria-label="Primary">
    <a href="#work">Reel</a>
    <a href="#studio">Makers</a>
    <a href="#playground">Sketchbook</a>
    <a href="#about">Story</a>
  </nav>

  <a class="brand display" href="#" aria-label="Plush Sky Den home">
    <span class="line" data-scramble>Plush</span>
    <span class="line" data-scramble>Sky</span>
    <span class="line"><span data-scramble>Den</span><span class="reg" aria-hidden="true"><span>R</span></span></span>
  </a>

  <h1 class="headline display" id="headline">
    <span class="line" data-scramble>Drift</span>
    <span class="line" data-scramble>up high</span>
  </h1>

  <a class="cta" href="#work">
    See reel
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg>
  </a>

  <div class="loader" id="loader">Ascending…</div>
</main>

======================================================================
5. CSS (exact values)
======================================================================
:root {
  --paper: #fdfbfc; --ink: #120a0e; --hot: #e8317a; --deep: #9e0b47;
  --blush: #ffc2d9; --white: #ffffff; --pad: clamp(18px, 3.4vw, 52px);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body { background: var(--paper); color: var(--ink); font-family: "Outfit", system-ui, sans-serif; overflow-x: hidden; -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration: none; }

.hero { position: relative; width: 100%; height: 100vh; height: 100svh; min-height: 560px; overflow: hidden; background: var(--paper); }
#stage { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: pan-y; }

.nav { position: absolute; z-index: 5; top: clamp(28px, 4.6vw, 52px); left: 22.3%; display: flex; flex-direction: column; gap: 2px; font-weight: 300; font-size: clamp(15px, 1.55vw, 21px); line-height: 1.12; color: #fff; text-shadow: 0 1px 14px rgba(110,0,40,.25); }
.nav a { position: relative; width: max-content; transition: transform .35s cubic-bezier(.2,.8,.2,1), opacity .3s; }
.nav a:hover { transform: translateX(6px); }
.nav a::before { content: ""; position: absolute; left: -14px; top: 50%; width: 6px; height: 6px; border-radius: 50%; background: currentColor; transform: translateY(-50%) scale(0); transition: transform .3s cubic-bezier(.2,.8,.2,1); }
.nav a:hover::before { transform: translateY(-50%) scale(1); }

.display { font-family: "Archivo", sans-serif; font-weight: 900; font-stretch: 125%; font-variation-settings: "wdth" 125; text-transform: uppercase; letter-spacing: -.01em; line-height: .86; }
.brand { position: absolute; z-index: 5; top: clamp(26px, 4.2vw, 48px); right: var(--pad); font-size: clamp(26px, 3.6vw, 52px); color: var(--ink); text-align: left; }
.brand .reg { display: inline-grid; place-items: center; width: .62em; height: .62em; margin-left: .08em; border: .09em solid var(--hot); border-radius: 50%; color: var(--hot); font-size: .62em; line-height: 1; vertical-align: .12em; }
.brand .reg span { font-size: .55em; }

.headline { position: absolute; z-index: 5; left: var(--pad); bottom: clamp(30px, 6vw, 70px); font-size: clamp(38px, 5.6vw, 84px); color: #fff; text-shadow: 0 2px 30px rgba(110,0,40,.25); cursor: default; }
.line { display: block; white-space: pre; }

.cta { position: absolute; z-index: 5; left: 50%; bottom: clamp(30px, 6vw, 70px); transform: translateX(-50%); display: inline-flex; align-items: center; gap: 34px; padding: 12px 22px 12px 26px; border: 1.5px solid rgba(255,255,255,.9); border-radius: 999px; color: #fff; font-size: clamp(15px, 1.35vw, 18px); font-weight: 400; background: rgba(255,255,255,.06); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); transition: background .35s, color .35s, gap .35s cubic-bezier(.2,.8,.2,1); }
.cta svg { width: 18px; height: 18px; transition: transform .35s cubic-bezier(.2,.8,.2,1); }
.cta:hover { background: #fff; color: var(--deep); gap: 44px; }
.cta:hover svg { transform: translateX(3px); }

a:focus-visible { outline: 2px solid #fff; outline-offset: 5px; border-radius: 6px; box-shadow: 0 0 0 5px var(--deep); }
.cta:focus-visible { border-radius: 999px; }

.loader { position: absolute; z-index: 4; left: 50%; top: 50%; transform: translate(-50%,-50%); font-size: 13px; letter-spacing: .14em; text-transform: uppercase; color: var(--deep); opacity: .7; transition: opacity .6s; pointer-events: none; }
.loader.done { opacity: 0; }

/* intro fade */
.nav, .cta, .brand { opacity: 0; transition: opacity 1s ease .2s, transform .35s; }
.is-ready .nav, .is-ready .cta, .is-ready .brand { opacity: 1; }

/* mobile */
@media (max-width: 760px) {
  .hero { min-height: 600px; }
  .nav { left: var(--pad); top: 22px; font-size: 15px; }
  .brand { top: 22px; font-size: 24px; }
  .headline { bottom: 104px; font-size: clamp(36px, 11.5vw, 54px); }
  .cta { left: var(--pad); transform: none; bottom: 34px; gap: 28px; }
}
@media (max-width: 760px) and (min-aspect-ratio: 1/1) {   /* phone landscape */
  .headline { bottom: 26px; font-size: 34px; }
  .cta { left: auto; right: var(--pad); bottom: 26px; }
}
@media (prefers-reduced-motion: reduce) { .nav a, .cta, .cta svg { transition: none; } }

======================================================================
6. RENDERER AND LAYOUT
======================================================================
- WebGLRenderer({ canvas: #stage, antialias: true, alpha: false, powerPreference: "high-performance" }), clear colour #ffffff, autoClear = false, outputColorSpace SRGB, ACESFilmic tone mapping, exposure 1.05.
- Pixel ratio = min(devicePixelRatio, 2), or min(devicePixelRatio, 1.5) when innerWidth < 760.
- Choose the TALL fur image when width/height < 0.85, otherwise WIDE.
- Cover-fit the chosen image, centred: drawH = (w/h > aspect) ? w/aspect : h.
- Define "fur units": 1 unit = drawH/2 px; the screen centre is (0,0) and +y is up.
  Hole centre in units: cx = (img.cx - .5) * 2 * aspect, cy = (.5 - img.cy) * 2. Hole radius in units: hole = img.r * 2.
- Recompute everything on window resize.

Each frame draws two passes: (1) the fur pass with an orthographic camera spanning ±(w/2/u) by ±(h/2/u); (2) clearDepth(); (3) the 3D balloon scene.

======================================================================
7. FUR SHADER (full-screen plane, ShaderMaterial, depthTest/depthWrite off)
======================================================================
Vertex: pass the world-space xy (modelMatrix * position) as varying vP. Scale the 2x2 plane to cover the ortho view.
Uniforms: uTex, uImgHalf = (aspect, 1), uCenter = hole centre, uHole, uTime, uMouse, uVel, uSwirl, uGust, uWindAmp (1, or 0.25 with reduced motion).
Include standard 2D simplex noise snoise(vec2) (Ashima / Stefan Gustavson).
Do not tone-map or colour-convert the texture; output its pixels as-is so the colours match the photo.

Fragment:
  p = vP - uCenter; r = length(p); a = atan(p.y, p.x); c = vec2(cos a, sin a)
  bend = smoothstep(uHole*.95, uHole*2.1, r) * (.65 + .35*smoothstep(uHole, uHole*3.5, r))   // roots still, tips move
  sway  = snoise(c*1.4 + vec2(uTime*.06, -uTime*.05)) * .04
  wave  = sin(r*7. - uTime*1.6 + snoise(c*1.8 + 2.)*2.5) * .014
  wave2 = sin(r*15. - uTime*2.4 + a*3. + snoise(c*3.)*2.) * .004
  da = (sway + (wave+wave2)*uWindAmp*(1.+uGust*.8) + uSwirl*.35) * bend
  q = uCenter + r*vec2(cos(a-da), sin(a-da))
  q -= c * sin(uTime*.9 + a*2. + r*3.) * .006 * bend * uWindAmp        // breathing
  dm = vP - uMouse; near = exp(-dot(dm,dm)/.06); wide = exp(-dot(dm,dm)/.4)
  q -= normalize(dm + 1e-5) * near * .045 * bend                        // cursor parts the fur
  q -= uVel * wide * .4 * bend                                          // fur dragged by the air
  uv = q / uImgHalf * .5 + .5
  gl_FragColor = texture2D(uTex, clamp(uv, vec2(.0005), vec2(.9995)))
Texture: LinearMipmapLinear min filter, max anisotropy.

======================================================================
8. 3D SCENE (balloon + clouds)
======================================================================
- PerspectiveCamera fov 30, near 0.1, far 100, at (0, 0, 8).
- scene.environment = PMREM of RoomEnvironment (sigma 0.04).
- Lights: HemisphereLight(0xfff4f8, 0xf3b6c9, 0.9); DirectionalLight 0xffffff 1.6 at (-3, 4, 5); rim DirectionalLight 0xffc6da 0.9 at (4, 1, -3).
- Hierarchy: anchor > swing > body. Place anchor at the hole centre: worldH = 2*8*tan(15°), wpu = worldH/h*u, anchor.position = (cx*wpu, cy*wpu, 0), anchor.scale = hole*2*1.0*wpu. One local unit = one balloon height = the hole diameter.
- Clay material on every mesh of both GLBs (replace the existing material): MeshPhysicalMaterial { map: original map (sRGB), color white if mapped else original, normalMap: original, roughness 0.82, metalness 0, sheen 0.6, sheenRoughness 0.8, sheenColor 0xffd6e4, clearcoat 0.08, clearcoatRoughness 0.6, bumpMap: procedural fingerprint noise, bumpScale 0.6 }.
  Procedural bump: a 256x256 canvas of random greys (128 ± 30), redrawn with ctx.filter = "blur(1.4px)", as a CanvasTexture with RepeatWrapping and repeat (6, 6).
- Balloon: normalise to unit height, centred on the origin, then move the model down 0.38 and set swing.position.y = 0.38, so the pivot sits near the envelope top and the basket swings like a pendulum. Add it to body. On load error, use the poster PNG on a 1x1 plane instead (same offsets).
- Clouds: normalise the cloud GLB to unit WIDTH, clone it 3 times, scale each clone by size/2, and give each its own group (child of anchor, z = 0.15). Position = (x/2, y/2) in local units. Positions and sizes are in hole radii, as in the reference:
    cloud 1: x -0.80, y  0.14, size 0.50, rotation.y  0.5, mass 1.00   (small, upper-left)
    cloud 2: x  0.82, y -0.12, size 0.60, rotation.y -0.4, mass 1.25   (bigger, right)
    cloud 3: x  0.60, y -0.56, size 0.28, rotation.y  0.2, mass 0.70   (tiny, lower-right)
- The loader text fades out (.done) once both GLBs have finished (success or failure).

======================================================================
9. INTERACTION PHYSICS (smooth, frame-rate independent)
======================================================================
Integrate everything with fixed sub-steps of h = 1/240 s using an accumulator (clamp each frame's delta to 0.1 s). Use semi-implicit Euler. m = 1, or 0.35 with prefers-reduced-motion.
soft(v, max) = max * tanh(v/max).

Pointer: on pointermove over the hero, convert to fur units: x = (clientX - left - w/2)/u, y = -(clientY - top - h/2)/u. Set inside = true; pointerleave sets inside = false.
Idle blend: idleW eases toward (inside ? 0 : 1) with factor 1 - exp(-h/1.6). Idle breeze path: ix = cos(t*.23)*.8 + cx, iy = sin(t*.31)*.45 + cy. Target = mix(last pointer, idle path, idleW).

Each step:
1. Smoothed cursor P (critically damped, ω = 7): v += (ω²(target - P) - 2ωv)·h; P += v·h.
2. Air W: cursor speed capped with soft(speed, 1.6); W += (cappedVel - W)·(1 - exp(-h/0.45)).
3. Fur vortex: torque = (rx·Wy - ry·Wx) / max(|r|, 0.6), where r = P - hole centre.
   swV += (torque·1.6·m - 2.1²·sw - 2·0.55·2.1·swV)·h; sw += swV·h.
4. Balloon drift (pivot):
   ax = 1.4(Wx·.25·m - dxV) - 2.2·dx - .8·dxV
   ay = 1.4(Wy·.14·m - dyV) - 2.2·dy - .8·dyV
   integrate dx, dy.
5. Basket pendulum (g/ℓ = 5.8, ℓ = 0.75, damping 0.85):
   thV += (-5.8·sin th - .85·thV - (ax/.75)·cos th + .5·m·Wx/.75)·h
   phV += (-5.8·sin ph - .85·phV + (ay/.75)·.6 + sin(t·.55)·.04·m)·h
6. Clouds (each): kd = 1.6/mass, kh = 1.8/mass, cd = .9
   vx += (kd(Wx·.3·m - vx) - kh·px - cd·vx)·h ; vy += (kd(Wy·.18·m - vy) - kh·py - cd·vy)·h ; integrate px, py.
7. Balloon look-at (critically damped, ω = 2.4): yaw target = soft((P.x - cx)/1.4, 1)·.5·m; pitch target = -soft((P.y - cy)/1.4, 1)·.14·m.

Apply each frame (t = simulated time):
  uniforms: uTime = t, uMouse = P, uVel = W·0.09, uSwirl = soft(sw, .5), uGust = soft(|W|/1.6, 1)
  body.rotation.y = yaw + sin(t·.4)·.06·m; body.rotation.x = pitch
  swing.rotation.z = th; swing.rotation.x = ph; swing.position.x = dx
  swing.position.y = .38 + dy + sin(t·1.1)·.02·m       (gentle bob)
  cloud i (phase = i·2.1): position = (x/2 + px + sin(t·.35+phase)·.012·m, y/2 + py + sin(t·.8+phase)·.018·m, .15); rotation.z = -vx·.6 + sin(t·.5+phase)·.03·m

Intended feel: buttery and physical. A hard flick swings the basket about 9°, it swings back past centre and settles within about 4 s. Circling the cursor around the ring slowly winds the fur into a vortex that springs back when you stop. Nothing should snap or jitter.

======================================================================
10. TEXT SCRAMBLE ANIMATION
======================================================================
Glyph set: "ABCDEFGHJKLMNOPQRSTUVWXYZ#%&*+=/<>".
On load, store each [data-scramble] element's text in dataset.text and replace it with spaces of the same length. After document.fonts.ready: add class "is-ready" to body (this fades in the nav, brand and CTA), then scramble each element with delay 250 + index·160 ms.
scramble(el, delay): duration = 380 + length·70 ms. For progress p, character i (reveal point = (i+1)/length) shows the final character if p ≥ reveal, a random glyph if p > reveal - .55, otherwise a space. Spaces stay spaces. Re-render every animation frame until done.
On pointerenter of #headline, re-scramble its lines with delays i·90 ms.
With prefers-reduced-motion, show the final text immediately.

======================================================================
11. RESPONSIVE RESULT TO VERIFY
======================================================================
- Desktop 16:9: fur fills the left and centre and runs off the left edge, with wispy tips over white on the right. Nav is white at 22.3% from the left, top. "PLUSH / SKY / DEN" with a pink circled R sits black over white at the top right. "DRIFT / UP HIGH" is large white text at the bottom left. The glassy "See reel →" pill sits at the bottom centre. The balloon fills the hole, with clouds to its left and right.
- Phone (375x812): the TALL fur covers the whole screen and the hole sits about 34% from the top. Nav top-left at 15px; brand top-right at 24px; headline at bottom 104px; the CTA pill is left-aligned at bottom 34px. No horizontal scroll.
- Phone landscape: headline at bottom-left at 34px, CTA at bottom-right.
- Accessibility: visible focus rings, canvas aria-hidden, reduced motion respected (wind amplitude 0.25, physics ×0.35, no scramble).
[/hidecontent]
← 返回首页