肯·伯恩斯平移

静态图片在画框中缓慢放大并漂移,这是让照片像影片一样流动的纪录片手法。

  • CSS
  • 图片
  • 循环
  • 收录于
黄昏时的兰卡威

AI 提示词

用 CSS 为 React 首屏图片制作肯·伯恩斯效果。把 <img>(object-fit: cover)放进一个 overflow hidden、固定宽高比的画框中。用一个往返播放的 keyframe 为图片做动画:从 scale(1) translate(0, 0) 到 scale(1.18) translate(-4%, -3%),时长 14 秒,ease-in-out,infinite alternate,这样循环重新开始时不会跳动。把 transform-origin 设在主体附近(例如 60% 40%)。底部叠加一层从透明到 60% 黑色的渐变,并在角落显示说明文字“黄昏时的兰卡威”。添加 will-change: transform。画框离开屏幕时用 animation-play-state: paused 暂停动画(IntersectionObserver),在 prefers-reduced-motion 下显示静态图片。

源代码

Demo.tsx
'use client'

import { useEffect, useRef } from 'react'
import s from './demo.module.css'

export default function KenBurns({ labels }: { labels: Record<string, string> }) {
  const ref = useRef<HTMLElement>(null)

  useEffect(() => {
    const el = ref.current
    if (!el) return
    const io = new IntersectionObserver(([e]) => el.toggleAttribute('data-paused', !e.isIntersecting))
    io.observe(el)
    return () => io.disconnect()
  }, [])

  return (
    <figure ref={ref} className={s.frame}>
      <div className={s.scene} role="img" aria-label={labels.caption} />
      <figcaption className={s.caption}>{labels.caption}</figcaption>
    </figure>
  )
}
demo.module.css
.frame {
  --sky-top: oklch(42% 0.09 300);
  --sky-low: oklch(70% 0.14 45);
  --sun: oklch(88% 0.11 75);
  --sea: oklch(36% 0.07 250);
  --island: oklch(22% 0.03 280);
  --shade: oklch(0% 0 0 / 0.6);
  position: relative;
  width: min(300px, 100%);
  aspect-ratio: 16 / 10;
  max-width: 100%;
  margin: 0;
  overflow: hidden;
  border-radius: 14px;
}

.scene {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 22% 14% at 30% 62%, var(--island) 97%, transparent 100%),
    radial-gradient(ellipse 34% 18% at 78% 64%, var(--island) 97%, transparent 100%),
    linear-gradient(180deg, transparent 62%, var(--sea) 62%),
    radial-gradient(circle at 58% 56%, var(--sun) 0 10%, transparent 10.5%),
    linear-gradient(180deg, var(--sky-top), var(--sky-low) 62%);
  transform-origin: 60% 40%;
  will-change: transform;
  animation: drift 14s ease-in-out infinite alternate;
}

.frame[data-paused] .scene { animation-play-state: paused; }

.frame::after {
  content: '';
  position: absolute;
  inset: 50% 0 0;
  background: linear-gradient(transparent, var(--shade));
}

.caption {
  position: absolute;
  left: 12px;
  bottom: 10px;
  z-index: 1;
  font: 500 var(--text-sm) / 1.2 var(--font-body);
  color: var(--stage-ink);
}

@keyframes drift {
  from { transform: scale(1) translate(0, 0); }
  to { transform: scale(1.18) translate(-4%, -3%); }
}

@media (prefers-reduced-motion: reduce) {
  .scene { animation: none; }
}

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。