ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์ข้อความแจ้งที่ปรับเปลี่ยนได้และเข้าถึงได้
ในโพสต์นี้ ฉันอยากจะแชร์แนวคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์ Toast ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์นี้ใน 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;
}
นอกจากจะวางตำแหน่งภายในวิวพอร์ตแล้ว คอนเทนเนอร์ข้อความป๊อปอัปยังเป็น
คอนเทนเนอร์กริดที่จัดแนวและกระจายข้อความป๊อปอัปได้ด้วย รายการจะอยู่ตรงกลางเป็นกลุ่มด้วย justify-content
และอยู่ตรงกลางแยกกันด้วย justify-items
ใส่gap
เล็กน้อยเพื่อไม่ให้ขนมปังปิ้งสัมผัสกัน
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
ข้อความ Toast ของ 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;
}
รูปแบบ
เมื่อตั้งค่าเลย์เอาต์และการวางตำแหน่งแล้ว ให้เพิ่ม CSS ที่ช่วยในการปรับให้เข้ากับการตั้งค่าและการโต้ตอบของผู้ใช้
คอนเทนเนอร์ข้อความป๊อปอัป
Toast ไม่มีการโต้ตอบ การแตะหรือปัด Toast จะไม่ทําอะไร แต่ ปัจจุบัน Toast ใช้เหตุการณ์ของเคอร์เซอร์ ป้องกันไม่ให้ข้อความป๊อปอัปขโมย การคลิกด้วย CSS ต่อไปนี้
.gui-toast-group {
pointer-events: none;
}
ข้อความ Toast ของ GUI
กำหนดธีมแบบปรับได้เป็นสว่างหรือมืดให้กับข้อความแจ้งด้วยพร็อพเพอร์ตี้ที่กำหนดเอง, HSL และ Media Query ของค่ากำหนด
.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 ของข้อความแจ้งทำได้ด้วยฟังก์ชัน 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 Grid จะจัดการเลย์เอาต์ เมื่อเพิ่มข้อความแจ้งใหม่ กริดจะวางข้อความแจ้งนั้น ไว้ที่จุดเริ่มต้นและเว้นที่ว่างกับข้อความแจ้งอื่นๆ ในขณะเดียวกัน ภาพเคลื่อนไหวบนเว็บจะใช้เพื่อเคลื่อนไหวคอนเทนเนอร์จากตำแหน่งเดิม
การรวม 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()
หมายความว่าโค้ดบรรทัดถัดไป
สามารถนำองค์ประกอบออกได้อย่างมั่นใจและถือว่า Toast ได้สิ้นสุด
วงจรแล้ว สุดท้าย การเรียกใช้ resolve()
จะทำให้คำมั่นสัญญาของ Toast ระดับสูงเป็นจริง เพื่อให้
นักพัฒนาแอปสามารถล้างข้อมูลหรือทำงานอื่นๆ ได้เมื่อ Toast แสดงขึ้น
export default Toast
สุดท้าย ระบบจะส่งออกฟังก์ชัน Toast
จากโมดูลเพื่อให้สคริปต์อื่นๆ นำเข้าและใช้งานได้
การใช้คอมโพเนนต์ข้อความป๊อปอัป
การใช้ Toast หรือประสบการณ์ของนักพัฒนาแอปของ Toast จะทำได้โดยการนำเข้าฟังก์ชัน
Toast
และเรียกใช้ด้วยสตริงข้อความ
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
หากนักพัฒนาแอปต้องการล้างข้อมูลหรือทำอะไรก็ตามหลังจากที่ข้อความป๊อปอัปแสดงขึ้น นักพัฒนาแอปจะใช้ async และ await ได้
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
บทสรุป
ตอนนี้คุณรู้วิธีที่ฉันใช้แล้ว คุณจะทำอย่างไร 🙂
มาลองใช้แนวทางที่หลากหลายและเรียนรู้วิธีต่างๆ ในการสร้างสรรค์บนเว็บกัน สร้างการสาธิต ทวีตลิงก์มาให้ฉัน แล้วฉันจะเพิ่มลิงก์นั้น ลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- @_developit ด้วย HTML/CSS/JS: การสาธิตและโค้ด
- Joost van der Schee กับ HTML/CSS/JS: การสาธิตและโค้ด