πŸ“„ Martin Briceno

μ΅œμ€€ν˜„Β·2022λ…„ 7μ›” 29일
0
post-thumbnail
μ œμž‘κΈ°κ°„ 22.06.19 ~ 22.06.21 (3일 μ†Œμš”)
μ‚¬μš© μ–Έμ–΄ html, scss, jquery
μ‚¬μš© ν”ŒλŸ¬κ·ΈμΈ gsap
λΆ„λ₯˜ pc, mobile, 클둠코딩


βœ” 마우슀λ₯Ό λ”°λΌλ‹€λ‹ˆλŠ” μš”μ†Œ

πŸ“Œ mouse μ’Œν‘œ κ°’ κ΅¬ν•˜κΈ°

$(document).mousemove(function(e){
	mouseX = (e.clientX * 100)/innerWidth + "vw";
	mouseY = (e.clientY * 100)/innerHeight + "vh";
})

πŸ’¬ 마우슀 μ»€μ„œλ₯Ό λ”°λΌλ‹€λ‹ˆλ €λ©΄ μ»€μ„œ μœ„μΉ˜λ₯Ό μ•Œμ•„μ•Όν•˜λ―€λ‘œ μ»€μ„œμœ„μΉ˜λ₯Ό clientX, clientYλ₯Ό μ΄μš©ν•˜μ—¬ Xμ’Œν‘œμ™€ Yμ’Œν‘œλ₯Ό κ΅¬ν–ˆμŠ΅λ‹ˆλ‹€.


πŸ’‘ 마우슀 μ’Œν‘œλ₯Ό μ•Œμ•„λ‚Ό 수 μžˆλŠ” μ—¬λŸ¬κ°€μ§€ 방법!

  • screenX, screenY
    - μ‚¬μš©μž λͺ¨λ‹ˆν„° 화면을 κΈ°μ€€μœΌλ‘œ ν•œ μ’Œν‘œλ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.

  • pageX, pageY
    - 전체 λ¬Έμ„œλ₯Ό κΈ°μ€€μœΌλ‘œ ν•œ μ’Œν‘œλ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.

  • clientX, clientY
    - λΈŒλΌμš°μ €μ—μ„œ μ‚¬μš©μžμ—κ²Œ μ›ΉνŽ˜μ΄μ§€κ°€ λ³΄μ—¬μ§€λŠ” μ˜μ—­μ„ κΈ°μ€€μœΌλ‘œ Β Β μ’Œν‘œλ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€. (μŠ€ν¬λ‘€λ°”κ°€ 움직이더라도 λ™μΌν•œ 값을 가짐)

  • offsetX, offsetY
    - μ’Œν‘œλ₯Ό 좜λ ₯ν•˜λ„λ‘ ν•˜λŠ” μ΄λ²€νŠΈκ°€ κ±Έλ €μžˆλŠ” μš”μ†Œ κΈ°μ€€μœΌλ‘œ μ’Œν‘œλ₯Ό Β Β ν‘œμ‹œν•©λ‹ˆλ‹€.

πŸ“Œ gsap set을 μ΄μš©ν•˜μ—¬ κ³ μ •

<div class="cursor"></div>
.cursor {
  position: fixed;
  top: -6px; left: -15px; 
  width: 13px; height: 13px;
  border: 1px #1d1d1d;
  border-radius: 100%;
  background-color: hsla(0,0%,100%,.01);
  backdrop-filter: invert(100%);
  transform: none;
  transition: width .3s, height .3s;
  pointer-events: none;
  z-index: 100;
}
$(document).mousemove(function(e){
  mouseX = (e.clientX * 100)/innerWidth + "vw";
  mouseY = (e.clientY * 100)/innerHeight + "vh";
  gsap.set('.cursor', {
    x: mouseX,
    y: mouseY
  })
})

πŸ’¬ ν•΄λ‹Ή μš”μ†Œλ₯Ό html, css μ„ΈνŒ… ν•˜κ³  cursor 클래슀λ₯Ό λ„£μ–΄ μ€€ ν›„, gsap.set을 μ΄μš©ν•΄μ„œ x: mouseX, y: mouseY둜 μ™„μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

πŸ–Š νŠΉμ • μš”μ†Œμ— μ»€μ„œλ₯Ό 두면 원 λ³€κ²½


πŸ’¬ work-area μš”μ†Œμ— κ°€λ©΄ 원이 μœ„ 사진과 같이 λ³€κ²½λ©λ‹ˆλ‹€.

  <div class="cursor">
        <div class="cursor-transform see-case hide">SEE CASE</div>
  </div>

