📎 https://github.com/eveneul/toss
📎 https://eveneul.github.io/toss
awwwards에 올라온 수상작을 레이아웃 레퍼런스로 참고하고, 기본적인 테마 색상이나 분위기는 토스 공식 홈페이지를 참고했습니다
먼저 SCSS로 스타일 작업을 했기 때문에 @media screen을 쓰기보다는 @mixin를 사용해서 처리했습니다
$tablet: 1024px;
$mobile: 768px;
/*반응형, 브라우저 크기가 767px 이하일때*/
@mixin mobile {
@media (max-width: $mobile) {
@content;
}
}
/*반응형, 브라우저 크기가 768이상, 1023px 이하일때*/
@mixin tablet {
@media (max-width: $tablet) {
@content;
}
}
분기점을 변수로 만들고, @mixin을 통해 CSS 변화가 필요한 부분마다 @include를 사용해 주었습니다
.menu-list {
display: flex;
@include mobile {
flex-direction: column;
}
}
SCSS로 mixin을 잘 사용하지 못할 때에는 @media screen을 이용해 모든 스타일을 복사해서 붙여넣기를 하고 필요한 부분만 수정했는데 mixin으로 미디어쿼리를 사용하니까 간편하고 무엇보다 헷갈리지 않아서 더 좋았습니다
(그전에는 뭘 바꿨는지 헷갈렸었음..)
화질구지..
.m-btn
이라는 햄버거 버튼을 만들어서 클릭했을 시 .active
라는 클래스가 생성됩니다
그리고 이 .active
태그의 유무를 확인하기 위해 hasClass를 사용해
.active
클래스가 있으면(햄버거 버튼 활성화) overflow: hidden
으로 드래그(스크롤)를 금지시켰고
그게 아니라면 드래그(스크롤)이 가능하도록 설정했습니다
$('.m-btn').click(function () {
$(this).toggleClass('active');
$('.header').toggleClass('active');
if ($('.header').hasClass('active')) {
$('html, body').css({ overflow: 'hidden', height: '100%' });
} else {
$('html, body').css({ overflow: 'auto', height: 'auto' });
}
});
const bgEffect = gsap.timeline({
scrollTrigger: {
trigger: '.sc-about',
start: 'top bottom',
end: 'bottom 20%',
scrub: true,
},
});
bgEffect
.to('.sc-about .bg', {
'width': '100%',
'border-radius': 0,
})
.to('.sc-about .bg', {
'width': '100%',
'border-radius': 0,
})
.to('.sc-about .bg', {
'width': '80%',
'border-radius': 30,
});
gsap의 timeline
을 통해서 만들었습니다
function format_number(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
천 단위로 콤마를 찍어 주고, 0부터 미리 지정해 둔 숫자까지 카운트 되면서 올라가는 함수를 만들었습니다
$('.about-item .num').each(function (idx, el) {
const value = {
val: $(this).text(),
};
const numEffect = gsap.timeline({
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: '+=100',
},
});
numEffect
.addLabel('a')
.from(
value,
{
duration: 2,
ease: 'circ.out',
val: 0,
roundProps: 'val',
onUpdate: function () {
el.innerText = format_number(value.val);
},
},
'a'
)
.from(
$(this).parents('.about-item'),
{
opacity: 0,
y: 50,
},
'a'
);
});
gsap timeline
으로 드래그함에 따라서 올라오고, onUpdate
를 통해 카운트 함수가 실행되도록 작성했습니다
gsap을 통해 드래그하면 가로에 있는 요소들이 움직이며 보이게끔 하는 효과를 구현했습니다!
(꼭 한번쯤 해보고 싶었던 거..)
let totalWidth = 0;
const visionEls = $('.vision-item');
const visionElWidth = $('.vision-item').innerWidth();
const visionItemW = $('.vision-item').width() / 2;
totalWidth = (visionEls.length * visionElWidth) / 2 - visionItemW;
basePosition();
gsap.to('.vision-list', {
x: -totalWidth,
scrollTrigger: {
trigger: '.sc-vision',
start: 'bottom bottom',
end: '+=300%',
scrub: true,
pin: true,
},
});
function basePosition() {
const innerW = $('.sc-vision .inner-s').width() / 2;
gsap.set('.vision-list', {
x: innerW - visionItemW,
});
}
스크롤하기 전 첫 번째 요소가 화면의 정중앙에 위치시켜 주기 위해서 아이템의 width 값을 구하고 2로 나뉘었습니다
그 값을 아이템을 감싼 ul에게 ((아이템 개수 * 아이템 각각의 width) / 2) - 아이템 width를 반으로 나눈 값을 적용해 주었습니다
(글로 설명하려니까 잘 안 됨..)
또 그값을 ul의 x로 적용시켜서 scrollTrigger
로 스크롤할 때 ul이 움직이게끔 작성했습니다
특히 basePosition
함수는 호이스팅 개념 덕분에 선언보다 위에서 호출했습니다 (상관없음)
애플 사이트에 단골로 나오는 스크롤할 때마다 재생되는 이미지 기능을 구현했습니다
<canvas class="canvas"></canvas>
롤링되는 내용물들의 배경으로 사용할 것이기 때문에 안에 내용물은 비웠습니다!
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 1200;
canvas.height = 675;
canvas는 바닐라 자바스크립트로 작성했습니다
canvas.getContext
로 해당 캔버스가 2d로 작업할 거라고 미리 알려주고 canvas.width와 canvas.height로 사이지를 적용시켜 주었습니다
const frameCount = 100;
const currentFrame = (idx) => {
return `asset/images/capture/capture${idx.toString()}.jpg`;
}; // 리턴 필수
frameCount
는 화면에 출력할 이미지의 총 개수이고
currentFrame
으로 asset
폴더에 있는 이미지들 전체를 불러왔습니다
계속 undefined
로 오류가 나길래 이리저리 고치다가 결국 return
을 쓰고 안 쓰고의 차이라는 걸 알고
감격스러워서 울 뻔했네요.. return은 필수!
const images = [];
const card = {
frame: 0,
};
for (let i = 0; i < frameCount; i++) {
const img = new Image();
img.src = currentFrame(i + 1);
images.push(img);
}
반복문을 통해서 불러온 이미지들을 images
라는 배열에 담았습니다
gsap.to(card, {
frame: frameCount - 1,
snap: 'frame',
ease: 'none',
scrollTrigger: {
trigger: '.sc-investor',
scrub: 0.5,
start: 'top 90%',
end: 'bottom center',
},
onUpdate: render,
});
images[0].onload = render;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(images[card.frame], 0, 0);
}
gsap scrollTrigger
로 드래그에 따라서 사진이 바뀌게 frame
으로 설정하고,
onUpdate
로 드래그할 때마다 함수가 실행되는데,
render
함수는 2d로 만들어진 canvas
에 이미지가 출력되도록 작성했습니다
반응형도 그렇고 그동안 레퍼런스를 보면서 해 보고 싶은 기능 구현들을 해 볼 수 있어서 재미있었습니다
반응형 구현이 어렵지 않을 거라 생각했던 나의 오만..을 흠뻑 때려 준 토스 리뉴얼 코딩 ㅠ.ㅠ
반응형은 여러번 더 공부하고 복습해야 할 것 같습니다