iOS & Android Style Bottom Sheet In JavaScript

Category: Javascript | May 18, 2022
Author:ivteplo
Views Total:202 views
Official Page:Go to website
Last Update:May 18, 2022
License:MIT

Preview:

iOS & Android Style Bottom Sheet In JavaScript

Description:

Bottom sheets are used on mobile applications all the time. They offer a simple way to display extra information or actions when needed.

In this article, I will show you how to create a draggable, touch-enabled, iOS & Android inspired bottom sheet with smooth slide animations on the page by using plain JavaScript. Let’s get started.

How to use it:

1. The required HTML structure for the bottom sheet. Feel free to add or remove content by following the comments as follows:

<!-- The sheet component -->
<div id="sheet" class="column items-center justify-end" aria-hidden="true">
  <!-- Dark background for the sheet -->
  <div class="overlay"></div>
  <!-- The sheet itself -->
  <div class="contents column">
    <!-- Sheet controls -->
    <header class="controls">
      <!-- The thing to drag if you want to resize the sheet -->
      <div class="draggable-area">
        <div class="draggable-thumb"></div>
      </div>
      <!-- Button to close the sheet -->
      <button class="close-sheet" type="button" title="Close the sheet">&times;</button>
    </header>
    <!-- Body of the sheet -->
    <main class="body fill column">
      <h2>Hello, World!</h2>
    </main>
  </div>
</div>

2. The core CSS styles for the bottom sheet. Feel free to override the CSS variables to create your own styles.

:root {
  --background: #fff;
  --foreground: #000;
  --divider: #dcdcdc;
  --overlay: #888;
}
@media (prefers-color-scheme: dark) {
  :root {
    --background: #000;
    --foreground: #fff;
    --divider: #333;
  }
}
#sheet {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  visibility: visible;
  transition: opacity 0.5s, visibility 0.5s;
}
#sheet[aria-hidden="true"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
#sheet .overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
  background: var(--overlay);
  opacity: 0.5;
}
#sheet .contents {
  border-radius: 1rem 1rem 0 0;
  background: var(--background);
  position: relative;
  overflow-y: hidden;
  --default-transitions: transform 0.5s, border-radius 0.5s;
  transition: var(--default-transitions);
  transform: translateY(0);
  max-height: 100vh;
  height: 30vh;
  max-width: 70rem;
  box-sizing: border-box;
  padding: 1rem;
  padding-top: 3rem;
}
#sheet .contents:not(.not-selectable) {
  transition: var(--default-transitions), height 0.5s;
}
#sheet .contents.fullscreen {
  border-radius: 0;
}
#sheet[aria-hidden="true"] .contents {
  transform: translateY(100%);
}
#sheet .draggable-area {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 3rem;
  margin: auto;
  padding: 1rem;
  cursor: grab;
}
#sheet .draggable-thumb {
  width: inherit;
  height: 0.25rem;
  background: var(--divider);
  border-radius: 0.125rem;
}
#sheet .close-sheet {
  position: absolute;
  right: 0;
  top: 0;
  border: none;
}
#sheet .body {
  height: 100%;
  overflow-y: auto;
  gap: 1rem;
}

3. The main JavaScript to enable the bottom sheet.

const $ = document.querySelector.bind(document)
const sheet = $("#sheet")
const sheetContents = sheet.querySelector(".contents")
const draggableArea = sheet.querySelector(".draggable-area")
let sheetHeight // in vh
const setSheetHeight = (value) => {
  sheetHeight = Math.max(0, Math.min(100, value))
  sheetContents.style.height = `${sheetHeight}vh`
  if (sheetHeight === 100) {
    sheetContents.classList.add("fullscreen")
  } else {
    sheetContents.classList.remove("fullscreen")
  }
}
const setIsSheetShown = (value) => {
  sheet.setAttribute("aria-hidden", String(!value))
}
// Open the sheet when clicking the 'open sheet' button
setSheetHeight(Math.min(50, 720 / window.innerHeight * 100))
setIsSheetShown(true)
// Hide the sheet when clicking the 'close' button
sheet.querySelector(".close-sheet").addEventListener("click", () => {
  setIsSheetShown(false)
})
// Hide the sheet when clicking the background
sheet.querySelector(".overlay").addEventListener("click", () => {
  setIsSheetShown(false)
})
const touchPosition = (event) =>
  event.touches ? event.touches[0] : event
let dragPosition
const onDragStart = (event) => {
  dragPosition = touchPosition(event).pageY
  sheetContents.classList.add("not-selectable")
  draggableArea.style.cursor = document.body.style.cursor = "grabbing"
}
const onDragMove = (event) => {
  if (dragPosition === undefined) return
  const y = touchPosition(event).pageY
  const deltaY = dragPosition - y
  const deltaHeight = deltaY / window.innerHeight * 100
  setSheetHeight(sheetHeight + deltaHeight)
  dragPosition = y
}
const onDragEnd = () => {
  dragPosition = undefined
  sheetContents.classList.remove("not-selectable")
  draggableArea.style.cursor = document.body.style.cursor = ""
  if (sheetHeight < 25) {
    setIsSheetShown(false)
  } else if (sheetHeight > 75) {
    setSheetHeight(100)
  } else {
    setSheetHeight(50)
  }
}
draggableArea.addEventListener("mousedown", onDragStart)
draggableArea.addEventListener("touchstart", onDragStart)
window.addEventListener("mousemove", onDragMove)
window.addEventListener("touchmove", onDragMove)
window.addEventListener("mouseup", onDragEnd)
window.addEventListener("touchend", onDragEnd)

You Might Be Interested In:


Leave a Reply