ภาพรวมพื้นฐานของวิธีสร้างส่วนประกอบของขนมปังปิ้งที่ปรับเปลี่ยนได้และเข้าถึงได้ง่าย
ในโพสต์นี้ ฉันอยากแชร์วิธีคิดวิธีสร้างส่วนประกอบของขนมปังปิ้ง ลองใช้งาน การสาธิต
หากชอบวิดีโอ นี่คือโพสต์นี้เวอร์ชัน 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;
}
ข้อความโทสต์ 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 ที่จะช่วยปรับให้เข้ากับการตั้งค่าและการโต้ตอบของผู้ใช้
ภาชนะบรรจุข้อความ
ข้อความโทสต์จะไม่ใช่แบบอินเทอร์แอกทีฟ การแตะหรือปัดบนข้อความไม่ได้ช่วยทำอะไร แต่จะใช้เหตุการณ์ตัวชี้ในขณะนี้ ป้องกันไม่ให้ข้อความโทสต์ ขโมยคลิกด้วย 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')
}
บทสรุป
ตอนนี้คุณก็รู้แล้วว่าตัวเองทำยังไง คุณจะทำอะไรบ้าง‽ 🙂
มาลองเปลี่ยนแนวทางของเราและเรียนรู้วิธีทั้งหมดเพื่อสร้างเว็บกันเถอะ สร้างเดโม ลิงก์ทวีตฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- @_developit ด้วย HTML/CSS/JS: การสาธิตและโค้ด
- Joost van der Schee ที่ใช้ HTML/CSS/JS: การสาธิตและโค้ด