การสร้างคอมโพเนนต์โทสต์

ภาพรวมพื้นฐานของวิธีสร้างส่วนประกอบของขนมปังปิ้งที่ปรับเปลี่ยนได้และเข้าถึงได้ง่าย

ในโพสต์นี้ ฉันอยากแชร์วิธีคิดวิธีสร้างส่วนประกอบของขนมปังปิ้ง ลองใช้งาน การสาธิต

การสาธิต

หากชอบวิดีโอ นี่คือโพสต์นี้เวอร์ชัน YouTube

ภาพรวม

ข้อความโทสต์คือข้อความสั้นๆ แบบไม่โต้ตอบ แบบแพสซีฟ และไม่พร้อมกันสำหรับผู้ใช้ โดยทั่วไปจะใช้เป็นรูปแบบการตอบสนองของอินเทอร์เฟซสำหรับการแจ้งให้ผู้ใช้ทราบผลของการดำเนินการ

การโต้ตอบ

ข้อความโทสต์ต่างจากการแจ้งเตือน การแจ้งเตือน และข้อความแจ้งเนื่องจากไม่ใช่แบบอินเทอร์แอกทีฟ ไม่ได้มีไว้เพื่อปิดหรือคงอยู่ตลอด การแจ้งเตือนมีไว้สำหรับข้อมูลสำคัญมากกว่า ข้อความแบบซิงโครนัสที่ต้องมีการโต้ตอบ หรือข้อความระดับระบบ (ตรงข้ามกับระดับหน้าเว็บ) ข้อความโทสต์จะเป็นแบบไม่ต้องสงสัยมากกว่ากลยุทธ์การแจ้งเตือนอื่นๆ

Markup

องค์ประกอบ <output> เป็นทางเลือกที่ดีสำหรับข้อความโทสต์เนื่องจากมีการประกาศให้โปรแกรมอ่านหน้าจออ่าน HTML ที่ถูกต้องเป็นพื้นฐานที่ปลอดภัยสำหรับเราในการปรับปรุงด้วย JavaScript และ CSS และยังมี JavaScript จำนวนมาก

ขนมปังปิ้ง

<output class="gui-toast">Item added to cart</output>

เพื่อให้ครอบคลุมมากขึ้นได้โดยการเพิ่ม role="status" ซึ่งจะมีทางเลือกสำรองหากเบราว์เซอร์ไม่ได้ให้บทบาทโดยนัยแก่องค์ประกอบ <output> ตามข้อกำหนด

<output role="status" class="gui-toast">Item added to cart</output>

ภาชนะบรรจุขนมปัง

แสดงข้อความโทสต์ได้มากกว่า 1 รายการต่อครั้ง ในการรวมข้อความโทสต์หลายรายการ จะใช้คอนเทนเนอร์ คอนเทนเนอร์นี้จัดการกับตำแหน่งของ ข้อความโทสต์บนหน้าจอด้วย

<section class="gui-toast-group">
  <output role="status">Wizard Rose added to cart</output>
  <output role="status">Self Watering Pot added to cart</output>
</section>

เลย์เอาต์

ฉันเลือกปักหมุดข้อความโทสต์ไว้ที่ inset-block-end ของวิวพอร์ต และหากมีการเพิ่มข้อความโทสต์มากขึ้น ข้อความโทสต์จะซ้อนกันจากขอบหน้าจอ

คอนเทนเนอร์ GUI

ภาชนะบรรจุข้อความโทสต์จะออกแบบเลย์เอาต์ทั้งหมดสําหรับการนําเสนอข้อความโทสต์ fixed ไปยังวิวพอร์ตและใช้พร็อพเพอร์ตี้เชิงตรรกะ inset เพื่อระบุขอบที่จะปักหมุด บวก padding เล็กน้อยจากขอบ block-end เดียวกัน

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

ภาพหน้าจอที่มีขนาดและระยะห่างจากขอบของกล่องเครื่องมือสำหรับนักพัฒนาเว็บซ้อนทับบนองค์ประกอบ .gui-toast-container

