Nahorniak Templates
База референсів шаблонів і компонентів
Назад до шаблону · Webfolio - Creative Agency & Portfolio Next.js Template
c242

scroll-progress-ring

Декор·Шаблон: Webfolio - Creative Agency & Portfolio Next.js Template·Складність анімації: subtle·Адаптивний: Так
scroll-progress-ring

Файли-джерела

  • out/home-main/index.htmldiv.progress-wrap

Summary

Fixed bottom-right circular SVG ring that fills as the user scrolls, doubling as a back-to-top button. Implemented with a single <path> whose stroke-dasharray / stroke-dashoffset are driven by window.scrollY / (scrollHeight - innerHeight) from common/scrollToTop.js.

HTML structure (minimal)

<div class="progress-wrap cursor-pointer">
  <svg class="progress-circle svg-content" width="100%" height="100%" viewBox="-1 -1 102 102">
    <path d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98" />
  </svg>
</div>

Key SCSS tokens

.progress-wrap {
  position: fixed;
  right: 30px;
  bottom: 30px;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .04);
  cursor: pointer;
  opacity: 0;
  transform: translateY(15px);
  transition: opacity .3s, transform .3s;
  z-index: 50;

  &.active-progress { opacity: 1; transform: translateY(0); }

  svg path {
    stroke: $main_color; /* #fd5b38 */
    stroke-width: 3;
    fill: none;
    stroke-dasharray: 307.919;
    stroke-dashoffset: 307.919;
    transition: stroke-dashoffset 10ms linear;
  }
}

Animation logic

// common/scrollToTop.js (paraphrased)
function scrollToTop() {
  const path = document.querySelector('.progress-wrap path');
  const wrap = document.querySelector('.progress-wrap');
  const length = path.getTotalLength();
  path.style.strokeDasharray = length;
  path.style.strokeDashoffset = length;

  function update() {
    const scroll = window.scrollY;
    const height = document.documentElement.scrollHeight - window.innerHeight;
    const progress = length - (scroll * length) / height;
    path.style.strokeDashoffset = progress;
    wrap.classList.toggle('active-progress', scroll > 50);
  }
  window.addEventListener('scroll', update);
  wrap.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
}

Notable details

  • Single SVG path acts as both progress ring and back-to-top trigger — no separate button needed.
  • Uses path.getTotalLength() so it scales correctly if the viewBox is changed.
  • Hidden until 50px scrolled, with a subtle slide-up reveal (active-progress class).

Use when

  • Long single-page layouts where users benefit from a quick way back to the top.
  • Sites that want a small kinetic detail in the corner without busting the layout.

Caveats

  • Click behavior assumes default scrolling, not GSAP ScrollSmoother. With ScrollSmoother on, window.scrollTo may need to be replaced with ScrollSmoother.get().scrollTo(0, true).
  • Ring is 46x46 in the bottom-right corner — screenshot capture may need a tighter region; pipeline likely returns a near-empty crop.