πŸ’¬ cursorμ•ˆμ— see-case divνƒœκ·Έλ₯Ό μƒμ„±ν•˜μ—¬ 원 μ•ˆμ— 'SEE CASE' κΈ€μžλ₯Ό ν‰μ†Œμ—λŠ” μ•ˆλ³΄μ΄κ²Œ hide 클래슀λ₯Ό μ£Όμ–΄ 숨겨주고 work-area μš”μ†Œμ— λ“€μ–΄κ°ˆ λ•Œ hideλ₯Ό μ œκ±°ν•˜μ—¬ 보이도둝 섀계λ₯Ό ν–ˆμŠ΅λ‹ˆλ‹€.

if(e.target.closest('.work-area') != null) {
	$('.cursor').addClass('cursor-work').children('.see-case').removeClass('hide')
}

πŸ’¬ μžμ‹ λΆ€ν„° λΆ€λͺ¨ μš”μ†ŒκΉŒμ§€ μ§€μ •ν•œ μ„ νƒμžμ— λ§Œμ‘±ν•  λ•ŒκΉŒμ§€ νƒμƒ‰ν•˜λŠ” closest λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ e.target이 work-area 쑰건을 λ§Œμ‘±ν•˜λ©΄ cursor에 cursor-work 클래슀λ₯Ό μΆ”κ°€ν•˜μ—¬ 원 cssλ₯Ό λ³€κ²½ν•΄μ£Όκ³ , see-case에 hideλ₯Ό 제거 ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. κ·Έ ν›„, gsap.to을 μ΄μš©ν•˜μ—¬ μ•„κΉŒ ꡬ해쀀 mouseX κ°’κ³Ό mouseY값을 μ„€μ •ν•΄μ£Όμ–΄ transition이 적용되게 ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

βž• link-talkμ—μ„œ μ»€μ„œ λ³€κ²½

else if (e.target.closest('.link-talk') != null) {
	$('.cursor').addClass('cursor-talk').children('.email').removeClass('hide')
}

πŸ’¬ νŽ˜μ΄μ§€ ν•˜λ‹¨μ— link-talk κ΅¬κ°„μ—μ„œλ„ μ»€μ„œκ°€ λ³€κ²½λ˜λŠ” μ΄λ²€νŠΈκ°€ 있기 λ•Œλ¬Έμ— λ§ˆμ°¬κ°€μ§€λ‘œ λ§ˆν¬μ—…μ„ ν•΄μ£Όκ³  else ifλ₯Ό μ‚¬μš©ν•΄μ„œ λ˜‘κ°™μ΄ κ΅¬ν˜„μ„ ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€.

βž• a, btn κ΅¬κ°„μ—μ„œ μ»€μ„œ λ³€κ²½

else if(e.target.closest('a, button') != null) {
	gsap.to('.cursor', {
		scale: 3,
	})
} 

πŸ’¬ a, btn κ΅¬κ°„μ²˜λŸΌ ν΄λ¦­ν•˜λŠ” κ΅¬κ°„μ—μ„œλ„ μ»€μ„œκ°€ μ»€μ§€λŠ” μ΄λ²€νŠΈκ°€ μžˆμ–΄μ„œ gsap.to을 μ΄μš©ν•˜μ—¬ scale을 μ‘°μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

else {
	$('.cursor').removeClass('cursor-talk')
	$('.cursor').removeClass('cursor-work')
	$('.cursor span').addClass('hide');
	gsap.to('.cursor', {
		scale: 1,
	})
}

πŸ’¬ κ·Έ μ™Έ κ΅¬κ°„μ—μ„œλŠ” λͺ¨λ“  cursor μš”μ†Œμ— λͺ¨λ“  클래슀λ₯Ό μ œκ±°ν•˜κ³  scale도 μ›μƒλ³΅κ·€ν•˜μ—¬ μ™„μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€!

πŸ”˜ μ΅œμ’…μ½”λ“œ

$(document).mousemove(function(e){
  mouseX = (e.clientX * 100)/innerWidth + "vw";
  mouseY = (e.clientY * 100)/innerHeight + "vh";
	gsap.set('.cursor', {
		x: mouseX,
		y: mouseY
	})
	if(e.target.closest('.work-area') != null) {
		$('.cursor').addClass('cursor-work').children('.see-case').removeClass('hide')
	} else if (e.target.closest('.link-talk') != null) {
		$('.cursor').addClass('cursor-talk').children('.email').removeClass('hide')
	} else if(e.target.closest('a, button') != null) {
		gsap.to('.cursor', {
			scale: 3,
		})
	} else {
		$('.cursor').removeClass('cursor-talk')
		$('.cursor').removeClass('cursor-work')
		$('.cursor span').addClass('hide');
		gsap.to('.cursor', {
			scale: 1,
		})
	}
})