นอกจากการวางตำแหน่งตัวเองภายในวิวพอร์ตแล้ว คอนเทนเนอร์โทสต์ยังเป็นคอนเทนเนอร์แบบตารางกริดที่สามารถจัดเรียงและกระจายข้อความโทสต์ได้ รายการต่างๆ จะจัดกึ่งกลางเป็นกลุ่มที่มี justify-content และจัดกึ่งกลางแต่ละรายการด้วย justify-items โยน gap สักเล็กน้อยเพื่อไม่ให้ขนมปังปิ้งสัมผัสได้

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

ภาพหน้าจอที่มีการวางซ้อนตารางกริด CSS บนกลุ่มโทสต์
ซึ่งครั้งนี้ไฮไลต์ช่องว่างและช่องว่างระหว่างองค์ประกอบย่อยของข้อความโทสต์

ข้อความโทสต์ GUI

ขนมปังปิ้งแต่ละชิ้นมี padding บางมุม บางมุมนุ่มนวลกว่าด้วย border-radius และฟังก์ชัน min() เพื่อช่วยปรับขนาดอุปกรณ์เคลื่อนที่และเดสก์ท็อป ขนาดที่ปรับเปลี่ยนตามอุปกรณ์ใน CSS ต่อไปนี้ป้องกันไม่ให้ข้อความโทสต์ขยายใหญ่ขึ้นเกิน 90% ของวิวพอร์ตหรือ 25ch

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

ภาพหน้าจอขององค์ประกอบ .gui-toast รายการเดียวที่แสดงระยะห่างจากขอบและรัศมีของเส้นขอบ

รูปแบบ

เมื่อตั้งค่าเลย์เอาต์และการวางตำแหน่งแล้ว ให้เพิ่ม CSS ที่จะช่วยปรับให้เข้ากับการตั้งค่าและการโต้ตอบของผู้ใช้

ภาชนะบรรจุข้อความ

ข้อความโทสต์จะไม่ใช่แบบอินเทอร์แอกทีฟ การแตะหรือปัดบนข้อความไม่ได้ช่วยทำอะไร แต่จะใช้เหตุการณ์ตัวชี้ในขณะนี้ ป้องกันไม่ให้ข้อความโทสต์ ขโมยคลิกด้วย CSS ต่อไปนี้

.gui-toast-group {
  pointer-events: none;
}

ข้อความโทสต์ GUI

กำหนดธีมที่กำหนดเองให้สว่างหรือมืดโดยใช้คุณสมบัติที่กำหนดเอง, HSL และคำสืบค้นสื่อที่ต้องการ

.gui-toast {
  --_bg-lightness: 90%;

  color: black;
  background: hsl(0 0% var(--_bg-lightness) / 90%);
}

@media (prefers-color-scheme: dark) {
  .gui-toast {
    color: white;
    --_bg-lightness: 20%;
  }
}

แอนิเมชัน

ข้อความโทสต์ใหม่ควรแสดงภาพเคลื่อนไหวขณะที่ข้อความโทสต์เข้ามาในหน้าจอ การปรับลดการเคลื่อนไหวนั้นทำได้โดยตั้งค่า translate เป็น 0 โดยค่าเริ่มต้น แต่จะอัปเดตค่าการเคลื่อนไหวเป็นความยาวในคำค้นหาสื่อค่ากำหนดการเคลื่อนไหว ทุกคนจะได้เห็นภาพเคลื่อนไหวกัน แต่มีเพียงผู้ใช้บางรายเท่านั้นที่เดินทางได้ไกล

นี่คือคีย์เฟรมที่ใช้สำหรับภาพเคลื่อนไหวของข้อความโทสต์ CSS จะควบคุมการเข้า การรอ และการออกจากข้อความโทสต์ ทั้งหมดนี้รวมอยู่ในภาพเคลื่อนไหวเดียว

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

จากนั้นองค์ประกอบ Toast จะตั้งค่าตัวแปรและจัดระเบียบคีย์เฟรม

.gui-toast {
  --_duration: 3s;
  --_travel-distance: 0;

  will-change: transform;
  animation: 
    fade-in .3s ease,
    slide-in .3s ease,
    fade-out .3s ease var(--_duration);
}

