DevelopmentπŸ‘©πŸ»‍🦳/Dynamic Web

μ›Ήμ—μ„œ λ§Œλ‚˜λŠ” μ „κΈ°μ°¨μ˜ λ―Έν•™: 포λ₯΄μ‰ λ””μžμΈ 개발기

AdaLovelace 2023. 11. 29. 20:47

 

 

 

μ›Ήμ‚¬μ΄νŠΈμ˜ ν•΅μ‹¬ μš”μ†Œ: FOOTER, LOGOS, λ„€λΉ„κ²Œμ΄μ…˜ λ©”뉴 κ΅¬ν˜„ν•˜κΈ°
μ›Ή κ°œλ°œμ˜ μ„Έκ³„μ—μ„œ, μ‚¬μš©μž κ²½ν—˜(UX)λŠ” μ΅œμš°μ„ μž…λ‹ˆλ‹€. μ˜€λŠ˜μ€ μ›Ήμ‚¬μ΄νŠΈμ—μ„œ μ€‘μš”ν•œ μ„Έ λΆ€λΆ„인 FOOTER, LOGOS, κ·Έλ¦¬κ³  λ„€λΉ„κ²Œμ΄μ…˜ λ©”뉴λ₯Ό μ–΄λ–»κ²Œ κ΅¬ν˜„ν•˜λŠ”μ§€ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

1. FOOTER의 μ€‘μš”μ„±κ³Ό κ΅¬ν˜„
μ›Ήμ‚¬μ΄νŠΈμ˜ ν•˜λ‹¨μ— μœ„μΉ˜ν•˜λŠ” FOOTERλŠ” μ‚¬μš©μžμ—κ²Œ μœ μš©ν•œ μ •보λ₯Ό μ œκ³΅ν•˜λŠ” μ€‘μš”ν•œ μ˜μ—­μž…λ‹ˆλ‹€. μ—¬κΈ°μ—λŠ” μ €μž‘κΆŒ μ •보, μ—°λ½μ²˜, μ‚¬μ΄νŠΈ λ§΅, μ†Œμ…œ λ―Έλ””μ–΄ λ§ν¬ λ“±μ΄ ν¬ν•¨λ  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. νš¨κ³Όμ μΈ FOOTER λ””μžμΈμ€ μ‚¬μš©μžμ˜ μ›Ήμ‚¬μ΄νŠΈ νƒμƒ‰μ„ λ•κ³ , ν•„μš”ν•œ μ •보λ₯Ό μ‰½κ²Œ μ°Ύμ„ μˆ˜ μžˆλ„둝 ν•©λ‹ˆλ‹€.

 

(이전 μ½”λ“œμ‚¬μš©μ‹œ 메뉴바가 열린이후 ν•˜λ‹¨ μŠ€ν¬λ‘€λ§μ‹œ ν•¨κ»˜ λΈ”λŸ¬μ²˜λ¦¬κ°€λ˜μ„œ 화면ꡬ성이 투λͺ…ν•˜κ²Œ λ³΄μ΄λŠ” 였λ₯˜λ₯Ό

μ œμž‘μ€‘ λ§Œλ‚˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€ μžλ°”μŠ€ν¬λ¦½νŠΈμ½”λ“œμ— κ·Έλž˜μ„œ 메뉴가 열리고 λ‹«νžλ•Œ λΈ”λŸ¬νš¨κ³Όκ°€ 적용되고 μ œκ±°λ˜λ„λ‘ λ‹€μ‹œ μ„€κ³„ν•˜μ˜€μŠ΅λ‹ˆλ‹€)