βž• 마우슀λ₯Ό λ”°λΌλ‹€λ‹ˆλŠ” 이미지

  $('.playing-item').mousemove(function(e){
  	var ratio = 100 / $(this).width();
  	var imgX = e.offsetX * ratio *.4 - 20;
  	gsap.to($(this).children('img'), {
  		xPercent: imgX,
  		ease: Power1.easeOut,
    })
  })

πŸ’¬ 이 뢀뢄도 μ»€μ„œμ™€ λΉ„μŠ·ν•˜κ²Œ μ’Œν‘œκ°’μ„ μ΄μš©ν•˜μ—¬ 이미지가 μ΅œμ†Œ xμΆ•μœΌλ‘œ -20%, μ΅œλŒ€ 20%둜 λΉ„μœ¨μ„ κ΅¬ν•˜μ—¬ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.


βœ” κ°€λ‘œμŠ€ν¬λ‘€ κ΅¬ν˜„

πŸ’¬ work-area κ΅¬κ°„μ—μ„œλŠ” μ»€μ„œκ°€ λ³€κ²½λ˜λŠ” 이벀트 뿐만 μ•„λ‹ˆλΌ κ°€λ‘œμŠ€ν¬λ‘€λ‘œ μ „ν™˜λ˜λŠ” μ΄λ²€νŠΈλ„ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ”, gsap ν”ŒλŸ¬κ·ΈμΈμœΌλ‘œ pin을 μ΄μš©ν•΄ x좕을 μ‘°μ •ν•˜μ—¬ κ΅¬ν˜„μ΄ κ°€λŠ₯ν–ˆμŠ΅λ‹ˆλ‹€!

πŸ“Œ gsap pin으둜 κ°€λ‘œμŠ€ν¬λ‘€ κ΅¬ν˜„

  gsap.to('.sc-work .work-area', {
	xPercent: -92 * ($('.work-item').length - 1), 
  	scrollTrigger: {
  		pin: '.sc-work',
  		trigger:'.work-area',
  		start:"top top",
  		end:"+=900%",
  		scrub: .5,
  	},
  })  

πŸ’¬ work-area μ•ˆμ— work-item듀이 μžˆμœΌλ―€λ‘œ work-areaκ°€ xμΆ•μœΌλ‘œ 움직이면 κ°€λ‘œμŠ€ν¬λ‘€μ„ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ’¬ 각 work-itemλ“€μ˜ 넓이가 92%이고 work-itemλ“€μ˜ κ°œμˆ˜μ— 1을 λΉΌλ©΄ λ§ˆμ§€λ§‰ work-item이 보여진 ν›„ λ‹€μ‹œ μ„Έλ‘œ 슀크둀이 κ°€λŠ₯ν•©λ‹ˆλ‹€.



βœ” gsap.utils.toArray / toggleActions

  show = gsap.utils.toArray('.show'); 
  show.forEach((show) => {
  	gsap.fromTo(show,{
  		y: 50,
  		opacity: 0
  	},{
  		y: 0,
  		opacity: 1,
  		scrollTrigger: {
  			trigger: show,
  			start: 'top bottom',
  			end: 'bottom-=20% top',
  			toggleActions: 'play reverse play reverse',
  		},
  	})
  })

πŸ“Œ gsap.utils.toArray

πŸ’¬ μ œμž‘μ„ ν•˜λ©΄μ„œ λ•Œλ‘œλŠ” 같은 효과λ₯Ό 가진 컨텐츠듀이 λ§Žμ•„μ„œ 이걸 λ°°μ—΄λ‘œ λ¬Άμ–΄ μž‘λ™μ‹œν‚€κ²Œ ν•˜λ©΄ 효율적이라고 μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ gsapμ—μ„œ λ°°μ—΄λ‘œ λ³€ν™˜μ‹œμΌœμ£ΌλŠ” gsap.utils.toArrayλ₯Ό μ΄μš©ν•˜μ—¬ μ œμž‘ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

