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

如何实现水平滚动时按滚动进度逐段绘制SVG圆形

Solution: Animate SVG Circles on Horizontal Scroll

To achieve the effect of drawing one SVG circle for every 20% of horizontal scroll progress, we'll use JavaScript to track scroll position and adjust SVG stroke properties dynamically. Here's a complete, polished implementation:

Modified Code

HTML

<div class="draw-svg-widget">
  <div class="svg-circles-progress">
    <svg width="421" height="408" viewBox="0 0 421 408" fill="none" xmlns="http://www.w3.org/2000/svg">
      <!-- Custom gradient for circle strokes (vertical, matches concentric design) -->
      <defs>
        <linearGradient id="circleStrokeGradient" x1="210.5" y1="7" x2="210.5" y2="401" gradientUnits="userSpaceOnUse">
          <stop stop-color="#CE008E"/>
          <stop offset="1" stop-color="#3913BA"/>
        </linearGradient>
        <linearGradient id="paint0_linear" x1="424.5" y1="229" x2="-211.5" y2="231" gradientUnits="userSpaceOnUse">
          <stop stop-color="#CE008E"/>
          <stop offset="0.512051" stop-color="#3913BA"/>
          <stop offset="0.869792" stop-color="#3913BA" stop-opacity="0"/>
        </linearGradient>
      </defs>
      
      <!-- Concentric circles (ellipses) -->
      <ellipse cx="209" cy="207" rx="29" ry="29" transform="rotate(-180 209 207)" stroke="#BDBDBD" stroke-dasharray="4 4"/>
      <ellipse cx="209" cy="207" rx="74" ry="74" transform="rotate(-180 209 207)" stroke="#BDBDBD" stroke-dasharray="4 4"/>
      <ellipse cx="209" cy="207" rx="118" ry="118" transform="rotate(-180 209 207)" stroke="#BDBDBD" stroke-dasharray="4 4"/>
      <ellipse cx="209" cy="207" rx="162" ry="162" transform="rotate(-180 209 207)" stroke="#BDBDBD" stroke-dasharray="4 4"/>
      <ellipse cx="209" cy="207" rx="200" ry="200" transform="rotate(-180 209 207)" stroke="#BDBDBD" stroke-dasharray="4 4"/>
      
      <path d="M2.49982 207L2.49982 213.5L15.4998 213.5L15.4998 207L2.49982 207ZM358.637 84.3176C360.913 87.0937 365.009 87.4991 367.785 85.2231C370.561 82.947 370.966 78.8515 368.69 76.0754L358.637 84.3176ZM15.4998 207C15.4998 167.102 27.8331 128.179 50.8107 95.5619L40.183 88.0751C15.6617 122.884 2.49982 164.421 2.49982 207L15.4998 207ZM50.8107 95.5619C73.7883 62.9446 106.287 38.2275 143.856 24.7954L139.479 12.5543C99.3862 26.8888 64.7044 53.2664 40.183 88.0751L50.8107 95.5619ZM143.856 24.7954C181.425 11.3633 222.228 9.87282 260.677 20.5282L264.149 8.00034C223.116 -3.37086 179.572 -1.78029 139.479 12.5543L143.856 24.7954ZM260.677 20.5282C299.126 31.1835 333.341 53.4637 358.637 84.3176L368.69 76.0754C341.695 43.1485 305.181 19.3716 264.149 8.00034L260.677 20.5282Z" fill="url(#paint0_linear)"/>
    </svg>
  </div>
  <div class="horizontal-scroll">
    <div class="box one"> Draw Circle 1 </div>
    <div class="box two"> Draw Circle 2 </div>
    <div class="box three"> Draw Circle 3 </div>
    <div class="box four"> Draw Circle 4 </div>
    <div class="box five"> Draw Circle 5 </div>
  </div>
</div>

CSS

.svg-circles-progress { 
  position: relative; 
  top: 0; 
  left: 0; 
  height: 200px; 
}
.svg-circles-progress svg { 
  max-width: 200px; 
  position: fixed; 
  top: 0; 
  height: 250px; 
}
.horizontal-scroll { 
  margin-top: 50px; 
  width: 1000px; 
  overflow-x: scroll; 
  overflow-y: hidden; 
  height: 200px; 
  border: 1px solid black; 
  display: flex; 
  justify-content: space-between; 
}
.horizontal-scroll .box { 
  margin: 20px; 
  padding: 10px; 
  background: blue; 
  width: 100px; 
  flex-shrink: 0; /* Prevent boxes from shrinking, maintains consistent scroll width */
  color: white;
  text-align: center;
}

JavaScript

document.addEventListener('DOMContentLoaded', () => {
  const scrollContainer = document.querySelector('.horizontal-scroll');
  const circles = document.querySelectorAll('.svg-circles-progress ellipse');
  
  // Precompute total stroke length for each circle (one-time calculation)
  const circleStrokeLengths = Array.from(circles).map(circle => circle.getTotalLength());
  
  // Handle scroll event and update circle states
  const updateCircleProgress = () => {
    const totalScrollableWidth = scrollContainer.scrollWidth - scrollContainer.clientWidth;
    if (totalScrollableWidth === 0) return; // Exit if no scroll is possible
    
    // Calculate scroll progress percentage (0-100)
    const scrollProgress = (scrollContainer.scrollLeft / totalScrollableWidth) * 100;
    
    circles.forEach((circle, index) => {
      const startThreshold = index * 20;
      const endThreshold = (index + 1) * 20;
      const strokeLength = circleStrokeLengths[index];
      
      if (scrollProgress < startThreshold) {
        // Reset to initial dashed gray state
        circle.style.stroke = '#BDBDBD';
        circle.style.strokeDasharray = '4 4';
        circle.style.strokeDashoffset = '0';
      } else if (scrollProgress >= endThreshold) {
        // Fully draw the circle with gradient stroke
        circle.style.stroke = 'url(#circleStrokeGradient)';
        circle.style.strokeDasharray = strokeLength;
        circle.style.strokeDashoffset = '0';
      } else {
        // Partially draw the circle based on current scroll segment
        const drawPercentage = (scrollProgress - startThreshold) / 20;
        const drawnLength = strokeLength * drawPercentage;
        
        circle.style.stroke = 'url(#circleStrokeGradient)';
        circle.style.strokeDasharray = `${drawnLength} ${strokeLength}`;
        circle.style.strokeDashoffset = '0';
      }
    });
  };
  
  // Set initial state on page load
  updateCircleProgress();
  
  // Attach scroll event listener
  scrollContainer.addEventListener('scroll', updateCircleProgress);
});

Key Details & Improvements

  1. Scroll Progress Logic:

    • We calculate scroll progress by dividing the current scroll position by the total scrollable width (content width minus visible width). This gives us a 0-100% value to trigger circle animations.
  2. SVG Stroke Animation:

    • Using getTotalLength() on each circle lets us precisely control how much of the stroke is visible. For partial drawing, we adjust the strokeDasharray to show only the completed portion of the circle.
  3. Gradient Optimization:

    • I added a vertical linear gradient (circleStrokeGradient) that looks more natural on concentric circles than the original horizontal gradient. You can easily modify the colors or direction to match your design.
  4. CSS Fix:

    • Added flex-shrink: 0 to the scrollable boxes to prevent them from resizing, ensuring the total scrollable width remains consistent across devices.

This implementation provides a smooth, responsive animation where each circle is drawn incrementally as you scroll through the horizontal container.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 05:44:09