2. LOGOS μ„Ήμ…˜
LOGOS μ„Ήμ…˜μ€ λΈŒλžœλ“œ μΈμ§€λ„λ₯Ό λ†’μ΄λŠ” λ° μ€‘μš”ν•œ μ—­ν• μ„ ν•©λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œλŠ” μ›Ήμ‚¬μ΄νŠΈκ°€ ν˜‘λ ₯ν•˜λŠ” λ‹€μ–‘ν•œ λΈŒλžœλ“œλ‚˜ ν›„μ›μ‚¬μ˜ λ‘œκ³ λ₯Ό ν‘œμ‹œν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. λ‘œκ³ λŠ” μ‹œκ°μ μœΌλ‘œ λ§€λ ₯μ μ΄λ©΄μ„œλ„ μ›Ήμ‚¬μ΄νŠΈμ˜ μ „λ°˜μ μΈ λ””μžμΈκ³Ό μ‘°ν™”λ₯Ό μ΄λ£¨μ–΄μ•Ό ν•©λ‹ˆλ‹€.

3. λ™μ μΈ λ„€λΉ„κ²Œμ΄μ…˜ λ©”뉴
μ‚¬μš©μžμ˜ μ›Ήμ‚¬μ΄νŠΈ λ‚΄ νƒμƒ‰μ„ μ‰½κ²Œ λ§Œλ“œλŠ” μš”μ†Œ μ€‘ ν•˜λ‚˜λŠ” λ„€λΉ„κ²Œμ΄μ…˜ λ©”λ‰΄μž…λ‹ˆλ‹€. μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό μ΄μš©ν•΄ λ©”뉴λ₯Ό νš¨κ³Όμ μœΌλ‘œ μ œμ–΄ν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆλ₯Ό λ“€μ–΄, λ©”뉴 ν‘œμ‹œ λ° μˆ¨κΉ€, λΈ”λŸ¬ νš¨κ³Ό μ μš©, λͺ¨λ°”μΌμ—μ„œμ˜ λ©”뉴 κ΄€λ¦¬ λ“±μ΄ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ κΈ°λŠ₯은 μ›Ήμ‚¬μ΄νŠΈμ˜ μ‚¬μš©μž μΉœν™”성을 ν¬κ²Œ ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€.

이 ν¬μŠ€νŠΈμ—μ„œλŠ” κ° μ„Ήμ…˜μ˜ κ΅¬μ²΄μ μΈ κ΅¬ν˜„ λ°©λ²•κ³Ό μ½”λ“œ μ˜ˆμ‹œλ₯Ό μ œκ³΅ν•˜κ² μŠ΅λ‹ˆλ‹€. μ›Ή κ°œλ°œμ— κ΄€μ‹¬ μžˆλŠ” λΆ„λ“€μ—κ²Œ μ΄ μ •보가 λ„움이 λ˜κΈ°λ₯Ό λ°”λžλ‹ˆλ‹€.

 

λ³€κ²½λœ μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œ

const navMenu = document.getElementById('nav-menu'),
      navToggle = document.getElementById('nav-toggle'),
      navClose = document.getElementById('nav-close'),
      header = document.getElementById('header'); // 헀더 μš”μ†Œ

/*===== MENU SHOW =====*/
/* Validate if constant exists */
if(navToggle){
    navToggle.addEventListener('click', () =>{
        navMenu.classList.add('show-menu');
        header.style.backdropFilter = 'none'; // 메뉴가 열릴 λ•Œ λΈ”λŸ¬ 효과 제거
    })
}

/*===== MENU HIDDEN =====*/
/* Validate if constant exists */
if(navClose){
    navClose.addEventListener('click', () =>{
        navMenu.classList.remove('show-menu');
        header.style.backdropFilter = 'blur(10px)'; // 메뉴가 λ‹«νž λ•Œ λΈ”λŸ¬ 효과 적용
    })
}

/*===== SCROLL EVENT FOR BLUR EFFECT =====*/
window.addEventListener('scroll', () => {
  if (!navMenu.classList.contains('show-menu')) {
    // 메뉴가 λ‹«ν˜€ μžˆμ„ λ•Œ μŠ€ν¬λ‘€μ— 따라 λΈ”λŸ¬ 효과 쑰절
    header.style.backdropFilter = window.scrollY > 50 ? 'blur(10px)' : 'none';
  }
});

/*=============== REMOVE MENU MOBILE ===============*/
const navLink = document.querySelectorAll('.nav__link')

