FrontEnd/HTML | JS | CSS

[Vanilla JS] ๋ฐ”ํ…€ ์‹œํŠธ(Bottom Sheet) ๊ตฌํ˜„ํ•˜๊ธฐ

ddonghyeo 2023. 9. 2. 00:03

HTML

<div class="up_sensor"></div>
  <div id="bottomSheet" class="bottom_sheet">
    <div class="bottom_sheet_handle_wrap">
      <div class="bottom_sheet_handle"></div>
    </div>
    <div style="margin-bottom: 40px;"></div>
    <div class="bottom_box">
      <!-- ๋ฐ”ํ…€์‹œํŠธ ๋‚ด์šฉ -->
    </div>
  </div>
</div>

up_sensor๋Š” ๋ฐ”ํ…€์‹œํŠธ๋ฅผ ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋™์ž‘์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์„ผ์„œ์ด๋‹ค.
์‹ค์ œ ๋ฐ”ํ…€ ์‹œํŠธ ์œ„์— ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” ํˆฌ๋ช…ํ•œ ๋ฒฝ์œผ๋กœ, ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋ฉด ์‚ฌ๋ผ์ง„๋‹ค.
์„ผ์„œ๋ฅผ ๋‘์ง€ ์•Š์œผ๋ฉด ๋ฐ”ํ…€ ์‹œํŠธ๋ฅผ ์˜ฌ๋ฆฌ๋Š” ๊ณผ์ •์—์„œ ์Šคํฌ๋กค ๋˜๋ฉด์„œ ์•ˆ์˜ ๋‚ด์šฉ์ด ์Šคํฌ๋กค ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋‹ค.

bottom_sheet_handle์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ”ํ…€์‹œํŠธ๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ๋‚ด๋ฆด ์ˆ˜ ์žˆ๋Š” hint๋ฅผ ์ œ๊ณตํ•œ๋‹ค.




CSS

.bottom_sheet {
    display: flex;
    position: fixed;
    bottom: 0;
    width: 100%;
    height: 10%;
    border-top-left-radius: 30px;
    border-top-right-radius: 30px;
    background-color: #FFFFFF;
    overflow-y: auto;
    flex-direction: column;
    align-items: center;
    transition-duration: 1s;
}

.bottom_sheet_handle_wrap {
    z-index: 1;
    background: linear-gradient(to bottom,
            rgba(255, 255, 255, 1) 20%,
            rgba(255, 255, 255, 0.75) 35%,
            rgba(255, 255, 255, 0.5) 60%,
            rgba(255, 255, 255, 0.25) 85%,
            rgba(255, 255, 255, 0) 100%);
    display: flex;
    justify-content: center;
    align-items: flex-start;
    width: 80%;
    height: 40px;
    margin-bottom: 10px;
    position: fixed;
}

.bottom_sheet_handle {
    width: 20%;
    height: 10px;
    background-color: #7c7979;
    border-radius: 30px;
    margin-top: 10px;
}

.bottom_box {
    width: 80%;
    height: auto;
    margin-top: 10px;
    margin-bottom: 10px;
    padding: 5px;
    border: 1px solid lightgray;
    border-radius: 10px;
    transition-duration: 2s;
    padding-top: 20px;
    padding-bottom: 20px;
}

.up_sensor {
    z-index: 1;
    position: absolute;
    background: transparent;
    width: inherit;
    height: 100%;
    bottom: 0;
    height: 10%;
    transition-duration: 1s;
}

up_sensor๋Š” ๋ฐ”ํ…€์‹œํŠธ๋ณด๋‹ค z-index๊ฐ€ ์ปค์•ผํ•˜๊ณ , background๊ฐ€ transparent์—ฌ์•ผ ํ•œ๋‹ค.

๋ฐ”ํ…€์‹œํŠธ์˜ height๊ฐ’์„ ์ค‘์ ์œผ๋กœ ์˜ฌ๋ฆฌ๊ณ  ๋‚ด๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์—, height๊ฐ’์„ ์ค‘์ ์ ์œผ๋กœ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค.

๋ฐ”ํ…€ ์‹œํŠธ์˜ transition-duration๊ฐ’์„ ํ†ตํ•ด์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์˜ฌ๋ผ๊ฐ€๊ณ  ๋‚ด๋ ค๊ฐ์„ ํ‘œํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

JS


var handle_wrap = document.getElementsByClassName('bottom_sheet_handle_wrap')[0];
var bottom_sheet = document.getElementsByClassName('bottom_sheet')[0];
var up_sensor = document.getElementsByClassName('up_sensor')[0];
let bottom_touch_start = 0;
let bottom_scroll_start;

