½ÃÀÛÆäÀÌÁö·Î Áñ°Üã±âÃß°¡
·Î±×ÀÎ
ȸ¿ø°¡ÀÔ l Ã⼮üũ l ¸¶ÀÌÆäÀÌÁö l CGIMALL
3D ÀÔü À̹ÌÁö ½½¶óÀ̵å : happycgi
ÀÚ·á½Ç »çÀÌÆ®µî·Ï ·©Å·100 ÇÁ·Î±×·¥¸®ºä °ü¸®ÀÚÃßõÀÚ·á Ãʺ¸°¡À̵å
Ä¿¹Â´ÏƼ
Àüü ÆîÃ帱â
Äü¸Þ´º¸µÅ© jquery , CSS , PHP , Javascript , ¹«·áÆùÆ® , ASP
»ó¼¼°Ë»ö
Ȩ > CSS > ±âº» ¼Ò½º > 3D ÀÔü À̹ÌÁö ½½¶óÀÌµå »ó¼¼Á¤º¸
»çÀÌÆ®µî·Ï
Ŭ¶ó¿ìµåű×
javascript
HTML
PHP
2022
Api
ASP
CSS
mysql
jquery
Slide
¸ðµâ
°¶·¯¸®
¸ÞÀϸµ
ÇöÀçÁ¢¼ÓÀÚ ¸í »õ·Î°íħ
3D ÀÔü À̹ÌÁö ½½¶óÀ̵å
¼Ò½ºÅë°èÁ¤º¸ ¿À·ù½Å°í ¹× ¹®ÀÇ
ÇØÇÇÆÀ
³×ƼÁð
Æ®À§ÅÍ·Î º¸³»±â ÆäÀ̽ººÏÀ¸·Î º¸³»±â
¼Ò½ººÐ·ù ±âº» ¼Ò½º
´Ù¿î·Îµå Ƚ¼ö 11 ȸ
°£´Ü¼³¸í À̹ÌÁö°¡ ÀÔüÀûÀ¸·Î ±â¿ï¾îÁö¸ç ÀüȯµÇ´Â 3D À̹ÌÁö ½½¶óÀ̵åÀÔ´Ï´Ù. Æ÷Æ®Æú¸®¿À, °¶·¯¸®, »óǰ À̹ÌÁö ¿µ¿ª µî¿¡ Ȱ¿ëÇϱâ ÁÁ½À´Ï´Ù.
Æò°¡Çϱâ ÈǸ¢ÇÔ ¸Å¿ìÁÁÀ½ ÁÁÀ½ ±¦ÂúÀ½ º¸Åë º°·Î
ȨÆäÀÌÁö¹Ù·Î°¡±â ¼Ò½º´Ù¿î·Îµå µ¥¸ð ¹Ì¸®º¸±â ½ºÅ©·¦Çϱâ

3D ȸÀü È¿°ú¿Í ºÎµå·¯¿î ¿òÁ÷ÀÓÀÌ Àû¿ëµÈ À̹ÌÁö ½½¶óÀÌµå ¿¹Á¦ÀÔ´Ï´Ù.
ÇöÀç ¼±ÅÃµÈ À̹ÌÁö´Â Áß¾Ó¿¡ Å©°Ô °­Á¶µÇ°í, ¾çÂÊ À̹ÌÁö´Â ±â¿ï¾îÁø ÇüÅ·Π¹èÄ¡µÇ¾î ÀÔüÀûÀÎ È­¸é ±¸¼ºÀ» ¸¸µé ¼ö ÀÖ½À´Ï´Ù.


ÀÌÀü/´ÙÀ½ ¹öưÀ» ÅëÇØ À̹ÌÁö¸¦ À̵¿ÇÒ ¼ö ÀÖÀ¸¸ç, ÇÏ´Ü Á¡ ¹öư°ú À̹ÌÁö Ŭ¸¯À» ÅëÇØ ¿øÇÏ´Â À̹ÌÁö·Î ¹Ù·Î À̵¿ÇÒ ¼ö ÀÖ½À´Ï´Ù.ÀϹÝÀûÀÎ À̹ÌÁö ½½¶óÀ̵庸´Ù ½Ã°¢ÀûÀÎ ÁýÁßµµ°¡ ³ô¾Æ Æ÷Æ®Æú¸®¿À, °¶·¯¸®, ºê·£µå ¼Ò°³, »óǰ À̹ÌÁö, À̺¥Æ® È­¸é µî¿¡ Ȱ¿ëÇϱâ ÁÁ½À´Ï´Ù.

HTML ±¸Á¶

<div id="app"></div>



CSS ¼Ò½º

@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap');


* {

  box-sizing: border-box;

}


body {

  min-height: 100vh;

  background-color: #ececec;

  font-family: "Bricolage Grotesque", sans-serif;

  

  display: grid;

  place-content: center;

  

  overflow: hidden;

}



JS ¼Ò½º

import React, { useState, useEffect } from "https://esm.sh/react@19"

import { createRoot } from "https://esm.sh/react-dom@19/client"

import { motion } from "https://esm.sh/motion/react"

import { ChevronLeft, ChevronRight } from "https://esm.sh/lucide-react"


