Element-uiに倣って簡単な$message方法を実現します。


前言
ユーザーに対して提示が必要な場合、このような場面に遭遇することがあります。モダリティボックスを使ってあまりにもハード核を提示し、toastヒントを使うのはまた軽すぎます。この時はページの上部を使ってスライドしたメッセージメッセージを選択して提示します。本論文はelement-uiの実装を模倣したものである。
考えが整理される
まず、element-uiの中のメッセージの効果はどうですか?考えを探してみます。

図から見れば、メッセージヒントは複数のストリップを同時に表示することができ、位置付けはすべてfixedであるように見えます。これらのメッセージを格納するために配列を使用することが考えられます。そして、各プロンプトメッセージの表示に基づいて、各項目のtop値を変更し、いくつかのアニメーション(transionを使用して)を追加し、詳細に処理しました。
コンポーネント作成
新しい2つのコンポーネントのMsgBox.vueとMsg.vueは、前者が着信メッセージデータの収集と処理を担当しています。
MsgBoxコンポーネント
tsの部分
まずMsgBox.vueで方法を作成し、配列を処理する方法addMsg、reetTop、clear、addMsgはメッセージデータの収集を担当しています。各msgには隠し属性ショーの表示を制御する責任があります。restettopは、トップからのメッセージのプロパティ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)
}

新しいメッセージが追加されるたびに、または元のメッセージが隠されているときに、reetTop方法がトリガされ、各メッセージの位置を再計算するために使用される。
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の原因は、reetTopを呼び出して配列を変更するたびに、サブアセンブリがmsgの変化を監視できなくなり、msgsを直接msgsに導入し、msgsから関連データを取ります。
Msgコンポーネント
tsの部分
サブアセンブリには論理が少なく、主にコンポーネントマウント時にタイマーを起動し、一定時間後に親コンポーネント中のreetTopをemitでトリガする方法でコンポーネントをクローズします。また、パラメータによって現在のメッセージを取得するためのcomputed方法があります。

private get info() {
 const msgs = this.msgs as MsgInfo[]
 return msgs[this.ind]
}

private get boxClass() {
 const type = this.msg.type
 return type ? `box-item-${type}` : ''
}

@Emit('resetTop')
private close() {
  return this.ind
}

private mounted() {
 if (this.msg.delay !== 0) {
 const delay = parseInt(this.msg.delay) || 3000
 setTimeout(() => {
  this.close()
 }, delay)
 }
}

template部分
ビューの部分も比較的簡単で、主にvueの持っているtransionのコンポーネントを使って実現するアニメーション効果で、apparの属性をプラスして入場するアニメーション効果があることに注意します。

<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のスタイルを参考にして、アニメーションを使って簡単なアニメーション効果を作りました。

.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のトランスポートゲートメソッドpotalを使用しています。コンポーネントを新しいVueにアップロードし、実用化してから、新しいインスタンスをbodyの下にマウントします。

import Vue, {VueConstructor} from "vue";

interface Param {
 cmp: VueConstructor & {instance?: () => any};
 name: string;
 method?: string;
 target?: string | HTMLElement;
 props?: any;
}

export default function portal(param: Param){
  let {cmp, name, method, target = document.body, props = {}} = param
  if(typeof target === 'string') target = document.querySelector(target) as HTMLElement;
  method = method || 'show'
  cmp.instance = ()=>{
    let instance = new Vue({
      render(h){
        return h(cmp, {props})
      }
    })

    instance.$mount();

    //  instance.$el       
    (target as HTMLElement).appendChild(instance.$el);
    return instance.$children[0];
  }
  const instance = cmp.instance()
  
  Vue.prototype[`$${name}`] = instance[method];
}
次に、Vueプロジェクトの入り口ファイルで転送ゲート方法を使ってMsgコンポーネントをマウントしてコンポーネントの中で使用することができます。

portal({
 name: 'msg',
 cmp: MsgBox,
 method: 'addMsg'
})
ここでは、Element-uiをまねて簡単な$message方法を実現する記事について紹介します。Element-ui$messageの内容については、以前の文章を検索したり、次の関連記事を見たりしてください。これからもよろしくお願いします。