//up_sensor์—์„œ ํ„ฐ์น˜๊ฐ€ ์›€์ง์˜€์„ ๊ฒฝ์šฐ (๋ฐ”ํ…€์‹œํŠธ๋ฅผ ๊ฑด๋“œ๋ ธ์„ ๊ฒฝ์šฐ) -> ๋ฐ”ํ…€์‹œํŠธ๋ฅผ ์˜ฌ๋ฆผ
up_sensor.addEventListener("touchmove", (e) => {
  bottom_sheet.style.height = 70 + "%" //๋ฐ”ํ…€์‹œํŠธ height๋ฅผ ์˜ฌ๋ฆฌ๊ธฐ 10% -> 70%
  up_sensor.style.height = 70 + "%"; //up_sensor๋„ ๋”ฐ๋ผ๊ฐ€๊ธฐ
  setTimeout(function () {
    up_sensor.style.display = "none";
  }, 1000); // ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์˜ฌ๋ผ๊ฐ„ ํ›„, up_sensor ์‚ฌ๋ผ์ง€๊ธฐ
});


//๋งจ ์œ„์—์„œ ์•„๋ž˜๋กœ ์Šคํฌ๋กคํ–ˆ์„ ๊ฒฝ์šฐ
bottom_sheet.addEventListener("touchstart", (e) => {
  bottom_touch_start = e.touches[0].pageY; // ํ„ฐ์น˜๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์œ„์น˜ ์ €์žฅ
  bottom_scroll_start = bottom_sheet.scrollTop //ํ„ฐ์น˜ ์‹œ์ž‘ ์‹œ ์Šคํฌ๋กค ์œ„์น˜ ์ €์žฅ
});

bottom_sheet.addEventListener("touchmove", (e) => {
  //์œ ์ €๊ฐ€ ์•„๋ž˜๋กœ ๋‚ด๋ ธ์„ ๊ฒฝ์šฐ + ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ๋งจ ์œ„์ผ ๊ฒฝ์šฐ
  if (((bottom_touch_start - e.touches[0].pageY) < 0) && (bottom_scroll_start <= 0)) {
    //๋ฐ”ํ…€์‹œํŠธ ๋‚ด๋ฆฌ๊ธฐ
    bottom_sheet.style.height = 10 + "%"
    up_sensor.style.display = "block"; //up_sensor ๋‹ค์‹œ ๋‚˜ํƒ€๋‚˜๊ธฐ
    up_sensor.style.height = "10%"; //up_sensor height ๋‹ค์‹œ ์ง€์ •
  };
});


//๋งจ ์œ„ ํ•ธ๋“ค์„ ์•„๋ž˜๋กœ ๋‹น๊ฒผ์„ ๊ฒฝ์šฐ
handle_wrap.addEventListener("touchstart", (e) => {
  bottom_touch_start = e.touches[0].pageY; // ํ„ฐ์น˜๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์œ„์น˜ ์ €์žฅ
});
handle_wrap.addEventListener("touchmove", (e) => {
  //์‚ฌ์šฉ์ž๊ฐ€ ์•„๋ž˜๋กœ ๋‚ด๋ ธ์„ ๊ฒฝ์šฐ
  if ((bottom_touch_start - e.touches[0].pageY) < 0) {
    //๋ฐ”ํ…€์‹œํŠธ ๋‚ด๋ฆฌ๊ธฐ
    bottom_sheet.style.height = 10 + "%"
    up_sensor.style.display = "block"; //up_sensor ๋‹ค์‹œ ๋‚˜ํƒ€๋‚˜๊ธฐ
    up_sensor.style.height = "10%"; //up_sensor height ๋‹ค์‹œ ์ง€์ •
  };
});

์‚ฌ์šฉ์ž๊ฐ€ ์•„๋ž˜๋กœ ๋‚ด๋ ธ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ณผ์ • ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์ข€ ๊ธธ์–ด์ง„๋‹ค.


๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒฝ์šฐ :

  1. ์‚ฌ์šฉ์ž๊ฐ€ up_sensor๋ฅผ ์œ„๋กœ ์˜ฌ๋ ธ์„ ๊ฒฝ์šฐ (์‹ค์ œ ์ฝ”๋“œ๋Š” ๊ทธ๋ƒฅ ์›€์ง์ด๊ธฐ๋งŒ ํ•ด๋„ ์˜ฌ๋ผ๊ฐ„๋‹ค.. ์œ„๋กœ ์˜ฌ๋ฆฌ๋Š” ๊ฒฝ์šฐ๋กœ ๊ตฌํ˜„ํ•ด๋„ ์ข‹๋‹ค)
    ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ๋‚ด๋ ค๊ฐ€๋Š” ๊ฒฝ์šฐ :
  2. ์Šคํฌ๋กค ๋งจ ์œ„์—์„œ ์•„๋ž˜๋กœ ์Šคํฌ๋กค ํ–ˆ์„ ๊ฒฝ์šฐ
  3. ๋ฐ”ํ…€์‹œํŠธ ํ•ธ๋“ค์„ ์•„๋ž˜๋กœ ๋‚ด๋ ธ์„ ๊ฒฝ์šฐ
    ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ด๋„ React๋กœ ๊ตฌํ˜„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋‹ค์ˆ˜๋ผ Vanilla JS๋กœ ๊ตฌํ˜„ํ•ด ๋ณด์•˜๋‹ค. ์ฝ”๋“œ๋„ ๊ธธ๊ณ  ๋ณต์žกํ•˜์ง€๋งŒ ์ตœ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์จ๋ดค๋‹ค!!!