<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Lotsa Notifications</title> <style> * { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --hue: 223; --bg: hsl(var(--hue), 10%, 90%); --fg: hsl(var(--hue), 10%, 10%); --transDur: 0.15s; font-size: calc(16px + (24 - 16) * (100vw - 320px) / (1280 - 320)); } body, button { color: var(--fg); font: 1em/1.5 "DM Sans", "Helvetica Neue", Helvetica, sans-serif; } body { background-color: var(--bg); height: 100vh; display: grid; place-items: center; transition: background-color var(--transDur); } .notification { padding-bottom: 0.75em; position: fixed; top: 1.5em; right: 1.5em; width: 18.75em; max-width: calc(100% - 3em); transition: transform 0.15s ease-out; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .notification__box, .notification__content, .notification__btns { display: flex; } .notification__box, .notification__content { align-items: center; } .notification__box { animation: flyIn 0.3s ease-out; background-color: hsl(0, 0%, 100%); border-radius: 0.75em; box-shadow: 0 0.5em 1em hsla(var(--hue), 10%, 10%, 0.1); height: 4em; transition: background-color var(--transDur), color var(--transDur); } .notification--out .notification__box { animation: flyOut 0.3s ease-out forwards; } .notification__content { padding: 0.375em 1em; width: 100%; height: 100%; } .notification__icon { flex-shrink: 0; margin-right: 0.75em; width: 2em; height: 2em; } .notification__icon-svg { width: 100%; height: auto; } .notification__text { line-height: 1.333; } .notification__text-title { font-size: 0.75em; font-weight: bold; } .notification__text-subtitle { font-size: 0.6em; opacity: 0.75; } .notification__btns { box-shadow: -1px 0 0 hsla(var(--hue), 10%, 10%, 0.15); flex-direction: column; flex-shrink: 0; min-width: 4em; height: 100%; transition: box-shadow var(--transDur); } .notification__btn { background-color: transparent; box-shadow: 0 0 0 hsla(var(--hue), 10%, 10%, 0.5) inset; font-size: 0.6em; line-height: 1; font-weight: 500; height: 100%; padding: 0 0.5rem; transition: background-color var(--transDur), color var(--transDur); -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent; } .notification__btn-text { display: inline-block; pointer-events: none; } .notification__btn:first-of-type { border-radius: 0 0.75rem 0 0; } .notification__btn:last-of-type { border-radius: 0 0 0.75rem 0; } .notification__btn:only-child { border-radius: 0 0.75rem 0.75rem 0; } .notification__btn + .notification__btn { box-shadow: 0 -1px 0 hsla(var(--hue), 10%, 10%, 0.15); font-weight: 400; } .notification__btn:active, .notification__btn:focus { background-color: hsl(var(--hue), 10%, 95%); } .notification__btn:focus { outline: transparent; } @supports selector(:focus-visible) { .notification__btn:focus { background-color: transparent; } .notification__btn:focus-visible, .notification__btn:active { background-color: hsl(var(--hue), 10%, 95%); } } /* Dark theme */ @media (prefers-color-scheme: dark) { :root { --bg: hsl(var(--hue), 10%, 10%); --fg: hsl(var(--hue), 10%, 90%); } .notification__box { background-color: hsl(var(--hue), 10%, 30%); } .notification__btns { box-shadow: -1px 0 0 hsla(var(--hue), 10%, 90%, 0.15); } .notification__btn + .notification__btn { box-shadow: 0 -1px 0 hsla(var(--hue), 10%, 90%, 0.15); } .notification__btn:active, .notification__btn:focus { background-color: hsl(var(--hue), 10%, 35%); } @supports selector(:focus-visible) { .notification__btn:focus { background-color: transparent; } .notification__btn:focus-visible, .notification__btn:active { background-color: hsl(var(--hue), 10%, 35%); } } } /* Animations */ @keyframes flyIn { from { transform: translateX(calc(100% + 1.5em)); } to { transform: translateX(0); } } @keyframes flyOut { from { transform: translateX(0); } to { transform: translateX(calc(100% + 1.5em)); } } </style> </head> <body> <svg display="none"> <symbol id="clock" viewBox="0 0 32 32"> <circle r="15" cx="16" cy="16" fill="none" stroke="currentColor" stroke-width="2" /> <polyline points="16,7 16,16 23,16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </symbol> <symbol id="error" viewBox="0 0 32 32"> <circle r="15" cx="16" cy="16" fill="none" stroke="hsl(13,90%,55%)" stroke-width="2" /> <line x1="10" y1="10" x2="22" y2="22" stroke="hsl(13,90%,55%)" stroke-width="2" stroke-linecap="round" /> <line x1="22" y1="10" x2="10" y2="22" stroke="hsl(13,90%,55%)" stroke-width="2" stroke-linecap="round" /> </symbol> <symbol id="message" viewBox="0 0 32 32"> <polygon points="1,6 31,6 31,26 1,26" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> <polyline points="1,6 16,18 31,6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </symbol> <symbol id="success" viewBox="0 0 32 32"> <circle r="15" cx="16" cy="16" fill="none" stroke="hsl(93,90%,40%)" stroke-width="2" /> <polyline points="9,18 13,22 23,12" fill="none" stroke="hsl(93,90%,40%)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </symbol> <symbol id="up" viewBox="0 0 32 32"> <circle r="15" cx="16" cy="16" fill="none" stroke="currentColor" stroke-width="2" /> <polyline points="11,15 16,10 21,15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> <line x1="16" y1="10" x2="16" y2="22" stroke="currentColor" stroke-width="2" stroke-linecap="round" /> </symbol> <symbol id="warning" viewBox="0 0 32 32"> <polygon points="16,1 31,31 1,31" fill="none" stroke="hsl(33,90%,55%)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> <line x1="16" y1="12" x2="16" y2="20" stroke="hsl(33,90%,55%)" stroke-width="2" stroke-linecap="round" /> <line x1="16" y1="25" x2="16" y2="25" stroke="hsl(33,90%,55%)" stroke-width="3" stroke-linecap="round" /> </symbol> </svg> </body> <script> window.addEventListener("DOMContentLoaded", () => { const nc = new NotificationCenter(); }); class NotificationCenter { constructor() { this.items = []; this.itemsToKill = []; this.messages = NotificationMessages(); this.killTimeout = null; this.spawnNotes(3); } spawnNote() { const id = this.random(0, 2 ** 32, true).toString(16); const draw = this.random(0, this.messages.length - 1, true); const message = this.messages[draw]; const note = new Notification({ id: <code>note-${id}</code>, icon: message.icon, title: message.title, subtitle: message.subtitle, actions: message.actions, }); const transY = 100 * this.items.length; note.el.style.transform = <code>translateY(${transY}%)</code>; note.el.addEventListener("click", this.killNote.bind(this, note.id)); this.items.push(note); } spawnNotes(amount) { let count = typeof amount === "number" ? amount : this.random(1, 5, true); while (count--) this.spawnNote(); } killNote(id, e) { const note = this.items.find((item) => item.id === id); const tar = e.target; if (note && tar.getAttribute("data-dismiss") === id) { note.el.classList.add("notification--out"); this.itemsToKill.push(note); clearTimeout(this.killTimeout); this.killTimeout = setTimeout(() => { this.itemsToKill.forEach((itemToKill) => { document.body.removeChild(itemToKill.el); const left = this.items.filter( (item) => item.id !== itemToKill.id ); this.items = [...left]; }); this.itemsToKill = []; if (!this.items.length) this.spawnNotes(); else this.spawnNotes(this.random(0, 1, true)); this.shiftNotes(); }, note.killTime); } } shiftNotes() { this.items.forEach((item, i) => { const transY = 100 * i; item.el.style.transform = <code>translateY(${transY}%)</code>; }); } random(min, max, round = false) { const percent = crypto.getRandomValues(new Uint32Array(1))[0] / 2 ** 32; const relativeValue = (max - min) * percent; return ( min + (round === true ? Math.round(relativeValue) : +relativeValue.toFixed(2)) ); } } class Notification { constructor(args) { this.args = args; this.el = null; this.id = null; this.killTime = 300; this.init(args); } init(args) { const { id, icon, title, subtitle, actions } = args; const block = "notification"; const parent = document.body; const xmlnsSVG = "http://www.w3.org/2000/svg"; const xmlnsUse = "http://www.w3.org/1999/xlink"; const note = this.newEl("div"); note.id = id; note.className = block; parent.insertBefore(note, parent.lastElementChild); const box = this.newEl("div"); box.className = <code>${block}__box</code>; note.appendChild(box); const content = this.newEl("div"); content.className = <code>${block}__content</code>; box.appendChild(content); const _icon = this.newEl("div"); _icon.className = <code>${block}__icon</code>; content.appendChild(_icon); const iconSVG = this.newEl("svg", xmlnsSVG); iconSVG.setAttribute("class", <code>${block}__icon-svg</code>); iconSVG.setAttribute("role", "img"); iconSVG.setAttribute("aria-label", icon); iconSVG.setAttribute("width", "32px"); iconSVG.setAttribute("height", "32px"); _icon.appendChild(iconSVG); const iconUse = this.newEl("use", xmlnsSVG); iconUse.setAttributeNS(xmlnsUse, "href", <code>#${icon}</code>); iconSVG.appendChild(iconUse); const text = this.newEl("div"); text.className = <code>${block}__text</code>; content.appendChild(text); const _title = this.newEl("div"); _title.className = <code>${block}__text-title</code>; _title.textContent = title; text.appendChild(_title); if (subtitle) { const _subtitle = this.newEl("div"); _subtitle.className = <code>${block}__text-subtitle</code>; _subtitle.textContent = subtitle; text.appendChild(_subtitle); } const btns = this.newEl("div"); btns.className = <code>${block}__btns</code>; box.appendChild(btns); actions.forEach((action) => { const btn = this.newEl("button"); btn.className = <code>${block}__btn</code>; btn.type = "button"; btn.setAttribute("data-dismiss", id); const btnText = this.newEl("span"); btnText.className = <code>${block}__btn-text</code>; btnText.textContent = action; btn.appendChild(btnText); btns.appendChild(btn); }); this.el = note; this.id = note.id; } newEl(elName, NSValue) { if (NSValue) return document.createElementNS(NSValue, elName); else return document.createElement(elName); } } function NotificationMessages() { return [ { icon: "error", title: "Oh No", subtitle: "Something really bad happened.", actions: ["Close"], }, { icon: "error", title: "Error", subtitle: "The operation completed successfully.", actions: ["OK"], }, { icon: "error", title: "Critical Error", subtitle: "An error has occurred while trying to display an error notification.", actions: ["OK"], }, { icon: "warning", title: "Reminder", subtitle: "You will receive more notifications.", actions: ["Close"], }, { icon: "warning", title: "Failed to Save Changes", actions: ["Retry", "Cancel"], }, { icon: "warning", title: "Download Failed", actions: ["Retry", "Cancel"], }, { icon: "warning", title: "Cannot Send Mail", subtitle: "The message was rejected by the server because it is too large.", actions: ["OK"], }, { icon: "warning", title: "Disk Not Ejected Properly", subtitle: "Eject “CopyThisFloppy” before disconnecting or turning it off.", actions: ["Close"], }, { icon: "warning", title: "Notifications", subtitle: "Notifications may include alerts, sounds, and icon badges.", actions: ["Don’t Allow", "Allow"], }, { icon: "success", title: "Changes Saved", actions: ["OK"], }, { icon: "success", title: "Download Complete", actions: ["OK"], }, { icon: "success", title: "Yippee", subtitle: "Nothing bad happened.", actions: ["OK"], }, { icon: "message", title: "Mail Password Required", subtitle: "Enter your password for user@domain.com.", actions: ["Close", "Continue"], }, { icon: "message", title: "Mail", subtitle: "You have 10 new messages.", actions: ["Read", "Dismiss"], }, { icon: "clock", title: "Coffee Break", subtitle: "In 5 minutes", actions: ["Close", "Snooze"], }, { icon: "clock", title: "Muffin Time", subtitle: "12:30 PM", actions: ["Close", "Snooze"], }, { icon: "clock", title: "Hammer Time", subtitle: "In 2 minutes", actions: ["Close", "Snooze"], }, { icon: "up", title: "Upgrade Available", subtitle: "Enjoy the latest technologies and refinements to your favorite apps.", actions: ["Install", "Details"], }, { icon: "up", title: "Upgrade Waiting", subtitle: "Get it now, or it won’t be long until you are far behind everyone else.", actions: ["Install", "Details"], }, { icon: "up", title: "Upgrade Now", subtitle: "The current version will soon be obsolete. What are you waiting for?", actions: ["Install", "Details"], }, ]; } </script> </html> 提示:你可以先修改部分代码再运行。 转载请注明:有爱前端 » 网站提示 喜欢 (0)or分享 (0)