Few

김종민·2023년 8월 25일
0
post-thumbnail

Few site 클론코딩


🔎 작업내용

  • 사이트명: Few
  • 작업 기간: 5일
  • 유형: 웹, GSAP, swiper, 클론 코딩
  • 특징: 마우스 포인터 커스텀, 무한 슬라이드 효과, 마우스 좌표 인식한 애니메이션 효과, GSAP 라이브러리를 사용한 애니메이션 효과

마우스 포인터 커스텀

마우스 포인터를 따라다니는 원형 포인터를 생성하고,
마우스 포인터에 필터를 적용, 그리고 원하는 태그(video 태그)에 마우스가 위치했을 때, 마우스의 크기가 변하는 효과 구현

<!--html-->
<div class="mouse">
	<i class="play"></i>
</div>

임의의 마우스 div를 만들어준 후 그 안에
마우스 포인터가 변환될 때 나올 이미지의 div태그를 만들어둔다.

<!--css-->
.mouse{
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    z-index: 10000;
    transform: translate(-50%, -50%);
    pointer-events: none;
    transform-origin: 100% 100%;
    backdrop-filter: invert(100%);
    ~~/* transition: .3s; */ ~~
}

위와같이 마우스의 크기와 위치를 잡아주는데

여기서 주의해야 할 점!!

  1. position 값을 absolute로 주게 될 경우,
    relative가 된 공간을 벗어나질 못한다. 그런걸 방지하기 위해
    position 값을 fixed로 줘서 효과를 적용.

  2. transition 효과를 주게되면 기입한 시간 만큼 포인터를 따라오는데 텀이 생긴다. 그렇기 때문에 구글링으로 복붙할 때
    transition은 확인 후 지우도록 하자!

<!--js-->
const mouse = $(".mouse");
$(document).mousemove(function(e) {
    const mouseX = e.clientX;
    const mouseY = e.clientY;
    mouse.css("left", mouseX + 'px');
    mouse.css("top", mouseY + 'px');
});
  1. 마우스를 불러 선언을 해준 후
    마우스무브 이벤트를 사용하여 마우스가 움직일때 clientX(x의 좌표), clientY(y의 좌표) 값을 각각 마우스X, 마우스Y에 선언해준다.

  2. 선언된 마우스의 css, left와 top 위치값을
    마우스X, 마우스Y의 값을 넣어주어 적용시킨다.

비디오 태그 위 호버시 마우스 변화

<!--css-->
.mouse .play{
	display: block;
    width: 20px;
	height: 20px;
    background: #f00;
    visibility: hidden;
    opacity: 0;
    } 
    
.mouse.arrow .play{
    visibility: visible;
    opacity: 1;
	} 
  1. play에는 마우스 div 안에 위치해있을 이미지의 값을 넣어준다.
    각 20px인 정사각형의 빨간 상자를 위치시키고,
    visibility: hidden / opacity: 0 처리를 하여 숨겨준다.

  2. 그 후 마우스 div 가 호버가 될 때 나올 arrow div를 추가된다면~?
    visibility: visible / opacity: 1 처리를 하여 눈에 보이도록 한다.

<!--js-->
//마우스가 변화하는 크기
mouseHover=gsap.to('.mouse',0.3,{
  width: '8rem',
  height: '8rem',
  paused:true, 👈
})

$('.sc-video .cont-group').hover(function(){
  mouseHover.restart() 👈
  $(".mouse").addClass('arrow')

},function(){
  mouseHover.reverse() 👈
  $(".mouse").removeClass('arrow')
})
  1. 비디오 구역에 호버가 될 때의 이벤트로,
    마우스가 호버 될 때 변화될 크기와 duration인 0.3을 적용하여
    mouseHover라는 변수에 선언 해준다.
    그리고 마우스가 구역내에 들어가거나 나올 때 효과가 진행될 수 있도록 기본동작을 paused:true 처리 해 준다.

  2. 그리고 실질적으로 마우스에 호버되었을 때
    mouseHover의 변수를 restart()로 재시작되어 크기가 변화된다. 그 후에 arrow 클래스를 추가해줘 visibility: visible / opacity: 1 적용,

  3. 마우스가 공간에서 떠났을 때는 mouseHover 변수를 거꾸로 돌아가게 하는 reverse()를 넣어준다. 그렇다면 시작되었던 마우스 크기 변화가 다시 paused:true가 되어 기본 사이즈로 돌아가고,
    arrow 클래스를 삭제시켜줘 visibility: hidden / opacity: 0가 된다.



