lab-css

硝子の錦鯉

曇り硝子の向こうを錦鯉が泳ぐ。offset-pathのモーションパスで泳跡を描き、backdrop-filterの硝子とmaskの光模様が水面を作る。

全画面で見る
backdrop-filteroffset-pathmask操作: 鑑賞のみ

対応ブラウザ

CSS機能Chrome / EdgeFirefoxSafari備考
backdrop-filter対応対応対応全主要ブラウザ対応
offset-path対応対応対応モーションパス
mask対応対応対応全主要ブラウザ対応

コード

表示中のコードは作品の実行に使われているものと同一ソースです。そのままコピーして試せます。

/* 硝子の錦鯉: offset-pathの泳跡、backdrop-filterの曇り硝子、
   maskで淡くした水面の光模様 */

.work-glass-koi {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #06131c;
}

.gk-pond {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(90% 70% at 30% 20%, rgba(38, 108, 130, 0.5), transparent 65%),
    radial-gradient(80% 80% at 75% 85%, rgba(16, 66, 92, 0.6), transparent 70%),
    linear-gradient(160deg, #0a2532 0%, #061722 60%, #04101a 100%);
}

/* 水面の光模様。maskで中央だけ淡く残す */
.gk-caustics {
  position: absolute;
  inset: 0;
  background-image:
    repeating-radial-gradient(circle at 28% 30%,
      rgba(150, 220, 235, 0.14) 0 2px, transparent 2px 42px),
    repeating-radial-gradient(circle at 72% 68%,
      rgba(150, 220, 235, 0.1) 0 2px, transparent 2px 56px);
  -webkit-mask: radial-gradient(80% 70% at 50% 45%, #000 30%, transparent 85%);
  mask: radial-gradient(80% 70% at 50% 45%, #000 30%, transparent 85%);
  animation: gk-shimmer 9s ease-in-out infinite alternate;
}

@keyframes gk-shimmer {
  to { background-position: 30px -20px, -40px 26px; }
}

/* ---- 錦鯉 ---- */

.gk-lane {
  position: absolute;
  pointer-events: none;
}

.gk-lane-1 { top: 12%; left: 6%; width: 62%; height: 60%; }
.gk-lane-2 { top: 40%; left: 34%; width: 58%; height: 52%; }

/* 泳跡はoffset-pathの閉じたパス。offset-rotate: autoで進行方向を向く */
.gk-koi {
  position: absolute;
  width: 92px;
  height: 34px;
  offset-path: path("M 40,60 C 160,10 300,40 380,90 C 420,120 380,180 280,190 C 170,200 60,170 30,120 C 15,95 20,72 40,60 Z");
  offset-rotate: auto;
  animation: gk-swim var(--dur, 22s) linear infinite;
}

.gk-koi-gold {
  --dur: 30s;
  width: 74px;
  height: 28px;
  animation-delay: -12s;
  offset-path: path("M 30,40 C 140,90 300,20 360,80 C 400,125 330,170 220,160 C 120,150 40,130 20,90 C 10,65 12,50 30,40 Z");
}

@keyframes gk-swim {
  to { offset-distance: 100%; }
}

/* 胴体: 白地に緋の斑。尾びれは::after */
.gk-koi::before {
  content: "";
  position: absolute;
  inset: 0 18% 0 0;
  border-radius: 58% 70% 70% 58% / 55% 55% 55% 55%;
  background:
    radial-gradient(30% 55% at 24% 35%, #e34a33 0 60%, transparent 62%),
    radial-gradient(26% 50% at 58% 62%, #e34a33 0 58%, transparent 60%),
    radial-gradient(18% 40% at 82% 30%, #1f2430 0 55%, transparent 57%),
    linear-gradient(180deg, #f7f3ea, #d9d2c4);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.35);
}

.gk-koi-gold::before {
  background:
    radial-gradient(30% 55% at 30% 40%, #d98324 0 60%, transparent 62%),
    radial-gradient(24% 45% at 66% 58%, #d98324 0 56%, transparent 58%),
    linear-gradient(180deg, #f5ead2, #dcc79a);
}

.gk-koi::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -4%;
  width: 34%;
  height: 130%;
  translate: 0 -50%;
  clip-path: polygon(0 50%, 100% 0, 78% 50%, 100% 100%);
  background: linear-gradient(90deg, #e8e0d0, rgba(232, 224, 208, 0.35));
  transform-origin: left center;
  animation: gk-tail 0.9s ease-in-out infinite alternate;
}

.gk-koi-gold::after {
  background: linear-gradient(90deg, #ecd9ab, rgba(236, 217, 171, 0.35));
}

@keyframes gk-tail {
  from { transform: rotate(-14deg); }
  to { transform: rotate(14deg); }
}

/* ---- 波紋 ---- */

.gk-ripple {
  position: absolute;
  width: 40px;
  aspect-ratio: 1;
  border: 1.5px solid rgba(180, 230, 240, 0.35);
  border-radius: 50%;
  animation: gk-ripple 5s ease-out infinite;
}

.gk-r1 { top: 24%; left: 22%; }
.gk-r2 { top: 62%; left: 70%; animation-delay: -2.5s; }

@keyframes gk-ripple {
  from {
    transform: scale(0.2);
    opacity: 0.9;
  }

  to {
    transform: scale(3.4);
    opacity: 0;
  }
}

/* ---- 曇り硝子 ---- */

.gk-glass {
  position: absolute;
  inset: 14% 20%;
  z-index: 10;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 14px;
  background:
    repeating-linear-gradient(115deg,
      rgba(255, 255, 255, 0.045) 0 3px, transparent 3px 9px),
    linear-gradient(135deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.03));
  backdrop-filter: blur(9px) saturate(135%) brightness(1.08);
  -webkit-backdrop-filter: blur(9px) saturate(135%) brightness(1.08);
}

.gk-glass-label {
  position: absolute;
  right: 12px;
  bottom: 8px;
  color: rgba(255, 255, 255, 0.4);
  font-size: 11px;
  letter-spacing: 0.2em;
}

/* backdrop-filter非対応環境では半透明の磨り硝子風にフォールバック */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .gk-glass {
    background: rgba(210, 230, 240, 0.55);
  }
}

/* 動きへの配慮: 停止し、CSSのみのチェックボックスでオプトイン再生 */
@media (prefers-reduced-motion: reduce) {
  .work-glass-koi * {
    animation-play-state: paused !important;
  }

  .work-glass-koi .motion-optin {
    display: inline-flex;
  }

  .work-glass-koi:has(.motion-optin input:checked) * {
    animation-play-state: running !important;
  }
}