You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改JS监听滚动高度,基于Canvas父级Div实现苹果风格序列滚动?

Fixing the Canvas Scroll Animation to Trigger Only When Target Section is in Viewport

Great question! Let's break down why your change broke things and fix it step by step.

Why Your Original Change Failed

When you replaced document.documentElement with document.getElementById("wrap1"), the code stopped working because:

  • #wrap1 isn't a scrollable container (it lacks overflow: scroll/auto), so its scrollTop value stays at 0 permanently.
  • The original logic relied on the entire document's scroll range, but you want the animation to activate only when the target section is visible in the viewport, using that section's context instead of the whole page.

Solution: Adjust Logic to Target the Section

We need to modify the code to:

  1. Detect when #wrap1 enters or exits the viewport
  2. Calculate scroll progress relative to that section
  3. Only update the canvas frame when the section is visible

Modified Full Code

HTML (No Changes Needed)

<div class="wrap1" id="wrap1">
  <div class="wrap" id="wrap">
    <canvas id="hero-lightpass"></canvas>
  </div>
</div>

CSS (Optional Adjustment)

Tweak the #wrap height to control how quickly the animation plays as you scroll:

#wrap1 {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0;
  background: #000;
}

#wrap {
  height: 100vh; /* Adjust this to change animation scroll duration */
}

canvas {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: 100vw;
  max-height: 100vh;
}

JavaScript (Core Fixes)

const canvas = document.getElementById("hero-lightpass");
const context = canvas.getContext("2d");
const wrap1 = document.getElementById("wrap1"); // Target our section container
const frameCount = 148;

const currentFrame = index =>
  (`https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${index.toString().padStart(4, '0')}.jpg`);

// Preload all frames for smooth animation
const preloadImages = () => {
  for (let i = 1; i < frameCount; i++) {
    const img = new Image();
    img.src = currentFrame(i);
  }
};

// Initialize first frame
const img = new Image();
img.src = currentFrame(1);
canvas.width = 1158;
canvas.height = 770;

img.onload = function() {
  context.drawImage(img, 0, 0);
};

// Update canvas with the correct frame
const updateImage = index => {
  img.src = currentFrame(index);
  context.drawImage(img, 0, 0);
};

// Listen for window scroll
window.addEventListener('scroll', () => {
  const rect = wrap1.getBoundingClientRect();
  // Total scroll distance where the section is visible (from entry to exit)
  const totalScrollRange = wrap1.offsetHeight + window.innerHeight;
  // How far we've scrolled into the section's visible range
  const scrollOffset = window.scrollY - (wrap1.offsetTop - window.innerHeight);

  // Only run animation if the section is in the viewport
  if (rect.top < window.innerHeight && rect.bottom > 0) {
    // Clamp scroll fraction between 0 and 1 to avoid invalid frame indices
    const scrollFraction = Math.min(1, Math.max(0, scrollOffset / totalScrollRange));
    // Map scroll fraction to frame number
    const frameIndex = Math.min(frameCount - 1, Math.ceil(scrollFraction * frameCount));
    requestAnimationFrame(() => updateImage(frameIndex + 1));
  } else {
    // Optional: Reset to first frame when section is out of view
    // requestAnimationFrame(() => updateImage(1));
  }
});

preloadImages();

Key Changes Explained

  • Target the section: We use wrap1 instead of the entire document to reference our animation container.
  • Viewport detection: getBoundingClientRect() tracks the section's position relative to the viewport, ensuring we only run the animation when it's visible.
  • Relative scroll calculation: We calculate progress based on the section's visible scroll range, not the entire page. This means the animation starts when the section enters the viewport and finishes when it exits.
  • Value clamping: Math.min() and Math.max() prevent invalid frame indices by keeping the scroll fraction between 0 and 1.

内容的提问来源于stack exchange,提问作者Marcus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 16:56:15