무한 슬라이드 효과

텍스트가 끊기지 않고 무한대로 흘러가는 효과로,
유일하게 키프레임을 활용하는 효과이다

<!--html-->
 <div class="text-wrap">
 	<div class="text">
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 	</div>
 	<div class="text">
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 		OUR WORK SHOWREEL —
 	</div>
 </div>

위와같이 텍스트들을 일렬로 묶어준다.
태그는 p태그, span태그 아무거나 무방하다.

그렇다면 아래와 같이 텍스트가 배열이 될것이다.

<!--css-->
.sc-video{
    overflow: hidden;
}
.sc-video .text-wrap{
    display: flex;
    position: absolute;
    top: 20%;
    left: 0;
    transform: translate(-5%,-50%) rotate(-5deg) 👈
}
.sc-video .text-wrap .text{
    font-size: clamp(21px,5vw,44px);
    white-space: nowrap;
    font-style: normal;
    line-height: 140%;
    will-change: transform; 👈
    animation: videotext 5s linear infinite forwards; 👈
}

@keyframes videotext { 👈
    0% {
       transform: translateX(0%);
    }
    
    100% {
       transform: translateX(-100%);
    }
}

.text 감싸고 있는 .text-wrap을 앱솔루트로 기준을 잡아준 후
trasnform으로 텍스트의 큰 기준을 잡아주도록 하고,
키프레임을 왼쪽으로 translateX(-100%)하도록 설정한다.
그리고 애니메이션이 적용될 .text에 키프레임을 적용시킨다.
이때 infinite로 무한히 움직이게 설정하는게 중요!!


❗❗❗가장~ 중요한 부분이다 집중❗❗❗


.sc-video .text-wrap{
    transform: translate(-5%,-50%) 👈 rotate(-5deg)
}

위의 코트에서 translate의 x축을 -5% 시켜준 이유
무한히 반복되는 중에 끝을 맞춰주지 못해 꽁다리가 잘렸다가 툭하고 튀며 다시 반복되는 현상이 있을 수 있어서, 축을 살짝 빼주어서 반복시에 끊김이 없도록 하는것이다!

✅스와이퍼로 무한슬라이드

키프레임 말고 스와이퍼로도 무한 슬라이드 설정이 가능하다.

<div class="swiper" dir="ltr"👈>
	<ul class="swiper-wrapper">
		<li class="swiper-slide">
			<img src="./asset/images/1.svg" alt="">
		</li>
        <li class="swiper-slide">
			<img src="./asset/images/2.svg" alt="">
		</li>
        <li class="swiper-slide">
        	<img src="./asset/images/3.svg" alt="">
		</li>
			/~~중략~~/
	</ul>
</div>
<div class="swiper" dir="rtl"👈>
	<ul class="swiper-wrapper">
		<li class="swiper-slide">
			<img src="./asset/images/1.svg" alt="">
		</li>
        <li class="swiper-slide">
			<img src="./asset/images/2.svg" alt="">
		</li>
        <li class="swiper-slide">
        	<img src="./asset/images/3.svg" alt="">
		</li>
			/~~중략~~/
	</ul>
</div>

swiper 라이브러리로 셋팅을 해주고,
swiper의 큰 그릇인 .swiper에 dir이라는 directory약자(아마도?), 방향을 선택해주는 속성을 적어준다.

dir="ltr"은 오른쪽에서 왼쪽 방향
dir="rtl"은 왼쪽에서 오른쪽 방향으로 흘러가도록!!

<!--js-->
const partnerSwiper = new Swiper('.sc-partner .swiper',{
  loop: true, 루프O
  slidesPerView: 7, //표시될 슬라이드 숫자
  autoplay: {
        delay: 0, // 자동실행 delay없이
      },
      speed: 5000,
      loopAdditionalSlides: 1,✨
})

loopAdditionalSlides: 1로 설정을 해줘야 끊김없이 무한 반복이 재생이 된다!