const linkAction = () =>{
    const navMenu = document.getElementById('nav-menu')
    // When we click on each nav__link, we remove the show-menu class
    navMenu.classList.remove('show-menu')
}
navLink.forEach(n => n.addEventListener('click', linkAction))

/*=============== CHANGE BACKGROUND HEADER ===============*/

function scrollHeader  () {
    const header = document.getElementById('header')
    // When the scroll is greater than 50 viewport height, add the scroll-header class to the header tag
    if(this.scrollY >= 50) header.classList.add('scroll-header'); 
    else header.classList.remove('scroll-header')
}
window.addEventListener('scroll', scrollHeader)


/*=============== CHANGE BACKGROUND HEADER ===============*/
document.querySelectorAll('.home__car-number').forEach(number => {
    const updateCount = () => {
        const target = +number.getAttribute('data-target');
        const count = +number.innerText;
        const increment = target / 100; 

        if (count < target) {
            number.innerText = Math.ceil(count + increment);
            number.classList.add('is-visible'); // 여기에 클래슀 μΆ”κ°€
            setTimeout(updateCount, 10);
        } else {
            number.innerText = target;
            number.classList.add('is-visible'); // λͺ©ν‘œμ— λ„λ‹¬ν–ˆμ„ λ•Œλ„ 클래슀 μΆ”κ°€
        }
    };

    updateCount();
});

/*=============== POPULAR SWIPER ===============*/

let swiperPopular = new Swiper(".popular__container", {
    loop: true,
    spaceBetween: 24,
    slidesPerView: 'auto',
    grabCursor: true,

  pagination: {
    el: ".swiper-pagination",
    dynamicBullets: true,
  },
  
  breakpoints: {
  768: {
    slidesPerView: 3,
  },
  1024: {
    spaceBetween: 48,
  },
},
});

/*=============== MIXITUP FILTER FEATURED ===============*/
let mixerFeatured = mixitup('.featured__content', {
    selectors: {
        target: '.featured__card'
    },
    animation: {
        duration: 300
    }
});

/* Link active featured */ 
const linkFeatured = document.querySelectorAll('.featured__item')


function activeFeatured(){
    linkFeatured.forEach(l=> l.classList.remove('active-featured'))
    this.classList.add('active-featured')
}
linkFeatured.forEach(l=> l.addEventListener('click',activeFeatured))
/*=============== SHOW SCROLL UP ===============*/ 
const scrollUp = () =>{
	const scrollUp = document.getElementById('scroll-up')
    // When the scroll is higher than 350 viewport height, add the show-scroll class to the a tag with the scrollup class
	this.scrollY >= 350 ? scrollUp.classList.add('show-scroll')
						: scrollUp.classList.remove('show-scroll')
}
window.addEventListener('scroll', scrollUp)

/*=============== SCROLL SECTIONS ACTIVE LINK ===============*/
const sections = document.querySelectorAll('section[id]')
    
const scrollActive = () =>{
  	const scrollDown = window.scrollY

	sections.forEach(current =>{
		const sectionHeight = current.offsetHeight,
			  sectionTop = current.offsetTop - 58,
			  sectionId = current.getAttribute('id'),
			  sectionsClass = document.querySelector('.nav__menu a[href*=' + sectionId + ']')

		if(scrollDown > sectionTop && scrollDown <= sectionTop + sectionHeight){
			sectionsClass.classList.add('active-link')
		}else{
			sectionsClass.classList.remove('active-link')
		}                                                    
	})
}
window.addEventListener('scroll', scrollActive)

 

 

 

 

μ˜€μž‘λ™ν•˜λŠ” ν™”λ©΄μž…λ‹ˆλ‹€
정상 μž‘λ™ν•˜λŠ” ν™”λ©΄μž…λ‹ˆλ‹€

 

 

 

/*=============== OFFER ===============*/
.offer{
  position: relative;
}

.offer__container{
  grid-template-rows: max-content 224px;
}