@media (prefers-reduced-motion: no-preference) {
  .gui-toast {
    --_travel-distance: 5vh;
  }
}

JavaScript

เมื่อสไตล์และเครื่องมืออ่านหน้าจอพร้อมใช้งาน HTML แล้ว ก็จำเป็นต้องใช้ JavaScript เพื่อรวมการสร้าง การเพิ่ม และการทำลายข้อความโทสต์ตามเหตุการณ์ของผู้ใช้ ประสบการณ์การใช้งานคอมโพเนนต์โทสต์สำหรับนักพัฒนาแอปควรเรียบง่ายเล็กน้อย และเริ่มต้นใช้งานได้ง่าย ดังนี้

import Toast from './toast.js'

Toast('My first toast')

การสร้างกลุ่มข้อความโทสต์และข้อความโทสต์

เมื่อโมดูลข้อความโทสต์โหลดจาก JavaScript ก็จะต้องสร้างคอนเทนเนอร์ข้อความโทสต์และเพิ่มลงในหน้าเว็บ ผมเลือกเพิ่มองค์ประกอบก่อนวันที่ body ซึ่งจะไม่ทำให้เกิดปัญหาการซ้อน z-index รายการเนื่องจากคอนเทนเนอร์อยู่เหนือคอนเทนเนอร์สำหรับองค์ประกอบเนื้อหาทั้งหมด

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

ภาพหน้าจอของกลุ่มข้อความโทสต์ระหว่างแท็กส่วนหัวและแท็กร่างกาย

ระบบจะเรียกใช้ฟังก์ชัน init() ภายในโมดูล โดยซ่อนองค์ประกอบเป็น Toaster ดังนี้

const Toaster = init()

การสร้างองค์ประกอบ HTML ของ Toast นั้นทำได้ด้วยฟังก์ชัน createToast() ฟังก์ชันนี้ต้องมีข้อความบางส่วนสำหรับข้อความโทสต์ สร้างองค์ประกอบ <output> เพิ่มคลาสและแอตทริบิวต์บางรายการ กำหนดข้อความ และส่งโหนดกลับมา

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

การจัดการข้อความโทสต์ 1 รายการหรือหลายรายการ

ขณะนี้ JavaScript จะเพิ่มคอนเทนเนอร์ใส่ข้อความโทสต์ลงในเอกสารและพร้อมเพิ่มข้อความโทสต์ที่สร้างขึ้นแล้ว ฟังก์ชัน addToast() จะจัดการข้อความโทสต์รายการเดียวหรือหลายรายการ ขั้นแรกให้ตรวจสอบจำนวนข้อความโทสต์และการเคลื่อนไหวนั้นว่าเหมาะสมไหม จากนั้นใช้ข้อมูลนี้เพื่อต่อท้ายข้อความโทสต์หรือทำภาพเคลื่อนไหวแบบแฟนพันธุ์แท้เพื่อให้ข้อความโทสต์อื่นๆ ดู "เพิ่มพื้นที่" สำหรับขนมปังปิ้งใหม่

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

เมื่อเพิ่มข้อความโทสต์รายการแรก Toaster.appendChild(toast) จะเพิ่มข้อความโทสต์ลงในหน้าที่ทริกเกอร์ภาพเคลื่อนไหว CSS โดยมีภาพเคลื่อนไหวเข้า รอ 3s และแสดงภาพเคลื่อนไหวออก ระบบจะเรียก flipToast() เมื่อมีข้อความโทสต์อยู่แล้ว โดยใช้เทคนิคที่เรียกว่า FLIP โดย Paul Lewis แนวคิดคือการคำนวณความแตกต่างของตำแหน่งของคอนเทนเนอร์ก่อนและหลังการเพิ่มข้อความโทสต์ใหม่ ลองคิดว่าเหมือนกับการทำเครื่องหมายตอนนี้ว่าเครื่องปิ้งขนมปังอยู่ที่ไหน จะทำอะไรต่อไป แล้วทำให้เคลื่อนไหวจากจุดนั้น ณ จุดที่อยู่