GSAP 라이브러리

기본 gsap 라이브러리 기능 소개

🔗 [GSAP] 애니메이션 사용법 1
🔗 [GSAP] 애니메이션 사용법 2

transform, animation과 같은 기능을 더욱 편리하고 안전하게 사용가능한 gsap

gsap라이브러리는 사용전 앞에 gsap.~을 붙이고
상황에 맞는 메소드를 붙여쓴다.

6️⃣$('.menu-btn').click(function(){
  if ($(this).hasClass('on')) {
    👉menuhideTl👈.restart()
  } else {
    👉menuTl👈.restart()
  }
  $(this).toggleClass('on');


// 1️⃣초기세팅
gsap.set('.snb .list .item a',{ yPercent:100,rotate:10 })
gsap.set('.side-nav .bg',{ xPercent:70 })
gsap.set('.snb .contact .wrap > *',{ yPercent:100 })


3️⃣const menuTl = gsap.timeline({
  paused:true, //정지
})

4️⃣👉menuTl👈
2️⃣
.to('.side-nav',{ visibility:'visible', opacity: 1 })
.to('.side-nav .bg',{ xPercent:-20 })
.to('.snb .list .item a',{ yPercent:0,rotate:0, stagger:0.1 })
.to('.snb .contact .wrap > *',{ yPercent:0, stagger:0.1 })


5️⃣const 👉menuhideTl👈 =  gsap.to('.side-nav',1,{
    opacity: 0 ,
    paused:true,
    onComplete:function(){
      gsap.set('.side-nav',{ visibility:'hidden',})
    }
})
  1. gsap.set()으로 transform을 줄 셀렉터를 선언하고 값을 세팅해준다.

  2. gsap.to()로 셋팅된 값이 후에 일어날 transform을 셀렉터를 선언하고 값을 넣어준다.

  3. gsap.timeline()을 const menuTl에 선언하여 사용성을 높여준다.timeline을 사용한 이유는 timeline을 사용하지 않았다면 gsap.to()에 셋팅된 값들이 동시다발적으로 진행되어 아름답지 않게된다.

  4. 타임라인이 선언된 menuTl을 사용하여 여러개의 .to()를 하나로 묶어서 처리한다.

  5. .side-nav가 사라질때의 셋팅값도 선언을 해준다.
    여기서 onComplete:function()은 위의 값들이 모두 적용이 된 후에~ 완료가 된 후에~ 실행되는 메소드로,
    .side-nav',{ visibility:'hidden'} 처리를 넣어준다.

  6. .menu-btn클릭시 pause를 시켰던 menuhideTl을 restart 해주고, 조건문에 부합하지 않다면 menuTl을 restart 해준다.


scrollTrigger✨

웹에 공통으로 빈번히 사용되는 이벤트를
스크롤 트리거를 이용해 아래와 같이 몇십줄이 넘는
제이쿼리 문법을 간단하게 몇줄로 요약이 가능하다!!

$(window).scroll(function(){
  let windowHeight = $(window).height();
  let scrollTop = $(this).scrollTop();
  let sectionTop = $('.sc-team').offset().top;
  let target = $('.sc-team .subtitle, .title');

  if(sectionTop < scrollTop + windowHeight * 0.7){

    target.each(function(i){
      let $this = $(this);

      setTimeout(function() {
        $this.addClass('clip');
      }, i * 500);
    })
  }
})

  ❤ /~~ 중략 ~~/ ❤  
   
  if (subtitleSectionTop < scrollTop + windowHeight) {
    subtitle.addClass('slideUp');
  }

  targets.forEach(function(targetInfo) {
    let target = $(targetInfo.selector);
    let targetSectionTop = target.offset().top;

    if (targetSectionTop < scrollTop + windowHeight) {
      target.addClass('slideUp');
    }
  });
});

30~40줄이 넘는, 일일이 다 확인해야했떤 노가다💧😣💧
이렇게 간단하게...가능합니다!

예시