.offer__bg{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.offer__data,
.offer__img{
  position: relative;
}

.offer__data{
  text-align: center;
}

.offer__title{
  margin-bottom: 2rem;
}

.offer__description{
  margin-bottom: 3rem;
}

.offer__img{
  position: absolute;
  max-width: initial;
  width: 400px;
  bottom: 2rem;
  right: -5.5rem;
}
/*=============== LOGOS ===============*/
.logos__img{
  width: 40px;
  opacity: .4;
  transition: .3s;
}

.logos__container{
  grid-template-columns: repeat(3, max-content);
  justify-content: center;
  align-items: center;
  gap: 4rem;
  padding-bottom: 2rem;
}

.logos__img:hover{
  opacity: 1;
}
/*=============== FOOTER ===============*/
.footer{
  position: relative;
  overflow: hidden;
}

.footer .shape__big,
.footer .shape__small{
  position: absolute;
}

.footer .shape__big{
  width: 300px;
  height: 300px;
  left: -12rem;
  top: 6rem;
}

.footer .shape__small{
  right: -13rem;
  bottom: -6rem;
}

.footer__container{
  row-gap: 2.5rem;
  position: relative;
}

.footer__logo{
  display: inline-flex;
  align-items: center;
  column-gap: .25rem;
  color: var(--title-color);
  font-size: var(--h2-font-size);
  font-weight: var(--font-semi-bold);
  margin-bottom: 1.25rem;
  transition: .3s;
}

.footer__logo i{
  font-size: 1.5rem;
  font-weight: 500;
}

.footer__logo:hover{
  color: var(--first-color);
}

.footer__title{
  font-size: var(--h3-font-size);
  margin-bottom: 1.25rem;
}

.footer__links{
  display: flex;
  flex-direction: column;
  row-gap: .5rem;
}

.footer__link,
.footer__social-link{
  color: var(--text-color);
  transition: .3s;
}

.footer__link:hover,
.footer__social-link:hover{
  color: var(--title-color);
}

.footer__social{
  display: flex;
  column-gap: 1.5rem;
}

.footer__social-link{
  font-size: 1.25rem;
}

.footer__copy{
  display: block;
  margin-top: 4.5rem;
  text-align: center;
  font-size: var(--small-font-size);
  color: var(--text-color-light);
}
/*=============== SCROLL BAR ===============*/
::-webkit-scrollbar{
  width: .6rem;
  background-color: hsl(219, 4%, 16%);
  border-radius: 1rem;
}

::-webkit-scrollbar-thumb{
  background-color: hsl(219, 4%, 24%);
}

::-webkit-scrollbar-thumb:hover{
  background-color: hsl(219, 4%, 32%);
}

/*=============== SCROLL UP ===============*/
.scrollup{
  position: fixed;
  right: 1rem;
  bottom: -30%;
  background-color: hsla(0, 0%, 100%, .1);
  backdrop-filter: blur(16px);
  display: inline-flex;
  padding: .45rem;
  border-radius: .5rem;
  font-size: 1.15rem;
  color: var(--white-color);
  z-index: var(--z-tooltip);
  transition: .4s;
}

.scrollup:hover{
  transform: translateY(-.25rem);
}
/* Show Scroll Up*/
.show-scroll{
  bottom: 3rem;
}

μŠ€νƒ€μΌλ§μ„ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€

 

μ΄λ²ˆμ— μ‚¬μš©ν•œ λΌμ΄λΈŒλŸ¬λ¦¬μž…λ‹ˆλ‹€

 

Featured Luxury Cars

μƒˆλ‘œμš΄ 라이브러리 kunkalabsλ₯Ό μ‚¬μš©ν•˜μ—¬ Featured Luxury Cars메뉴λ₯Ό κ΅¬μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€

 

 

λ²„νŠΌ κ΅¬ν˜„μžμ²΄κ°€ 맀우 λΆ€λ“œλŸ½κ²Œλ©λ‹ˆλ‹€ γ…Žγ…Ž

ν΄λ¦­μ‹œ μ»¬λŸ¬κ°€ μžμ—°μŠ€λŸ½κ²Œ μ‚½μž…λ˜λ©΄μ„œ μ˜¬λΌμ˜€λŠ”κ²Œ μ˜ˆμ˜λ‹€κ³  μƒκ°λ¬λ˜ 뢀뢄쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€

 

 

μ‚¬μš©λœ ν•¨μˆ˜ λ¦¬λ·°ν•΄λ³΄μžλ©΄

 

 

const linkFeatured = document.querySelectorAll('.featured__item'): .featured__item ν΄λž˜μŠ€λ₯Ό κ°€μ§„ λͺ¨λ“  μš”μ†Œλ₯Ό μ„ νƒν•˜κ³ , linkFeatured μƒμˆ˜μ— μ΄λ“€μ˜ λͺ©λ‘μ„ μ €μž₯ν•©λ‹ˆλ‹€.

function activeFeatured() { ... }: activeFeaturedλΌλŠ” ν•¨μˆ˜λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. 

이 ν•¨μˆ˜λŠ” ν΄λ¦­λœ μš”μ†Œμ— 'active-featured' ν΄λž˜μŠ€λ₯Ό μΆ”κ°€ν•˜κ³ , λ‹€λ₯Έ λͺ¨λ“  μš”μ†Œμ—μ„œ μ΄ ν΄λž˜μŠ€λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.

linkFeatured.forEach(l => l.classList.remove('active-featured')): linkFeatured에 

μ €μž₯된 λͺ¨λ“  μš”μ†Œλ₯Ό μˆœνšŒν•˜λ©΄μ„œ, κ° μš”μ†Œμ—μ„œ 'active-featured' ν΄λž˜μŠ€λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.

this.classList.add('active-featured'): ν˜„μž¬ ν΄λ¦­λœ μš”μ†Œ(thisλ₯Ό ν†΅ν•΄ μ°Έμ‘°)에 'active-featured' ν΄λž˜μŠ€λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. 

μ΄λŠ” μ‚¬μš©μžκ°€ ν΄λ¦­ν•œ μš”μ†Œλ₯Ό μ‹œκ°μ μœΌλ‘œ κ°•μ‘°ν•˜λŠ” λ° μ‚¬μš©λ©λ‹ˆλ‹€.

linkFeatured.forEach(l => l.addEventListener('click', activeFeatured)): linkFeatured에 μ €μž₯된 λͺ¨λ“  μš”μ†Œμ— λŒ€ν•΄ 

클릭 μ΄λ²€νŠΈ λ¦¬μŠ€λ„ˆλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. μ‚¬μš©μžκ°€ μ΄ μš”μ†Œλ“€ μ€‘ ν•˜λ‚˜λ₯Ό ν΄λ¦­ν•˜λ©΄ activeFeatured ν•¨μˆ˜κ°€ μ‹€ν–‰λ˜μ–΄, 

클릭된 μš”μ†Œμ— 'active-featured' ν΄λž˜μŠ€κ°€ μΆ”κ°€λ˜κ³  λ‹€λ₯Έ μš”μ†Œλ“€μ—μ„œλŠ” μ œκ±°λ©λ‹ˆλ‹€.

이 μ½”λ“œλŠ” μ‚¬μš©μžκ°€ 'featured' μ„Ήμ…˜μ˜ λ‹€λ₯Έ ν•­λͺ©μ„ ν΄λ¦­ν•  λ•Œλ§ˆλ‹€ μ‹œκ°μ  ν”Όλ“œλ°±μ„ μ œκ³΅ν•˜μ—¬, ν˜„μž¬ μ„ νƒλœ ν•­λͺ©μ„ λͺ…ν™•ν•˜κ²Œ ν‘œμ‹œν•©λ‹ˆλ‹€.

 

 

scrollrevealjs의 ν™ˆνŽ˜μ΄μ§€ 메인이닀

μ΄λ ‡κ²Œ λ‘λ²ˆμ§Έ ν”„λ‘œμ νŠΈλ„ 성황리에 λ§ˆμ³€μŠ΅λ‹ˆλ‹€ 

λ‹€μŒ ν”„λ‘œμ νŠΈλŠ” λ¦¬μ•‘νŠΈλ‘œ λŒμ•„μ˜€λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€πŸ€—