#
10.스킬바
In
#
목차
1. 스킬바 막대형 1.1. 스킬바 원형 1.2. 막대형2
2. with GSAP
#
1. 스킬바 막대형
<div class="content">contents</div>
<div class="animation">
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="30"></div>
</div>
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="90"></div>
</div>
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="60"></div>
</div>
</div>
<div class="content">contents</div>
.progress-bar {
position: relative;
width: 960px;
height: 30px;
margin: 3em auto;
border: 1px solid green;
}
.progress-bar .bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 0;
background: green;
}
.progress-bar .rate {
position: absolute;
top: 0;
right: 15px;
bottom: 0;
line-height: 30px;
font-size: 1.2em;
color: green;
}
.content {
height: 800px;
font-size: 3em;
}
$(function () {
var progressWrap = $('.progress-bar');
var animationOst = $('.animation').offset().top - 600;
var isAni = false;
$(window).scroll(function () {
if ($(window).scrollTop() >= animationOst && !isAni) {
progressAnimation();
}
});
function progressAnimation() {
progressWrap.each(function () {
var $this = $(this),
progressBar = $this.find('.bar'),
progressText = $this.find('.rate'),
progressRate = progressText.attr('data-rate');
progressBar.animate({ width: progressRate + '%' }, 2500);
// console.log(progressText);
var text = function () {
$({ rate: 0 }).animate(
{ rate: progressRate },
{
duration: 2000,
progress: function () {
var now = this.rate;
console.log(now);
progressText.text(Math.floor(now) + '%');
},
complete: function () {
isAni = true;
},
}
);
};
text();
});
}
});
#
1.1. 스킬바 원형
<section>
<h2>content</h2>
</section>
<div class="charts">
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="60">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="80">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="50">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
</div>
<section>
<h2>content</h2>
</section>
.charts {
width: 80%;
margin: 3rem auto;
display: flex;
justify-content: center;
}
.charts .chart {
margin: 0 20px;
position: relative;
}
.charts .chart h2 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin: 0;
}
.charts .chart svg {
width: 220px;
height: 220px;
}
circle {
fill: #ffffff;
stroke-width: 20;
stroke-dasharray: 628;
stroke-dashoffset: 628;
/* animation:line 2s forwards; */
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-linecap: round;
}
@keyframes line {
from {
stroke-dashoffset: 628;
}
to {
stroke-dashoffset: 0;
}
}
.charts .chart:nth-child(1) circle {
stroke: #ffc114;
}
.charts .chart:nth-child(2) circle {
stroke: #ff5248;
}
.charts .chart:nth-child(3) circle {
stroke: #19cdca;
}
.charts .chart:nth-child(4) circle {
stroke: #4e88e1;
}
section {
height: 100vh;
}
line {
stroke: #4e88e1;
}
$(function () {
var charts = $('.charts');
var chart = $('.chart');
var chartOST = chart.offset().top - 700;
// var excuted = false;
// console.log(excuted);
$(window).scroll(function () {
var currentSCT = $(this).scrollTop();
if (currentSCT >= chartOST) {
if (!charts.hasClass('active')) {
animateChart();
charts.addClass('active');
}
}
});
function animateChart() {
chart.each(function () {
var item = $(this);
var title = item.find('h2');
var targetNum = title.attr('data-num');
var circle = item.find('circle');
$({ rate: 0 }).animate(
{ rate: targetNum },
{
duration: 1500,
progress: function () {
var now = this.rate;
var amount = 630 - (630 * now) / 100;
title.text(Math.floor(now));
circle.css({ strokeDashoffset: amount });
},
}
);
}); //chart each
}
});
#
1.2. 막대형2
<section>
<h2>content</h2>
</section>
<div class="charts">
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
<div class="chart">
<h2 data-num="80">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
</div>
<section>
<h2>content</h2>
</section>
.charts {
width: 80%;
margin: 3rem auto;
display: flex;
justify-content: center;
}
.charts .chart {
margin: 0 20px;
position: relative;
}
.charts .chart h2 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin: 0;
}
line {
fill: #ebebeb;
stroke-width: 20;
stroke-dasharray: 300;
stroke-dashoffset: 300;
}
/* @keyframes line {
from {stroke-dashoffset: 628;}
to {stroke-dashoffset: 0;}
} */
.charts .chart:nth-child(1) line {
stroke: #ffc114;
}
.charts .chart:nth-child(2) line {
stroke: #ff5248;
}
.charts .chart:nth-child(3) line {
stroke: #19cdca;
}
.charts .chart:nth-child(4) line {
stroke: #4e88e1;
}
section {
height: 100vh;
}
line {
stroke: #4e88e1;
}
var charts = $('.charts');
var chart = $('.chart');
var chartOST = chart.offset().top - 700;
// var excuted = false;
// console.log(excuted);
$(window).scroll(function () {
var currentSCT = $(this).scrollTop();
if (currentSCT >= chartOST) {
if (!charts.hasClass('active')) {
animateChart();
charts.addClass('active');
}
}
});
function animateChart() {
chart.each(function () {
var item = $(this);
var title = item.find('h2');
var targetNum = title.attr('data-num');
var circle = item.find('line');
$({ rate: 0 }).animate(
{ rate: targetNum },
{
duration: 1500,
progress: function () {
var now = this.rate;
var amount = 300 - (300 * now) / 100;
title.text(Math.floor(now));
circle.css({ strokeDashoffset: amount });
},
}
);
}); //chart each
}
#
2. with GSAP
gsap 을 사용해보자
cdn
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
- html
<div class="circular-pbar">
<span class="circular-pbar-counter">0</span>
</div>
- css
.circular-pbar {
width: 200px;
height: 200px;
border-radius: 50%;
background: conic-gradient(darkred 33%, 0, black);
position: relative;
}
.circular-pbar-counter {
position: absolute;
font-size: 3em;
color: white;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- css
.circular-pbar
그라디언트 색상변경
지역변수 --p 선언후 0 할당 배경으로 grey 색상을 지정하고 색상의 location에 변수를 할당한다.
3
.circular-pbar {
--p: 0;
background: conic-gradient(#df3030 var(--p, 0), 0, #cacaca);
}
- js
gsap.to 를 사용하여 변수 p의 값인 location 을 변경한다
gsap.to('.circular-pbar', {
'--p': '33%',
duration: 4,
ease: 'expo.out',
});
숫자를 애니메이트 해보자
const circles = document.querySelectorAll('.circular-pbar');
circles.forEach((el) => {
const counter = el.querySelector('.circular-pbar-counter');
const tg = counter.textContent + '%';
const tm = gsap.timeline({
defaults: { duration: 4, ease: 'expo.out' },
scrollTrigger: {
trigger: el,
toggleActions: 'play pause resume reset',
},
});
tm.from(counter, {
textContent: 0,
modifiers: {
textContent: (textContent) => {
return textContent.toFixed();
},
},
});
tm.to(el, { '--p': tg }, 0);
});
- #4- counter 요소의 텍스트(목표 진행률 값)을 읽어와서 '%' 문자와 결합. 이 값은 나중에 CSS 변수로 사용됨.
- #6~7- GSAP 타임라인 생성 후 기본값으로 지속 시간(4초), 가속도 효과('expo.out') 할당
- #16 - modifiers 플러그인은 gsap 의 기본으로 내장된 것으로 특정속성의 변경값을 실시간으로 수정해준다.
- 모든 애니메이션에 사용 가능하며 개발자가 원하는 모든 속성에 적용할수 있다.
- 이때 함수를 사용해야 한다.