前言
在需要对用户进行提示时,有时会遇到这种场景:使用模态框提示太过硬核,使用toast提示又太轻了,这时候可以选择使用页面顶部滑下的消息提示。本文仿照element-ui的实现一个类似$message的方法。
思路梳理
首先我们来看下element-ui中消息提示的效果是怎么样的,找些思路。
从图中我们可以看消息提示是可以同时显示多条的,并且定位看起来都是fixed,居中展示,我们自然可以想到使用数组来存储这些消息的信息,并且根据每一条提示消息的显示隐藏更改每一项的top值,然后就是加一些动画(使用transition)以及细节处理了。
组件编写
新建两个组件MsgBox.vue和Msg.vue,前者负责收集和处理传入的消息数据(如:{type: 'success', message: '提示消息'}),对数组进行一定处理后,再将每一项传给Msg.vue展示。
MsgBox组件
ts部分
我们首先在MsgBox.vue中编写方法处理数组的方法addMsg、resetTop和clear,其中addMsg负责收集消息数据,给每一个msg添加一个负责控制该条消息显示隐藏的属性show;resetTop负责控制消息距顶距离的属性top及各条消息的显示隐藏;clear负责当数组中所有消息都处于隐藏状态时将消息数组清空:
private addMsg(msg: Msg) {
this.msgs.push({...msg, show: true})
this.resetTop()
}
private resetTop(ind1 = -1) {
this.clear()
let ind = 0
const msgs = this.msgs.map((msg: MsgInfo, i: number) => {
if (i === ind1) {
msg.show = false
}
if (msg.show) {
msg.top = 20 + ind * 72
ind++
}
return msg
})
this.msgs = [...msgs]
}
private clear() {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
const allFalse = this.msgs.some((t) => t.show)
if (!allFalse) {
this.msgs = []
}
}, 1000)
}
每次有新消息加入,或者原有消息隐藏时都会触发resetTop方法,用来重新计算各条消息的位置。
template部分
html部分就比较简单了,只是遍历msgs数组,将每一项传给子组件Msg。
<template> <div> <msg-box v-for="(msg,i) of msgs" :msg="msg" :key="i" :ind="i" @resetTop="resetTop" :msgs="msgs"></msg-box> </div> </template>
这里传入数组msgs的原因是在每次调用resetTop更改数组时,子组件监听不到msg发生的变化,只好将msgs传入,直接从msgs中取相关数据,如果哪位大佬看出问题了希望可以指点下。
Msg组件
ts部分
子组件中逻辑较少,主要是在组件挂载时启动一个定时器,在一定时间后通过emit触发父组件中的resetTop方法将组件关闭。 另外还有一些根据参数获取当前消息信息的computed方法。
private get info() {
const msgs = this.msgs as MsgInfo[]
return msgs[this.ind]
}
private get boxClass() {
const type = this.msg.type
return type "htmlcode">
<template>
<transition name="msg" appear>
<div :class="['box-item', boxClass]" v-if="info.show" :style="{top: info.top + 'px'}">
<div class="msg-container">
<i :class="['iconfont', iconClass]"></i>
{{ info.msg }}
</div>
<span @click="close">
<i class="iconfont icon-cc-close"></i>
</span>
</div>
</transition>
</template>
css部分
样式部分主要是借鉴了element-ui的样式,以及使用了animation做了简单的动画效果
.box-item {
height: 16px;
position: fixed;
min-width: 380px;
// element-ui抄来的样式
border-width: 1px;
border-style: solid;
border-color: #EBEEF5;
position: fixed;
left: 50%;
transform: translateX(-50%);
background-color: #edf2fc;
transition: opacity .3s,transform .4s,top .4s;
padding: 15px 15px 15px 20px;
display: flex;
align-items: center;
justify-content: space-between;
&-success{
background-color: #f0f9eb;
border-color: #e1f3d8;
}
&-warning {
background-color: #fdf6ec;
border-color: #faecd8;
}
&-error {
background-color: #fef0f0;
border-color: #fde2e2;
}
}
.msg-container {
display: flex;
align-items: center;
.iconfont {
margin-right: 5px;
}
}
.msg-enter-active {
animation: anim 0.5s;
}
.msg-leave-active {
animation: anim 0.5s reverse;
}
@keyframes anim {
0% {
opacity: 0;
transform: translate(-50%, -200%);
}
100% {
opacity: 1;
transform: translate(-50%, 0);
}
}
到此为止,除css代码外不到150行实现了消息提示组件。
将组件中方法放到Vue原型链上
但是我们该怎么调用呢,参考element-ui中的使用方式this.$message,是把组件的入口方法挂载到Vue的原型链上,并且在此之前应该实例化了该组件,接下来我们就要实例化组件,然后将组件实例挂载到body上,并且将实例上的入口方法加在Vue原型链上。
这里使用到了我们公司一位大佬参考react写的vue中的传送门方法portal,主要思路是将组件挂载新的Vue上,并实例化,然后再将新实例挂载到body下面(这样也防止DOM层级嵌套产生的zIndex无法覆盖等问题),最后将指定方法添加到Vue原型链上。
import Vue, {VueConstructor} from "vue";
interface Param {
cmp: VueConstructor & {instance"htmlcode">
portal({
name: 'msg',
cmp: MsgBox,
method: 'addMsg'
})
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
