夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 响应式

  • 组件

  • 过渡&动画

  • 可复用性

  • 工具

  • 规模化

  • 路由(核心)

  • 组合式

  • Vuex

  • Pinia

  • 其他

    • 实现动态toast
      • toast 方法
      • toast 组件
    • 手动渲染虚拟dom
    • 全局事件总线($bus)
    • 全局方法和属性 createAPP
    • Vue3-TS 中使用 Express
    • watch监听Props属性值失败原因
    • main.js推荐配置
    • 脚手架前期基础配置
    • 跨域之config.js配置
    • Vue中的防抖函数封装和使用
    • vue的数据更新
    • 操作本地缓存
    • api使用流程
    • ajax封装及api的获取和配置
    • vue解析n字符
    • TS获取ref绑定元素
    • 解决vue3中Props无法引入外部类型
    • vue2升vue3注意事项
  • Vue3 源码学习记录

  • 《Vue》笔记
  • 其他
夜猫子
2024-11-01
目录

实现动态toast

# toast 方法

import { createVNode, nextTick, render } from 'vue'
import ToastComponent from './index.vue'

let toastInstance = null
let container = null
let autoCloseTimer = null

export function showToast(options = {}) {
  // 清除之前的自动关闭定时器
  if (autoCloseTimer) {
    clearTimeout(autoCloseTimer)
    autoCloseTimer = null
  }

  // 销毁已有Toast
  if (toastInstance) {
    // 直接调用关闭方法清理
    closeToast()
  }

  const {
    text = '',
    duration = 2000,
    overColor = 'transparent',
    maskColor = 'rgba(0, 0, 0, 0.7)'
  } = options

  // 创建容器
  container = document.createElement('div')
  document.body.appendChild(container)

  // 创建虚拟节点 - 使用普通对象而非 ref
  toastInstance = createVNode(ToastComponent, {
    text,
    overColor,
    maskColor,
    showWrap: true,
    showContent: true
  })

  // 渲染到容器
  render(toastInstance, container)

  // // 设置自动关闭定时器
  if (duration > 0) {
    autoCloseTimer = setTimeout(closeToast, duration)
  }
}

// 统一的关闭方法
function closeToast() {
  if (!toastInstance || !container) return

  // 触发组件淡出动画
  toastInstance.component.props.showContent = false

  // 延迟执行清理操作
  nextTick(() => {
    // 隐藏整个组件
    toastInstance.component.props.showWrap = false

    // 等待动画完成后再移除DOM
    setTimeout(() => {
      if (container) {
        // 卸载组件
        render(null, container)
        document.body.removeChild(container)

        // 重置变量
        container = null
        toastInstance = null
      }
    }, 250) // 与CSS动画时间一致
  })
}

export const toast = {
  show: showToast,
  clear: closeToast
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# toast 组件

<template>
  <div
    class="m-toast"
    :style="{ backgroundColor: overColor }"
    :class="showContent ? 'fadein' : 'fadeout'"
    v-if="showWrap"
  >
    <div class="mask" :style="{ backgroundColor: maskColor }">
      <div v-if="text" class="toast-text">{{ text }}</div>
    </div>
  </div>
</template>

<script setup>
// 只需定义 props,不需要额外逻辑
defineProps({
  text: String,
  maskColor: String,
  overColor: String,
  showWrap: Boolean,
  showContent: Boolean
})
</script>

<style lang="less" scoped>
.m-toast {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw; /* 修复:改为100vw确保全宽 */
  min-height: 100vh;
  min-height: 100dvh;
  z-index: 9999;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  touch-action: none;

  .mask {
    position: fixed;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    max-width: 80vw;
    gap: 10px;
    z-index: 100;
    left: 50%;
    top: 50%;
    font-size: 28px;
    padding: 20px;
    border-radius: 15px;
    transform: translate(-50%, -50%);
    color: #fff;

    .toast-text {
      text-align: center;
      word-break: break-word;
    }
  }
}

.fadein {
  animation: animate_in 0.25s forwards;
}
.fadeout {
  animation: animate_out 0.25s forwards;
}
@keyframes animate_in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
@keyframes animate_out {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
编辑 (opens new window)
上次更新: 2025/7/23 18:02:16
编写Pinia持久化插件
手动渲染虚拟dom

← 编写Pinia持久化插件 手动渲染虚拟dom→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式