我们在平时会用弹窗或者 toast 对用户进行提示,而弹窗是属于强提示类型的(需要用户点击等交互后弹窗才消失),提供的信息量也大,而 toast 提示则属于弱提示类型(无论用户有没有看到,反正我提示了),仅进行小小的提醒。
弹窗类型的交互通常可以多个弹窗叠加,用户关闭一个后,再看下一个;而 toast 提示通常在同一时间只显示一个,那么如果同时多个 toast 需要提示时,怎么办?
ant design 中的做法是同时将多个 toast 放到一个容器里(没有该容器则创建,有则直接使用),从上往下都展示出来【ant design message】,
我们是为了跟新闻客户端 APP 的提示更加贴近,同一时间只弹一个,其他的先缓存起来。
我们接下来把这两种方式都实现一下。
1. 先实现一个简单的 toast
我们先来实现一个简单的 toast,然后再说如何控制多个 toast 的先后提示。
COPYJAVASCRIPT
// 在浏览器上模拟toast提示 export const toast = ( text: string | number, duration: number = 1800, container: HTMLElement = document.body ): Promise<any> => { toastInstance = true; let div: HTMLElement = document.createElement('div'); div.className = 'toast'; div.style.cssText = `position: relative; margin-top: 20px; padding: 12px 20px; border-radius: 5px; background-color: rgba(255, 255, 255, 0.96); color: #111111; font-size: 16px; line-height: 1.5; white-space: nowrap; text-align: center;`; div && (div.innerHTML = text.toString()); container.appendChild(div); return new Promise((resolve) => { setTimeout(() => { div && div.parentNode && div.parentNode.removeChild(div); // 固定时间后消失 toastInstance = false; resolve(); }, duration); }); };
用 div 模拟一个 toast。
2. 展示多个 toast 的方式
2.1 将多个 toast 都展示出来
这里我们的重点是要创建一个 toast 所在的唯一的容器,然后给容器一个定位:
COPYJAVASCRIPT
let toastContainer: any = null; export const getToastContainer = () => { if (toastContainer) { return toastContainer; } toastContainer = document.createElement('div'); toastContainer.style.cssText = `position: fixed; top: 20px; left: 50%; transform: translate3d(-50%, 0, 0); z-index: 9999;`; document.body.appendChild(toastContainer); return toastContainer; };
最后给一个总入口,把单个 toast 放在容器里:
COPYJAVASCRIPT
const message = (text) => { getToastContainer(); toast(text, 1800, toastContainer); };
可以查看 demo 效果:类 ant design 的 toast 提示。
2.2 同时只显示一个 toast 提示
上面的提示方式在 PC 端还可以,但是在移动端的小屏幕上,同时显示多个会显得比较拥挤,因此我们这里同时最多只显示一个 toast 提示,上一个消失之后,再展示下一个 toast 提示。
这里我们要对toast
稍微改造下:
同一时间只能创建一个 toast,因此添加一个 toastInstance 变量进行控制;
修改 toast 的样式;
COPYJAVASCRIPT
+ let toastInstance = false; const toast = (text, duration = 1800, container = document.body) => { + if (toastInstance) { + return; + } + toastInstance = true; let div = document.createElement('div'); div.className = 'toast'; div.style.cssText = `position: fixed; padding: 12px 20px; border-radius: 5px; + top: 20px; + left: 50%; + transform: translate3d(-50%, 0, 0); background-color: rgba(255, 255, 255, 0.96); box-shadow: 0 0 2px #666666; color: #111111; font-size: 16px; line-height: 1.5; white-space: nowrap; text-align: center;`; div && (div.innerHTML = text.toString()); container.appendChild(div); return new Promise((resolve) => { setTimeout(() => { div && div.parentNode && div.parentNode.removeChild(div); // 固定时间后消失 + toastInstance = false; resolve(); }, duration); }); };
然后通过一个方法和缓存来控制 toast 的显示:
COPYJAVASCRIPT
let toastList = []; // 后面的提示先缓存起来 const toastWaterFall = async (text) => { if (toastInstance) { toastList.push(text); // 若当前toast正在进行,则缓存 } else { await toast(text); // 否则展示toast提示 if (toastList.length) { while (toastList.length) { await sleep(300); // 延迟一段时间 await toast(toastList.shift() + ''); } } } };
可以点击链接查看 demo:同时最多只展示一个 toast 提示。
3. 总结
关于 toast 的设计还有很多要考虑的问题,这里我们也是探讨了如何控制多个 toast 的展示问题。