<!--html-->
<div class="text-group">
	<h2 class="subtitle ✨wrap">
		<span>WE ARE FEW</span>
	</h2>

	<strong>
		<span class="title ✨wrap"><span>Thoughtful Design &</span></span>
		<span class="title ✨wrap"><span>Scalable Code. Fast &</span></span>
		<span class="title ✨wrap"><span>Flexible Teams.</span></span>
	</strong>

	<div class="desc-wrap">
		<p class="desc">Navigating the process of product design and development is complicated and time consuming. The number of people and stages that go into getting a product to market can be frustrating. Not to mention the complexities that allow you to scale when you need to.
		<br><br> At Few, you’ll get a holistic product team approach. From strategy, to design, to development, we’ll partner with you all the way from "seed round" to "IPO".</p>
		<a href="" class="more-btn">Learn More</a>
	</div>
</div>

기본. 마크업으로 이벤트를 주고 싶은 태그에 임의의 동일한 클래스명('.wrap')을 추가해준다.

<!--js-->
1️⃣
gsap.set('.sc-team .wrap > *',{ yPercent:100 })
gsap.set('.sc-team .text-group .desc',{ xPercent:-100 })

2️⃣
 weareTl =gsap.timeline({
  ✨scrollTrigger:{
    trigger:".sc-team", //기준
    start:"0% 60%", //트리거기준 //윈도으기준 둘이 만나면 실행
    end:"100% 10%",
    markers:true,표시자
  }
 })

3️⃣	
 weareTl
 .to('.sc-team .wrap > *',{
  yPercent:0,
  stagger:0.1,
 })
 
 .to('.sc-team .text-group .desc',{
  xPercent:0
 })
  1. gsap.set으로 기본 셋팅값을 설정
  2. weareTl에 타임라인 설정을 해준다, scrollTrigger 속성을 넣어주고 그 값으로 기준값인 trigger를 지정해주고, 트리거와 윈도우의 위치값을 start/end로 설정해준다.
  3. weareTl에게 .to()를 각각 입력해주면 깔끔하고 쉽게 적용이 가능하다.


marquee scroll

아래와 같이 이미지가 나란히 정렬되어 있는 구조를 marqee라 칭한다고 한다.
스크롤 시 스크롤 감지를 하여 marqee에 이벤트를 효과를 정리하려고 한다.

<!--html-->
<div class="marqee-wrapper">
	<ul class="marqee-scrub list1">
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="htt	ps://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
	</ul>
    <ul class="marqee-scrub list2">
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="htt	ps://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
		<li> <div class="thumb-box"> <img src="https://few.io/images/home/slider/arlton-made-by-few.jpg" alt=""> </div> </li>
	</ul>
</div>

리스트를 묶을수 있는 marqee-crub이라는 클래스를 설정하고,
두 리스트는 움직여야 하는 방향이 서로 다르기 때문에 list1,2로 구분지었다.
그리고 두 marqee를 한꺼번에 조절할 수 있도록 wrapper로 한번 더 묶어준다.

<!--css-->
.marqee-scrub{display: flex; gap: 20px;}
.marqee-scrub.list1{}
.marqee-scrub.list2{flex-direction: row-reverse;} 👈

flex로 각 리스트를 정렬해주고 list2는 list1과 방향을 반대로 주기 위해
flex-direction: row-reverse 처리를 하여 시작점을 다르게 설정하였다.

<!--js-->
marqeeTl =gsap.timeline({
    scrollTrigger:{
      trigger:".marqee-wrapper", 👈 //기준
      start:"0% 100%", //트리거기준 //윈도으기준 둘이 만나면 실행
      end:"100% 0%",
      scrub:0, 👈
      markers:true, // 표시자
    }
  });

  marqeeTl
  .addLabel('a')👈
  .to('.marqee-scrub.list1',{xPercent:-20},'a' 👈)
  .to('.marqee-scrub.list2',{xPercent:20},'a' 👈)

그리고 timelinescrollTrigger를 이용하여
transform을 변경해주면 된다!
일단은 marqeeTl를 timeLine을 이용하여 만들어주고 그 안에 scrollTrigger를 만들어준다. 기준이 되는 trigger는
marqee의 두 묶음이 되는 wrapper로 지정, 트리거 기준점을 잡아준다.

중요한 부분scrub이란 속성인데 문지르다는 뜻으로
왔다갔다 효과를 낼수 있는 속성이다! scrub: 0은 나의 마우스 스크롤 반응에 가장 가깝고, 숫자가 들어갈수록 편차가 생긴다.

