推荐一个小而美的 Toast 插件 (一键复制使用)
一. 前言
推荐一个小而美的 Toast 插件。考虑到该演示的简易性质,我们决定不引入任何第三方用户界面(UI
)库,这带来了如何实现非侵入式的用户通知(如 toast
提示)。
通常情况下,对于简单的警告或通知,开发者可能会依赖浏览器自带的 alert
函数。然而,alert
弹窗会强制暂停用户的交互流程,提供较差的用户体验;并且它不适合处理多个同时出现的通知,也缺乏对不同通知类型的视觉区分能力。
尽管网络上有许多现成的解决方案,例如基于 jQuery
时代的独立 toast
插件和 Bootstrap
库中的实现,但这些选项要么过时,要么过于庞大,不符合我们对轻量化和简洁性的追求。因此,我决定开发一个自定义的、轻巧且易于使用的 toast
组件,以满足我们的特定需求。
新组件不仅将提升用户体验,确保通知不会打断用户的操作流程,还将支持多种通知类型,比如成功、警告和错误信息等,从而为用户提供更加直观的信息反馈。通过这个过程,我们希望不仅能解决当前的问题,还能为未来类似的需求创建一个可复用的解决方案。
二. 设计理念
秉承 autofit.js
的设计理念,我继续选择使用纯 JavaScript
实现新项目,以确保代码的极致精简和广泛的兼容性。在此基础上,我还特别为该项目编写了 TypeScript 声明文件(.d.ts
),从而全面支持 TypeScript 开发环境。
由此,autolog.js
应运而生:一个不仅继承了前作的高效与兼容性优势,还进一步增强了对现代开发流程支持的全新库。
autolog.js
由两部分组成:一段极简的 JavaScript
和一段精炼的 CSS
。经过 gzip
压缩后,整个包的体积仅为 1.40 KB,确保了快速加载和高效的性能表现。
文末可复制源码
1. 源码 js 部分(v2.0.0)
const cssStr = `#autolog{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;pointer-events:none;width:100vw;height:100vh;position:fixed;left:0;top:0;z-index:9999999;cursor:pointer;transition:0.2s}#autolog span{pointer-events:auto;width:max-content;animation:fadein 0.4s;animation-delay:0s;border-radius:6px;padding:10px 20px;box-shadow:0 0 10px 6px rgba(0,0,0,0.1);margin:4px;transition:0.2s;z-index:9999999;font-size:14px;display:flex;align-items:center;justify-content:center;gap:4px;height:max-content}#autolog span.hide{opacity:0;pointer-events:none;transform:translateY(-10px);height:0;padding:0;margin:0}.autolog-warn{background-color:#fffaec;color:#e29505}.autolog-error{background-color:#fde7e7;color:#d93025}.autolog-info{background-color:#e6f7ff;color:#0e6eb8}.autolog-success{background-color:#e9f7e7;color:#1a9e2c}.autolog-{background-color:#fafafa;color:#333}@keyframes fadein{0%{opacity:0;transform:translateY(-10px)}100%{opacity:1;transform:translateY(0)}}`
const svgIcons = {
warn: `<svg t="1713405237257" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2387" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M934.4 770.133333L605.866667 181.333333C586.666667 147.2 550.4 128 512 128c-38.4 0-74.666667 21.333333-93.866667 53.333333L89.6 770.133333c-19.2 34.133333-19.2 76.8 0 110.933334S145.066667 938.666667 183.466667 938.666667h657.066666c38.4 0 74.666667-21.333333 93.866667-57.6 19.2-34.133333 19.2-76.8 0-110.933334z m-55.466667 81.066667c-8.533333 14.933333-23.466667 23.466667-38.4 23.466667H183.466667c-14.933333 0-29.866667-8.533333-38.4-23.466667-8.533333-14.933333-8.533333-34.133333 0-49.066667L473.6 213.333333c8.533333-12.8 23.466667-21.333333 38.4-21.333333s29.866667 8.533333 38.4 21.333333l328.533333 588.8c8.533333 14.933333 8.533333 32 0 49.066667z" fill="#e29505" p-id="2388"></path><path d="M512 746.666667m-42.666667 0a42.666667 42.666667 0 1 0 85.333334 0 42.666667 42.666667 0 1 0-85.333334 0Z" fill="#e29505" p-id="2389"></path><path d="M512 629.333333c17.066667 0 32-14.933333 32-32v-192c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v192c0 17.066667 14.933333 32 32 32z" fill="#e29505" p-id="2390"></path></svg>`,
error: `<svg t="1713405212725" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1744" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" fill="#d93025" p-id="1745"></path><path d="M657.066667 360.533333c-12.8-12.8-32-12.8-44.8 0l-102.4 102.4-102.4-102.4c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l102.4 102.4-102.4 102.4c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466666 8.533334s17.066667-2.133333 23.466667-8.533334l102.4-102.4 102.4 102.4c6.4 6.4 14.933333 8.533333 23.466667 8.533334s17.066667-2.133333 23.466666-8.533334c12.8-12.8 12.8-32 0-44.8l-106.666666-100.266666 102.4-102.4c12.8-12.8 12.8-34.133333 0-46.933334z" fill="#d93025" p-id="1746"></path></svg>`,
info: `<svg t="1713405208589" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1582" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M853.333333 138.666667H170.666667c-40.533333 0-74.666667 34.133333-74.666667 74.666666v512c0 40.533333 34.133333 74.666667 74.666667 74.666667h151.466666V917.333333c0 12.8 8.533333 25.6 19.2 29.866667 4.266667 2.133333 8.533333 2.133333 12.8 2.133333 8.533333 0 17.066667-4.266667 23.466667-10.666666l136.533333-138.666667H853.333333c40.533333 0 74.666667-34.133333 74.666667-74.666667V213.333333c0-40.533333-34.133333-74.666667-74.666667-74.666666z m10.666667 586.666666c0 6.4-4.266667 10.666667-10.666667 10.666667H501.333333c-8.533333 0-17.066667 4.266667-23.466666 10.666667l-89.6 93.866666V768c0-17.066667-14.933333-32-32-32H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V213.333333c0-6.4 4.266667-10.666667 10.666667-10.666666h682.666666c6.4 0 10.666667 4.266667 10.666667 10.666666v512z" fill="#0e6eb8" p-id="1583"></path><path d="M512 490.666667H298.666667c-17.066667 0-32 14.933333-32 32S281.6 554.666667 298.666667 554.666667h213.333333c17.066667 0 32-14.933333 32-32S529.066667 490.666667 512 490.666667zM672 341.333333H298.666667c-17.066667 0-32 14.933333-32 32S281.6 405.333333 298.666667 405.333333h373.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z" fill="#0e6eb8" p-id="1584"></path></svg>`,
success: `<svg t="1713405224326" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2225" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" fill="#1a9e2c" p-id="2226"></path><path d="M701.866667 381.866667L448 637.866667 322.133333 512c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l149.333334 149.333333c6.4 6.4 14.933333 8.533333 23.466666 8.533334s17.066667-2.133333 23.466667-8.533334l277.333333-277.333333c12.8-12.8 12.8-32 0-44.8-14.933333-12.8-36.266667-12.8-49.066666-2.133333z" fill="#1a9e2c" p-id="2227"></path></svg>`
}
const autolog = {
log(text, type = '', time = 2500) {
if (typeof type === 'number') {
time = type
type = ''
}
let mainEl = getMainElement()
let el = document.createElement('span')
el.className = `autolog-${type}`
el.innerHTML = svgIcons[type] + text
mainEl.appendChild(el)
setTimeout(() => {
el.classList.add('hide')
}, time - 500)
setTimeout(() => {
mainEl.removeChild(el)
el = null
}, time)
}
}
function getMainElement() {
let mainEl = document.querySelector('#autolog')
if (!mainEl) {
mainEl = document.createElement('div')
mainEl.id = 'autolog'
document.body.appendChild(mainEl)
let style = document.createElement('style')
style.innerHTML = cssStr
document.head.insertBefore(style, document.head.firstChild)
}
return mainEl
}
exportdefault autolog
以上就是 autolog.js
的完整 JavaScript 代码。如您所见,它仅导出了一个名为 log
的方法,调用此方法时只需要提供一个必填参数。
让我来解释一下这段代码的工作流程:
-
可选参数处理:由于
log
方法支持两个可选参数(time
和type
),代码首先会检查用户传递了哪些可选参数。这使得在使用时可以根据需要单独传递time
或者type
参数,提供了灵活性。 -
主容器管理:通过
getMainElement
方法获取或创建用于容纳所有日志条目的主容器。如果主容器不存在,则自动创建。这种方式省去了用户手动初始化的步骤,避免了传统插件中常见的init
方法需求,简化了集成过程。 -
日志元素生成:为每次的日志记录创建一个新的
span
元素,用于展示具体的日志内容。这样做保证了每个日志项都是独立的 DOM 节点,便于管理和样式控制。 -
优雅退出机制:采用两个定时器来实现日志条目的优雅退出。第一个定时器会在清除元素前 0.5 秒为其添加退场动画,确保视觉上的平滑过渡;第二个定时器负责彻底移除该元素,并将引用置为
null
,以此断开与 DOM 的连接,防止游离 DOM 节点的产生和潜在的内存泄漏问题。
尤其值得注意的是,CSS
部分在 autolog.js
中扮演着至关重要的角色,它不仅定义了日志条目的外观样式,还承载了关键的显示逻辑,包括但不限于动画效果等。
2. css 部分
实力宠粉,主打一个听劝!
昨天评论区掘友建议 all in js
,今天一早我已经改完了,并发了 2.0.0
版本,把 css
放到了 js
里,图标都换成了 svg
也放到了 js
里。
由此可见,你也可以重写这些 css
,为他们添加不同的颜色。
没有什么巧妙的设计,也没有什么精致的构思,朴实无华的一百多行代码而已,希望这些代码可以帮到各位。
三. 安装和使用
使用也非常简单,只需引入 ~两个文件~ 一个文件。
1. 安装
npm i autolog.js
2. 使用
import autolog from 'autolog.js'
autolog.log('Hi,this is a normal tip')
autolog.log('Hello World', 'success', 2500)
// 其中 "success" 和 2500 都是可选项
四. 完整效果演示
五. 示例代码,可直接复制使用
代码仅供演示,实际项目中按照规范引入使用!
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>autolog.js Demo</title>
<link rel="shortcut icon" href="https://raw.githubusercontent.com/Larryzhu-dev/autolog.js/main/autolog.svg" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
text-decoration: none;
}
body {
background: #1f1f1f;
overflow: hidden;
}
button {
background: none;
border: none;
outline: none;
background: #eee;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
transition: 0.2s;
}
input {
background: #eee;
padding: 6px 12px;
border-radius: 6px;
border: none;
outline: none;
width: 120px;
}
button:hover {
opacity: 0.9;
}
.autologBox,
.autofitBox {
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding-top: 200px;
}
.pluginInfos {
color: #999;
}
.customText {
color: #fff;
font-size: 20px;
}
.autologBox .logo img {
width: 300px;
}
.buttons {
display: flex;
justify-content: center;
gap: 12px;
}
.codeBlock {
background: #181818;
color: #cccccc;
padding: 12px;
border: 1px solid #333;
border-radius: 12px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: 12px;
}
.logo {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.logo img {
width: 160px;
}
</style>
<script src="https://unpkg.com/autolog.js@2.2.1/autolog.min.js"></script>
</head>
<body>
<div class="autologBox">
<div class="logo">
<a href="https://github.com/LarryZhu-dev/autolog.js" target="_blank">
<img src="https://raw.githubusercontent.com/Larryzhu-dev/autolog.js/main/autolog.svg" alt="indexPages" />
</a>
</div>
<div class="pluginInfos">
<span>autolog.js 一款轻量化小弹窗插件 1.40kb(gzip)</span>
</div>
<div class="customText">
<input type="text" placeholder="请输入文本" id="text" />
<input type="text" placeholder="请输入时长" id="time" />
</div>
<div class="buttons">
<button class="success" onclick="showLog()">素框</button>
<button class="success" onclick="showLog('success')">成功</button>
<button class="error" onclick="showLog('error')">错误</button>
<button class="warn" onclick="showLog('warn')">警告</button>
<button class="info" onclick="showLog('info')">公告</button>
</div>
<div class="install">
<div class="codeBlock">
<span>npm install autolog.js</span>
<button onclick="copy()">copy</button>
</div>
</div>
</div>
<script>
autolog.log('hi,autolog.js!', 'info')
function showLog(logType) {
let text = document.getElementById('text').value
let time = document.getElementById('time').value
autolog.log(text || 'Hello World', logType || '', Number(time) || 2500)
}
function copy() {
const code = document.querySelector('.codeBlock span').innerText
const input = document.createElement('input')
input.value = code
document.body.appendChild(input)
input.select()
document.execCommand('copy')
document.body.removeChild(input)
autolog.log('复制成功', 'success', 2000)
}
</script>
</body>
</html>