πŸ’¬ νŠΉμ • μš”μ†Œμ— λ„λ‹¬ν–ˆμ„ λ•Œ yκ°’κ³Ό opacity 쑰정을 gsapλ₯Ό μ΄μš©ν•΄ μž‘μ„±ν•œ μ½”λ“œμž…λ‹ˆλ‹€. 이 효과λ₯Ό μ“Έ 컨텐츠듀이 λ„ˆλ¬΄λ‚˜λ„ λ§Žμ•„μ„œ 각각 show 클래슀λ₯Ό μΆ”κ°€ν•˜κ³  gsap.utils.toArrayλ₯Ό μ΄μš©ν•˜μ—¬ forEach둜 λ°˜λ³΅λ¬Έμ„ λŒλ¦¬λ‹ˆ 각 μš”μ†Œλ§ˆλ‹€ gsapλ₯Ό μ•ˆμ¨λ„ 효율적으둜 μž‘μ„±ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€!


πŸ“Œ toggleActions

πŸ’¬ μŠ€ν¬λ‘€μ„ 움직이면 gsapκ°€ μž‘λ™μ„ ν•˜μ§€λ§Œ λ‹€μ‹œ λ˜λŒμ•„κ°ˆ λ•ŒλŠ” λ§ˆμ§€λ§‰ 효과λ₯Ό 기점으둜 λ©ˆμΆ°μžˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ λ˜λŒμ•„κ°ˆ λ•Œμ—λ„ 효과λ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄μ„œ ScrollTrigger에 λΆ€κ°€μ˜΅μ…˜μΈ toggleActions을 μ΄μš©ν•˜μ—¬ μ μš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  toggleActions: 'play none none none'
  //toggleActions: "onEnter, onLeave, onEnterBack, onLeaveBack"

πŸ’¬ toggleActionsλ₯Ό μ μš©ν•˜μ§€ μ•Šμ„ 경우 일반적인 μ„ΈνŒ…μž…λ‹ˆλ‹€.

  • toggleActions
    • 첫번째 μš”μ†ŒλŠ” onEnter둜 scrollTrigger에 start뢀뢄에 듀어와 endμ‹œμ  μ „κΉŒμ§€ μ‹€ν–‰λ©λ‹ˆλ‹€.
    • λ‘λ²ˆμ§Έ μš”μ†ŒλŠ” onLeave둜 endμ‹œμ μ„ λΉ μ Έλ‚˜κ°ˆ λ•Œ μ‹€ν–‰λ©λ‹ˆλ‹€.
    • μ„Έλ²ˆμ§Έ μš”μ†ŒλŠ” onEnterBack으둜 이미 onEnterκ°€ μ‹€ν–‰λœ μ‹œμ μ—μ„œ λ‹€μ‹œ onEnter λΆ€λΆ„μœΌλ‘œ λ“€μ–΄μ˜¬ λ•Œ μ‹€ν–‰λ©λ‹ˆλ‹€.
    • λ§ˆμ§€λ§‰ μš”μ†ŒλŠ” onLeaveBack으둜 λ‹€μ‹œ κ·Έ 뢀뢄을 λ²—μ–΄λ‚˜κ²Œ 되면 μ‹€ν–‰λ©λ‹ˆλ‹€.
    toggleActions: 'play reverse play reverse',
    πŸ’¬ μ œμž‘ν•˜λ©΄μ„œ 직접 μ μš©ν•œ show 뢀뢄에 toggleActionsλ₯Ό 보게 되면 μš”μ†Œμ— λ“€μ–΄μ˜¬ 땐 μ •μƒμ μœΌλ‘œ playκ°€ 되고 endμ‹œμ μ„ λΉ μ Έλ‚˜κ°ˆ λ•Œ μ‹€ν–‰λλ˜ νš¨κ³Όκ°€ λ°˜λŒ€λ‘œ λŒμ•„κ°€κ²Œ λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. 같은 효과λ₯Ό onEnterBackκ³Ό onLeaveBack에도 μ μš©ν•˜μ—¬ 각 νŠΉμ • 슀크둀 μ•‘μ…˜μ— 따라 νš¨κ³Όμ•‘μ…˜μ„ 쀄 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.



βœ” position: sticky


πŸ’¬ position: stickyλŠ” 슀크둀 μœ„μΉ˜κ°€ μž„κ³„μ μ— 이λ₯΄λ©΄ position: fixed와 같이 κ³ μ •μƒνƒœκ°€ λ©λ‹ˆλ‹€.


πŸ’¬ pcν™”λ©΄μ—μ„œ κ°€λ‘œμŠ€ν¬λ‘€λ‘œ μ›€μ§μ΄λ˜ 컨텐츠듀을 λͺ¨λ°”일 ν™”λ©΄μ—μ„œλŠ” work-item 각각 position: sticky 속성을 μ£Όμ–΄μ„œ λ³€κ²½ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

0개의 λŒ“κΈ€