원형 프로그레스 바(Circular Progress Bar)가 포함된 Scroll-to-Top 버튼

유용한 코드 작성일: 2025-12-19 수정일: 2026-02-26 08:30
수정
사용자가 페이지를 스크롤할 때 스크롤 진행률을 0~100% 수치와 원형 테두리(Circular Progress Bar)로 시각화해주는 'Top으로 이동' 버튼을 만들어줘. 클릭하면 페이지 최상단으로 부드럽게 이동해야 하고, 스크롤을 어느 정도 내렸을 때만 나타나게 해줘. 아래 코드를 참고 하면 됩니다.

=====================================
  Scroll to Top 버튼 (원형 진행률)
=====================================


[1] HTML
-------------------------------------
</body> 바로 위에 배치.
Lucide Icons CDN 미사용 시 <i data-lucide> 대신
인라인 SVG로 교체 (아래 대체 코드 참고).

<div id="scrollTopBtn" role="button" tabindex="0" aria-label="Back to top">
    <div class="scroll-top-bg"></div>
    <svg class="scroll-progress-ring" viewBox="0 0 44 44" aria-hidden="true">
        <circle class="scroll-progress-track" cx="22" cy="22" r="18"/>
        <circle class="scroll-progress-fill" cx="22" cy="22" r="18" id="scrollProgressFill"/>
    </svg>
    <div class="scroll-top-inner">
        <i data-lucide="chevron-up" class="scroll-top-arrow"></i>
        <span class="scroll-top-pct" id="scrollTopPct">0%</span>
    </div>
</div>

* Lucide 없이 쓸 때 아이콘 대체:
<svg class="scroll-top-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>


[2] CSS
-------------------------------------
색상 3곳을 프로젝트에 맞게 변경.

#scrollTopBtn {
    position: fixed;
    bottom: 28px;
    right: 24px;
    width: 50px;
    height: 50px;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(12px);
    transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
    z-index: 990;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
}

#scrollTopBtn.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

#scrollTopBtn:hover {
    transform: translateY(-3px);
}

.scroll-top-bg {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: #1a1a2e;                        /* [색상1] 기본 배경 */
    box-shadow: 0 4px 16px rgba(26, 26, 46, 0.28);
    transition: background-color 0.2s ease, box-shadow 0.2s ease;
}

#scrollTopBtn:hover .scroll-top-bg {
    background: #16213e;                        /* [색상2] hover 배경 */
    box-shadow: 0 6px 22px rgba(26, 26, 46, 0.4);
}

.scroll-progress-ring {
    position: absolute;
    width: 100%;
    height: 100%;
    transform: rotate(-90deg);
}

.scroll-progress-track {
    fill: none;
    stroke: rgba(255, 255, 255, 0.18);
    stroke-width: 3;
}

.scroll-progress-fill {
    fill: none;
    stroke: #e94560;                            /* [색상3] 진행률 링 */
    stroke-width: 3;
    stroke-linecap: round;
    stroke-dasharray: 113.1;
    stroke-dashoffset: 113.1;
    transition: stroke-dashoffset 0.12s linear;
}

.scroll-top-inner {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    pointer-events: none;
    user-select: none;
}

.scroll-top-arrow {
    width: 14px;
    height: 14px;
    color: rgba(255, 255, 255, 0.95);
    stroke-width: 2.8;
    display: block;
}

#scrollTopBtn:hover .scroll-top-arrow {
    animation: arrowBounce 0.45s ease forwards;
}

@keyframes arrowBounce {
    0%   { transform: translateY(0); }
    35%  { transform: translateY(-4px); }
    65%  { transform: translateY(-1px); }
    100% { transform: translateY(-2px); }
}

.scroll-top-pct {
    font-size: 9px;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.82);
    line-height: 1;
    letter-spacing: -0.3px;
}


[3] JavaScript
-------------------------------------
</body> 직전 또는 DOMContentLoaded 안에서 실행.
외부 라이브러리 의존 없음.

(function () {
    var scrollTopBtn  = document.getElementById('scrollTopBtn');
    var scrollTopPct  = document.getElementById('scrollTopPct');
    var progressFill  = document.getElementById('scrollProgressFill');
    var circumference = 2 * Math.PI * 18;

    if (!scrollTopBtn || !progressFill) return;

    function updateScrollTop() {
        var scrolled  = window.scrollY;
        var docHeight = document.documentElement.scrollHeight - window.innerHeight;
        var pct       = docHeight > 0 ? Math.min(100, Math.round(scrolled / docHeight * 100)) : 0;
        var offset    = circumference * (1 - pct / 100);

        progressFill.style.strokeDashoffset = offset;
        if (scrollTopPct) scrollTopPct.textContent = pct + '%';

        if (scrolled > 200) {
            scrollTopBtn.classList.add('visible');
        } else {
            scrollTopBtn.classList.remove('visible');
        }
    }

    window.addEventListener('scroll', updateScrollTop, { passive: true });
    updateScrollTop();

    scrollTopBtn.addEventListener('click', function () {
        window.scrollTo({ top: 0, behavior: 'smooth' });
    });

    scrollTopBtn.addEventListener('keydown', function (e) {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            window.scrollTo({ top: 0, behavior: 'smooth' });
        }
    });
})();


[적용 체크리스트]
-------------------------------------
- 색상 3곳 변경  : [색상1] 기본배경, [색상2] hover배경, [색상3] 진행률링
- 아이콘        : Lucide CDN 없으면 인라인 SVG로 교체
- HTML 위치     : </body> 바로 위
- JS 로드       : </body> 직전 또는 DOMContentLoaded
- 외부 의존성    : 없음 (Vanilla JS + 순수 CSS)
scroll to top 버튼에 효과를 줄 때 사용하는 프롬프트
언어: Plain Text