סקירה כללית בסיסית של אופן הבנייה של רכיב טוסט גמיש וגמיש.
בפוסט הזה אני רוצה לשתף אתכם במחשבה על האופן שבו ניתן לפתח רכיב להפעיל טוסט. כדאי לנסות את demo.
אם אתם מעדיפים סרטון, הנה גרסה של YouTube לפוסט:
סקירה כללית
הודעות SMS הן לא אינטראקטיביות, פסיבית ואסינכרוניות בהודעות קצרות למשתמשים. בדרך כלל הם משמשים כדפוס משוב בממשק, שמיועד ליידע את המשתמש על התוצאות של פעולה מסוימת.
אינטראקציות
הודעות קופצות לא דומות להתראות, התראות וגם הודעות כי הם לא אינטראקטיביים, הם לא נועדו להתבטל או להשאר בתוקף. התראות נועדו למידע חשוב יותר, להעברת הודעות סינכרוניות מחייבת אינטראקציה או הודעות ברמת המערכת (בניגוד להצגת הודעות ברמת הדף). הודעות טקסט הן פסיביות יותר מאסטרטגיות אחרות של הודעה.
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>
מאגר הודעות טוסט
אפשר להציג יותר מהודעה קופצת אחת בכל פעם. כדי לתזמר מספר טוסטים, נעשה שימוש במכל. הקונטיינר הזה מטפל גם במיקום מצטלבות על המסך.
<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%;
}
}
Animation
הודעה על טוסט חדש אמור להציג את עצמו עם אנימציה כשהוא נכנס למסך.
ההתאמה של תנועה מופחתת מתבצעת על ידי הגדרת הערכים של 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)) }
}
לאחר מכן, הרכיב מסוג 'טוסט' מגדיר את המשתנים ומתזמר את תמונות המפתח.
.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
}
ניהול הודעה אחת או יותר
עכשיו 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 מאת פול
לואיס. הרעיון הוא לחשב את ההפרש
במיקומים של המכל, לפני ואחרי שהטוסט החדש נוסף.
אפשר לחשוב על זה כמו לסמן את המיקום של הטוסטר עכשיו, את המיקום שלו, ואז
ליצור אנימציה מהמקום שבו הוא נמצא.
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 (שלוש האנימציות של תמונות המפתח) לפתרון המובטח.
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()
, תוכלו לקיים את ההבטחה הגבוהה של 'טוסט'.
מפתחים יכולים לפנות מקום או לבצע עבודה אחרת לאחר שמוצג ההודעה הקולית.
export default Toast
בשלב האחרון, הפונקציה Toast
מיוצאת מהמודול אל סקריפטים אחרים
ייבוא ושימוש.
שימוש ברכיב Toast
כדי להשתמש בטוסט, או בחוויית הפיתוח של ההודעה הנגדית, מתבצע על ידי ייבוא
הפונקציה Toast
והפעלתה את הפונקציה הזו באמצעות מחרוזת הודעה.
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
אם המפתח ירצה לבצע פינוי מקום או כל דבר אחר, אחרי שהחלון הקופץ הם יכולים להשתמש באסינכרוני להמתין.
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
סיכום
עכשיו, אחרי שהסברתי איך עשיתי את זה, איך היית? 🙂
בואו לגוון את הגישות שלנו ונלמד את כל הדרכים לבניית אתרים באינטרנט. אפשר ליצור הדגמה, לשלוח לי קישורים של שלחו לי ציוץ ואני אוסיף אותם לקטע 'רמיקסים' של הקהילה שבהמשך.
רמיקסים קהילתיים
- @_developit בעזרת HTML/CSS/JS: demo & קוד
- ג'וסט ואן דר סכי עם HTML/CSS/JS: demo & קוד