const flipToast = toast => {
  // FIRST
  const first = Toaster.offsetHeight

  // add new child to change container size
  Toaster.appendChild(toast)

  // LAST
  const last = Toaster.offsetHeight

  // INVERT
  const invert = last - first

  // PLAY
  const animation = Toaster.animate([
    { transform: `translateY(${invert}px)` },
    { transform: 'translateY(0)' }
  ], {
    duration: 150,
    easing: 'ease-out',
  })
}

ตารางกริด CSS จะยกเลย์เอาต์ เมื่อมีการเพิ่มข้อความโทสต์ใหม่ ตารางกริดจะจัดวาง ในตอนต้นและเว้นวรรคพร้อมกับข้อความอื่นๆ ในขณะเดียวกัน จะมีการใช้ภาพเคลื่อนไหวในเว็บเพื่อทำให้คอนเทนเนอร์เคลื่อนไหวจากตำแหน่งเดิม

การรวม JavaScript ทั้งหมดไว้ด้วยกัน

เมื่อมีการเรียกใช้ Toast('my first toast') ระบบจะสร้างข้อความโทสต์และเพิ่มลงในหน้าเว็บ (หรือแม้แต่คอนเทนเนอร์อาจเคลื่อนไหวเพื่อรองรับโทสต์ใหม่) ระบบจะแสดงผลสัญญา และดูภาพเคลื่อนไหว CSS ที่สำเร็จ (ภาพเคลื่อนไหวคีย์เฟรม 3 ภาพ) เพื่อหาความละเอียดที่แน่นอน

const Toast = text => {
  let toast = createToast(text)
  addToast(toast)

  return new Promise(async (resolve, reject) => {
    await Promise.allSettled(
      toast.getAnimations().map(animation => 
        animation.finished
      )
    )
    Toaster.removeChild(toast)
    resolve() 
  })
}

ฉันรู้สึกว่าส่วนที่สับสนของโค้ดนี้คือในฟังก์ชัน Promise.allSettled() และการแมป toast.getAnimations() เนื่องจากฉันใช้ภาพเคลื่อนไหวของคีย์เฟรมหลายรายการกับข้อความโทสต์ เพื่อที่จะทราบว่าทั้งหมดทำงานเสร็จแล้ว จึงต้องขอแต่ละรายการจาก JavaScript และคำขอ finished แต่ละรายการที่สังเกตได้จึงจะเสร็จสมบูรณ์ allSettled ช่วยเราทำสิ่งนี้โดยแยกตัวเองว่าสมบูรณ์เมื่อทำตามสัญญาทั้งหมดแล้ว การใช้ await Promise.allSettled() หมายความว่าบรรทัดถัดไปของโค้ดจะนำองค์ประกอบออกได้อย่างมั่นใจ และถือว่าข้อความโทสต์เสร็จสิ้นแล้ว สุดท้าย การเรียก resolve() ช่วยตอบสนองสัญญาระดับสูงอย่าง Toast ทำให้นักพัฒนาซอฟต์แวร์แก้ไขปัญหาหรือทำงานอื่นๆ ได้หลังจากข้อความโทสต์แสดงขึ้นแล้ว

export default Toast

สุดท้าย ระบบจะส่งออกฟังก์ชัน Toast จากโมดูลเพื่อให้สคริปต์อื่นๆ นำเข้าและใช้งาน

การใช้คอมโพเนนต์ Toast

การใช้ข้อความโทสต์หรือประสบการณ์ของนักพัฒนาซอฟต์แวร์ข้อความโทสต์นั้นทำได้โดยการนำเข้าฟังก์ชัน Toast และเรียกด้วยสตริงข้อความ

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

หากนักพัฒนาซอฟต์แวร์ต้องการล้างข้อมูลหรืออะไรก็ตาม หลังจากข้อความโทสต์แสดงขึ้นแล้ว ก็สามารถใช้อะซิงโครนัสและawait

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

บทสรุป

ตอนนี้คุณก็รู้แล้วว่าตัวเองทำยังไง คุณจะทำอะไรบ้าง‽ 🙂

มาลองเปลี่ยนแนวทางของเราและเรียนรู้วิธีทั้งหมดเพื่อสร้างเว็บกันเถอะ สร้างเดโม ลิงก์ทวีตฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง

รีมิกซ์ของชุมชน