const ASSETS = [

  {

    src: 'https://images.unsplash.com/photo-1769921546096-7a648d953a3e?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'urban exploration',

  },

  {

    src: 'https://images.unsplash.com/photo-1777726515600-65be20641e1b?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'night scene',

  },

  {

    src: 'https://images.unsplash.com/photo-1776582929657-9710d9cfa46a?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'yellow wildflowers',

  },

  {

    src: 'https://images.unsplash.com/photo-1776582929656-78ad8b515d75?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'street with mount fuji',

  },

  {

    src: 'https://images.unsplash.com/photo-1775990630948-3c1f696f4ab1?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'bridgestone bicycle shop',

  },

  {

    src: 'https://images.unsplash.com/photo-1775380744191-8fbff371c40b?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'train window view',

  },

  {

    src: 'https://images.unsplash.com/photo-1774775479879-082fd47d41e1?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'train tracks',

  },

  {

    src: 'https://images.unsplash.com/photo-1773544517453-95c148cb42b7?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'lawson convenience store',

  },

  {

    src: 'https://images.unsplash.com/photo-1771385809377-9b0348e1f8dc?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'street scene',

  },

  {

    src: 'https://images.unsplash.com/photo-1775990631076-f6f208079475?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',

    title: 'japanese culture',

  },

]


const App = () => {

  const [activeIndex, setActiveIndex] = useState(3)


  const toPrev = () => {

    setActiveIndex(prev => Math.max(0, prev - 1))

  }


  const toNext = () => {

    setActiveIndex(prev => Math.min(ASSETS.length - 1, prev + 1))

  }


  const toSlide = (index) => {

    setActiveIndex(index)

  }


  return (

    <div className="p-2 text-neutral-800 select-none">

      {/* carousel wrapper */}

      <div className="w-30 md:w-50 mt-8">

        {/* slides container */}

        <motion.div 

          className="flex w-fit" 

          animate={{ x: `${-activeIndex * 100 / ASSETS.length}%` }}

          transition={{ type: 'spring', bounce: 0.2, duration: 0.8 }}

          >


          {ASSETS.map((item, i) => {

            const isActive = activeIndex === i

            {/* slide */}

            return (

              <div className="perspective-midrange" key={i}>

                <motion.div 

                  className="w-30 md:w-50 aspect-3/4 flex flex-col items-center gap-2 will-change-[transform,scale]"

                  animate={{ rotateY: (activeIndex - i) * 60, scale: isActive ? 1 : 0.85  }}

                  transition={{ type: 'spring', bounce: 0.1, duration: 1 }}

                  >

                  <img src={item.src} alt={item.title} className="w-full h-full object-cover rounded-lg" onClick={() => toSlide(i)} />


                  <motion.div 

                    className="text-xs md:text-sm whitespace-nowrap will-change-[opacity,filter]" 

                    animate={{ filter: isActive ? 'blur(0)' : 'blur(2px)', opacity: isActive ? 1 : 0 }}

                    >

                    {item.title}

                  </motion.div>

                </motion.div>

              </div>

            )

          })}

        </motion.div>

      </div>


      {/* controls */}

      <div className="fixed bottom-4 left-0 right-0 w-fit px-2 mx-auto flex items-center gap-4 justify-center text-neutral-700 rounded-full bg-neutral-200/50 px-2 backdrop-blur-xs border border-neutral-200/80 shadow-sm">

        {/* prev button */}

        <button onClick={toPrev} className="p-2 cursor-pointer">

          <ChevronLeft />

        </button>

        {/* slide dots */}

        <div className="w-[180px] flex justify-center items-center gap-2">

          {ASSETS.map((_, i) => (

            <div 

              key={i} 

              onClick={() => toSlide(i)}

              className={`rounded-full cursor-pointer h-2 transition-[width,background-color] duration-300 ${activeIndex === i ? 'w-7 bg-current' : 'w-2 bg-current/30'}`}>

            </div>

          ))}

        </div>

        {/* next button */}

        <button onClick={toNext} className="p-2 cursor-pointer">

          <ChevronRight />

        </button>

      </div>


    </div>

  )

}


const root = createRoot(document.getElementById("app"))


root.render(<App />)


 

³×ƼÁð ÀÇ°ß   ÀÌ¿ëÇϽŠÀÚ·áÀÇ Èı⸦ ÀÚÀ¯·Ó°Ô ÀÛ¼ºÇϼ¼¿ä. (»ó¾÷ÀûÀÎ ±¤°í ¹× µµ¹è¼º ±Û µîÀº »çÀüÅ뺸¾øÀÌ »èÁ¦µÉ ¼ö ÀÖ½À´Ï´Ù.)
³»¿ë ¾ÆÀ̵ð Àǰ߳²±â±â
µî·ÏµÈ ÀǰßÀÌ ¾ø½À´Ï´Ù.
1
À̸§
³»¿ë
:³×¸Â¾Æ¿ä: :È­³ª´Â±º¿ä: :Àá¿Í: :¿ì¿ïÇØ: :À̰ǾƳÄ: :¿ÕÇÏÇÏ: ¿Õ¿ôÀ½~ ³î·¥~
Æò°¡Çϱâ ÈǸ¢ÇÔ ¸Å¿ìÁÁÀ½ ÁÁÀ½ ±¦ÂúÀ½ º¸Åë º°·Î
µµ¹è¹æÁöŰ
 98625541 º¸ÀÌ´Â µµ¹è¹æÁö۸¦ ÀÔ·ÂÇϼ¼¿ä.