本文是 Element 的组件源码学习系列。
项目源码:ElemeFE/element | GitHub,badge:v2.13.0
Badge 组件使用文档:badge 标记
.vue 文件:/packages/badge
.scss 文件:/packages/theme-chalk/badge.scss
.d.ts 文件:/types/badge.d.ts
Props
我们先来看一下 el-badge
这个组件的入参
1 | props: { |
is-dot
is-dot
这个属性比较简单,当这个属性赋值为 true
的时候,右上角的标记是一个小圆点。我们来看一下是如何实现的:
1 | <div class="el-badge"> |
首先,el-badge
的用法,是 <el-badge>
标签,包裹住需要右上角有标签的元素,所以 <template>
中的实现,是返回了一个 <div>
,包裹住被需要有标签的元素 <slot>
,在外面形成一个壳。然后通过 CSS,将标签定位到该元素的右上角。
动效
首先,标签被包裹在一个动效里面,这个动效是 ElementUI 设计的动效,具体的效果可以参考 内置过渡动画。
动效大致的实现思路是,利用 Vue 提供的原生组件 <transition>
,然后实现了具体的类。比如这里的 name = el-zoom-in-center
,就需要实现 el-zoom-in-center-enter
、el-zoom-in-center-active
、el-zoom-in-center-leave
等。具体要实现哪些类,在 Vue 文档中可以找到:过渡 & 动画 | 过渡的类名
在 ElementUI 中,这些类的实现,可以在 transition.scss 这里找到,大家有兴趣可以去看一下 ~
CSS
在不考虑动效之后,我们来看一下 is-dot
是如何实现的。
首先,当用户传入 is-dot
的时候,此时 content
为 undefined
1 | computed: { |
所以 <sup>
的 class
为 el-badge__content
和 is-dot
。
然后 :class
的逻辑里面,还有一句是 'is-fixed': $slots.default
。啥意思呢?$slots.default
指的是被 el-badge
包裹住的组件,如果 el-badge
包裹了某个组件,比如一个文本,则 $slots.default
的值就不为空,calss
就还需要加上 is-fixed
。
我们来看一下这几个类的实现逻辑:
1 | // badge.scss |
大体的实现思路如下:
- 使用
<sup>
标签。<sup>
标签在浏览器中,有一个默认的 CSS 属性是vertical-align: super
,该属性的作用是,将<sup>
垂直对齐文本的上标,也就是放在右上角 - 设置
position: relative
包裹住需要有角标的 HTML 元素,方便把角标通过position: absolute
定位到右上角;设置display: inline-block
,让被包裹元素和角标在一行 - 如果被包裹元素存在,则触发
@include when(fixed)
函数,设置position: absolute; top: 0; right: #{1 + $--badge-size / 2};
,将角标调整到右上角向左10px
。 - 针对元素的宽度和高度,做一些调整:
transform: translateY(-50%) translateX(100%);
。这句话大概的意思是:把元素沿着横向(x轴)移动自身宽度的100%,一般是从左侧为开始点也就是0点。而数值是100%,所以是从右侧0点向左移动自身宽度的100%。同时,向上移动自身高度的 50%
所以,整体实现的效果是,如果设置 right: 0px; transform: translateY(-50%) translateX(100%);
。则 badge 放在被定位元素的右上角,从 X 轴上面没有重合,Y 轴上面,badge 上移了 50% 的高度。然后为了美观,将 right
设置为 #{1 + $--badge-size / 2};
。通过 var.scss
文件,我们可以得到 $--badge-size
默认是 18px
。所以默认情况下,badge 在被包裹元素的右上角,朝左移动 10px,高度有 50% 的重合。
当然,如果有 is-Dot
的时候,直接将 <sup>
通过 width = height = 8px; border-radius: 50%
,画成一个小圆球就好。
max 和 value
value
有值的时候,content
就有值。此时赋值 v-text="content"
,就是让 <sup>
元素包裹一个文本就好。
我们可以看一下 max
是怎么实现的:
1 | content() { |
如果 max
和 value
都存在并且是数字类型,则比较 value
和 max
的大小,如果 value
小于 max
,则 badge 的内容就是 value
的值。如果 value
大于 max
,则 badge 的内容就是 ${max}+
字符串的值。比如 value = 100; max = 10
,则 badge 的内容就是 10+