애니메이션 풍선 범위 슬라이더
본문
모든 프로젝트에 부드럽게 표시되는 아름답고 간단한 범위의 풍선 풍선 애니메이션.
https://codepen.io/aaroniker/pen/ZEEWoKj
HTML :
<div id="slider"></div>
<!-- dribbble -->
<a class="dribbble" href="https://dribbble.com/shots/7515563-Balloon-Slider" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
CSS :
#slider {
--active: #5628EE;
--balloon: var(--active);
--value: #fff;
--line: #CDD9ED;
touch-action: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
width: 320px;
height: 2px;
border-radius: 1px;
background: var(--line);
position: relative;
}
#slider .noUi-connects .noUi-connect {
background: var(--active);
}
#slider .noUi-handle:before, #slider .noUi-handle:after {
border-radius: 20px;
-webkit-transform: scale(var(--s));
transform: scale(var(--s));
transition: border-radius .3s ease, -webkit-transform .3s ease;
transition: transform .3s ease, border-radius .3s ease;
transition: transform .3s ease, border-radius .3s ease, -webkit-transform .3s ease;
}
#slider .noUi-handle:before {
--s: .5;
background: var(--active);
}
#slider .noUi-handle:after {
--s: .2;
background: #fff;
}
#slider .noUi-handle.noUi-active:before {
--s: 1;
}
#slider .noUi-handle.noUi-active:after {
--s: 1;
}
#slider .balloon {
--o: 0;
--s: 0;
--y: 0;
--r: 0deg;
width: 52px;
height: 68px;
pointer-events: none;
position: absolute;
z-index: 5;
left: -26px;
bottom: 0;
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
}
#slider .balloon > div {
width: 52px;
height: 68px;
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
opacity: var(--o);
-webkit-transform: scale(var(--s)) translate(0, var(--y)) rotate(var(--r));
transform: scale(var(--s)) translate(0, var(--y)) rotate(var(--r));
transition: opacity .4s ease, -webkit-transform .4s ease;
transition: transform .4s ease, opacity .4s ease;
transition: transform .4s ease, opacity .4s ease, -webkit-transform .4s ease;
}
#slider .balloon > div:before, #slider .balloon > div:after {
content: '';
display: block;
}
#slider .balloon > div:before {
width: 52px;
height: 52px;
border-radius: 60%;
border-bottom-left-radius: 480%;
border-bottom-right-radius: 480%;
border-top-left-radius: 480%;
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
background: var(--balloon);
}
#slider .balloon > div:after {
content: attr(data-value);
font-family: 'Roboto', Arial;
font-size: 14px;
font-weight: 500;
color: var(--value);
position: absolute;
left: 0;
right: 0;
top: 16px;
line-height: 24px;
text-align: center;
}
#slider .balloon > div svg {
display: block;
width: 8px;
height: 6px;
fill: var(--balloon);
position: absolute;
left: 22px;
bottom: 0;
}
#slider .balloon.active {
--o: 1;
--s: 1;
--y: -36px;
}
#slider * {
touch-action: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
#slider.noUi-state-tap .noUi-connect, #slider.noUi-state-tap .noUi-origin {
transition: -webkit-transform .3s;
transition: transform .3s;
transition: transform .3s, -webkit-transform .3s;
}
#slider .noUi-target {
position: relative;
}
#slider .noUi-base,
#slider .noUi-connects {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
}
#slider .noUi-origin,
#slider .noUi-connect {
position: absolute;
will-change: transform;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
top: 0;
left: 0;
z-index: 1;
}
#slider .noUi-connects {
overflow: hidden;
z-index: 0;
border-radius: 1px;
}
#slider .noUi-connects .noUi-connect {
width: 100%;
height: 100%;
}
#slider .noUi-handle {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
position: absolute;
outline: none;
cursor: pointer;
width: 40px;
height: 40px;
left: 12px;
top: -19px;
}
#slider .noUi-handle:before, #slider .noUi-handle:after {
content: '';
position: absolute;
}
#slider .noUi-handle:before {
left: 0;
top: 0;
width: 40px;
height: 40px;
}
#slider .noUi-handle:after {
left: 2px;
top: 2px;
width: 36px;
height: 36px;
}
#slider .noUi-origin {
left: auto;
right: 0;
height: 0;
width: 10%;
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
}
*:before, *:after {
box-sizing: inherit;
}
body {
min-height: 100vh;
display: flex;
font-family: 'Roboto', Arial;
justify-content: center;
align-items: center;
background: #F5F9FF;
}
body .dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
}
body .dribbble img {
display: block;
height: 28px;
}
JS :
let slider = document.getElementById('slider'),
balloon,
balloonTransform,
balloonReset;
noUiSlider.create(slider, {
start: 60,
connect: 'lower',
range: {
min: 0,
max: 100
}
});
if(!balloon) {
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 8 6');
let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d','M3.19770784,0.400189793 L0.183732919,4.48916968 C-0.132777702,4.91857125 -0.0301446804,5.51531196 0.41297019,5.82202737 C0.58019429,5.93777661 0.780561067,6 0.986063367,6 L7.01401322,6 C7.55855868,6 8,5.57222019 8,5.04452705 C8,4.84538453 7.93578945,4.65121861 7.81634366,4.48916968 L4.80236874,0.400189793 C4.48585812,-0.0292117777 3.87005999,-0.128668564 3.42694512,0.178046844 C3.33840792,0.239330552 3.2609487,0.314392601 3.19770784,0.400189793 Z');
svg.appendChild(path);
let inner = document.createElement('div');
inner.appendChild(svg);
balloon = document.createElement('div');
balloon.classList.add('balloon');
balloon.appendChild(inner);
slider.getElementsByClassName('noUi-base')[0].appendChild(balloon);
balloonTransform = new Proxy({
r: 0
}, {
set(target, key, value) {
target[key] = value;
balloon.style.setProperty('--r', target.r + 'deg');
return true;
},
get(target, key) {
return target[key];
}
});
}
var timestamp = null,
lastX = null,
startedMoving = false,
moved = false;
slider.noUiSlider.on('start', function() {
let percent = this.get() / this.options.range.max * 100;
balloon.classList.add('active');
balloon.childNodes[0].dataset.value = Math.round(this.get());
TweenMax.to(balloon, 0, {
x: this.target.offsetWidth * percent / 100,
scale: .75 + .25 * percent / 100
});
balloonReset = setInterval(() => {
if(!moved && startedMoving) {
TweenMax.to(slider.getElementsByClassName('noUi-handle')[0], .3, {
css: {
scale: 1
}
});
balloonTransform.r = 0;
startedMoving = false;
}
moved = false;
}, 20);
});
slider.noUiSlider.on('slide', function() {
let percent = this.get() / this.options.range.max * 100;
balloon.childNodes[0].dataset.value = Math.round(this.get());
if(timestamp === null) {
timestamp = Date.now();
lastX = this.get();
return;
}
var now = Date.now(),
speedX = Math.round((this.get() - lastX) / (now - timestamp) * 160),
speedX = speedX > 10 ? 10 : speedX < -10 ? -10 : speedX;
balloonTransform.r = speedX * -2;
startedMoving = true;
moved = true;
TweenMax.to(balloon, 1.4, {
x: this.target.offsetWidth * percent / 100,
scale: .75 + .25 * percent / 100,
ease: Elastic.easeOut.config(1, .6)
});
TweenMax.to(slider.getElementsByClassName('noUi-handle')[0], .3, {
css: {
scale: .9
}
});
timestamp = now;
lastX = this.get();
});
slider.noUiSlider.on('end', function() {
balloon.classList.remove('active');
clearInterval(balloonReset);
});
- 이전글랜덤 비밀번호 생성기-HTML | CSS | JS 19.11.30
- 다음글오픈 소스 일러스트레이션 키트 19.11.30