Wenzi

如何控制多个 toast 提示的展示

蚊子前端博客
发布于 2020/07/20 13:59
当需要同时展示多个toast提示,我们应该如何控制呢?

我们在平时会用弹窗或者 toast 对用户进行提示,而弹窗是属于强提示类型的(需要用户点击等交互后弹窗才消失),提供的信息量也大,而 toast 提示则属于弱提示类型(无论用户有没有看到,反正我提示了),仅进行小小的提醒。

弹窗类型的交互通常可以多个弹窗叠加,用户关闭一个后,再看下一个;而 toast 提示通常在同一时间只显示一个,那么如果同时多个 toast 需要提示时,怎么办?

  • ant design 中的做法是同时将多个 toast 放到一个容器里(没有该容器则创建,有则直接使用),从上往下都展示出来【ant design message】,
  • 我们是为了跟新闻客户端 APP 的提示更加贴近,同一时间只弹一个,其他的先缓存起来。

我们接下来把这两种方式都实现一下。

1. 先实现一个简单的 toast #

我们先来实现一个简单的 toast,然后再说如何控制多个 toast 的先后提示。

// 在浏览器上模拟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 所在的唯一的容器,然后给容器一个定位:

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 放在容器里:

const message = (text) => {
    getToastContainer();
    toast(text, 1800, toastContainer);
};

可以查看 demo 效果:类 ant design 的 toast 提示

2.2 同时只显示一个 toast 提示 #

上面的提示方式在 PC 端还可以,但是在移动端的小屏幕上,同时显示多个会显得比较拥挤,因此我们这里同时最多只显示一个 toast 提示,上一个消失之后,再展示下一个 toast 提示。

这里我们要对toast稍微改造下:

  1. 同一时间只能创建一个 toast,因此添加一个 toastInstance 变量进行控制;
  2. 修改 toast 的样式;
+ 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 的显示:

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 的展示问题。

标签:reacttoast
阅读(2412)
Simple Empty
No data