Frontend Lab
← 回到所有效果
F10S001framer-motion

Smooth Scroll (Lenis)

平滑滾動

難度 ★★★★★標籤 滾動 · lenis · scrollTo3 min read

效果簡述

把整頁滾動換成 Lenis 的 RAF 平滑版本。重點是「真的需要嗎」—— 預設 原生滾動更快、更省電。Lenis 只是「同事跟主管都覺得很滑」時的好工具。

Lenis 平滑滾動 · duration 1.2

試試這兩個按鈕

預設|duration 1.2

Claude Code Prompt

💬 Claude Code Prompt
參考:S001 smooth scroll
技術:lenis(單獨用,不要全站套,會跟其他 useScroll 打架)

Demo 行為:
- new Lenis()
- requestAnimationFrame loop 呼叫 lenis.raf(time)
- 提供 scrollTo(target) 按鈕做示範
- unmount 時 lenis.destroy()

限制:
- 不要在 layout.tsx 全站啟用(會跟 F11 / F13 衝突)
- 手機效能差,考慮關閉(lenis options: smoothTouch: false)
- 含 sticky 區塊要小心,可能會卡

完整程式碼

SmoothScrollLenis.tsx
'use client'

import Lenis from 'lenis'
import { useEffect, useRef } from 'react'

export function SmoothScrollLenis() {
const lenisRef = useRef<Lenis | null>(null)

useEffect(() => {
  const lenis = new Lenis()
  lenisRef.current = lenis
  let raf = 0
  function loop(t: number) {
    lenis.raf(t)
    raf = requestAnimationFrame(loop)
  }
  raf = requestAnimationFrame(loop)
  return () => {
    cancelAnimationFrame(raf)
    lenis.destroy()
  }
}, [])

function scrollTo(target: number) {
  lenisRef.current?.scrollTo(target, { duration: 1.4 })
}

return (
  <div className="flex gap-3">
    <button onClick={() => scrollTo(0)}>到頂</button>
    <button onClick={() => scrollTo(1200)}>到 1200px</button>
  </div>
)
}

何時用

  • ✅ 作品集 / 品牌站(賣質感)
  • ✅ 需要 scrollTo 平滑跳轉(錨點)
  • ✅ 客戶指名要「絲滑」

何時別用

  • ❌ 內容站 / 文件站(破壞讀者預期的滾動速度)
  • ❌ 跟 useScroll/ScrollTrigger 大量並用(互打)
  • ❌ 行動裝置為主(耗電、卡頓)

我踩過的坑

  1. 跟 useScroll 同時用會錯位:Lenis 改了真實 scroll 位置的更新 時機,framer-motion 的 useScroll 偶爾抓到舊值。解法:要嘛只用一個, 要嘛把 ScrollTrigger / framer-motion 的 scroll source 對齊到 Lenis。

  2. destroy 漏掉切頁就壞:SPA 切頁但 RAF 還在跑,會吃 CPU。 一定要 cleanup cancelAnimationFrame + lenis.destroy()

  3. 手機效能差:smoothTouch 預設 false 是對的,別開。

相關效果

搜尋

按 ⌘K 隨時開啟