그리고 marqeeTl.to()를 이용해 list1과 2에 각각 xPercent를 넣어주었다.
하지만 여기서 스크롤 시 list1과 2가 각각 따로 반응하는걸 볼 수 있는데,

이럴때 써야 하는 두번째로 중요한 부분!!
addLabel()이란 속성으로, 값에 임의로 a라는 값을 넣어주었다.addLabel(a)
뜻은 이 라벨이 달린것은 서로 같은것이라는 의미로 list1과 list2에 addLabel(a)에 넣어주었던 a라는 임의의 값을 달아주면
1ist1과 2는 같은걸로 묶여 동일하게 이벤트가 실행된다.


마우스 좌표 값에 따른 효과

해당 섹션안에 있는 리스트에 마우스를 올렸을 때
마우스의 좌표에 따라서 배경에 효과가 들어가는 이벤트

<!--html-->
<ul class="list">
	<li class="item">
		<div class="circle"></div>
	</li>
</ul>

<!--css-->
.sc-works .list li{
    position: relative;
    overflow: hidden;
}
.sc-works .list .circle{
    opacity: 0;
    position: absolute;
    top: 0;left: 0;
    width: 300px;
    height: 300px;
    background: #f00;
    filter: blur(100px);
    transition: opacity 0.3s;
}
.sc-works .list li.on .circle{
    opacity: 1;
}

.cicle이 해당되는 list 태그에 overflow: hidden을 해주어
circle이 넘쳐흘렀을 때 숨겨지도록 설정하였다.

<!--js-->
$('.sc-works .list li').mousemove(function(e){

  circle=$(this).find('.circle');
  x=e.offsetX - circle.width()/2; 👈
  y=e.offsetY - circle.width()/2; 👈

  $(this).addClass('on').siblings().removeClass('on')

  gsap.to(circle,{
    x:x,
    y:y/5
  })
})

list태그에서 마우스가 움직였을 때의 이벤트로,
circle을 찾아 변수선언을 해주었고, mousemove시에 움직이는 값(e)를 받아 처리한다. 그리고 list안의 영역안에서 좌표를 구하고 싶은데!
그때 사용하는게 바로 offset이다.

client와 offset의 차이 ⭐️
client X or Y는 화면전체의 영역에서 잡는 것이고,
offset X or Y는 해당구역의 영역에서 잡는 것을 뜻한다! 구분 확실히!!

해당 영역의 각각의 x값과 y를 x,y변수에 넣어주게 되면 아래와 같이 x,y좌표 기준으로 박스가 생성이 된다.

그래서 x=e.offsetX - circle.width()/2; 뒤에 circle width값의 절반으로 수정을 해주면 아래와 같이 포인터의 위치가 circle의 중앙으로 위치하게 된다.

그 후에는 .to()를 이용하여 x값은 위의 선언된 x값을, y값은 y값을 넣어주고,
y의 값은 감도를 줄이기 위해 /5로 값을 줄여뜨려주었다.


비디오 태그를 호출

ScrollTrigger.create({ 👈
  trigger:".sc-video", //기준
  start:"0% 100%", //트리거기준 //윈도으기준 둘이 만나면 실행
  end:"100% 10%",
  // markers:true, // 표시자

  onEnter:function(){ //도달시
    video.get(0) 👈.play()
    videoBlur.get(0) 👈.play()
  }
})
  • ScrollTrigger.create$(window).scroll과 같은 효과를 나타낸다.
  • onEnter:function()은 이름 그대로 스크롤이 enter(입장) 했을 때 실행이 된다.
  • 비디오태그를 호출할 때는 get()이라는 메소드를 사용해야만 호출이 가능하고,
    (0)은 인덱스값 즉,0 => 첫번째를 의미한다.


새로고침 시 리셋

window.onload = function(){

  setTimeout(function(){
    scrollTo(0,0)
  },100);
}

웹창을 새로고침 시에 scrollTo(0,0),
좌표값0,0인 맨 상단으로 스크롤이동한다.
setTimeout을 이용하여, 0.1초만에 이동토록 설정한다.

profile
웹 퍼블리셔의 코딩 일기

0개의 댓글