badge组件
在上一节我们已经开发了头像组件,有时候随着头像的诞生,会产出一些额外的组件,也就是badge(徽标)组件
效果大致是这样的
但现在徽标也不仅仅用于此了
它可以应用在别的组件之上,也可以脱离组件,单独使用,例如这样
总之,badge组件是很灵活的,具体如何使用需要根据实际场景去判断
好的,那我们开始吧~
badge组件属性
我们思考一下,我们大致需要什么功能
首先是正常的使用,也分为小红点的形式,和显示数字的形式,也就是下面这样
这两者我们通过is-dot(展示为小红点)和count(徽标显示的数字)来进行控制
其次,我们的小红点默认是在外面的,我们可以控制它在外部还是内部,像下面这样
这样,我们通过in-dot来控制它是否显示在内部
然后,我们希望可以给badge一些状态,像下面这样
于是我们可以通过status 进行控制
但是有时候我们想自定义颜色怎么办,那么我们就添加一个color属性来进行控制
这里注意:
color的优先级会高于status
在之后,我们可以通过offset设置badge的位置
然后我们需要进行一些细节的控制,overflowCount(徽标显示的最大数字) 、showZero(徽标是否展示 0,也就是从零开启)、border(圆角的角度)
以及最后,我们想隐藏badge,可以通过一个hidden来进行控制
于是,我们根据属性,定义出props
export type BadgeProps = {
color?: string;
count?: number;
border?: number;
isDot?: boolean;
inDot?: boolean;
offset?: 'left' | 'right' | [number, number];
overflowCount?: number;
showZero?: boolean;
status?: Status;
hidden?: boolean;
};
组件模板
<template>
<div ref="badgeRef" class="yk-badge">
<!-- 徽标 -->
<div ref="supRef" class="yk-badge__sup">
<transition name="modal">
<div
v-if="isShowDot"
:class="['yk-badge__dot', dotClass]"
:style="dotStyle"
></div>
</transition>
<transition name="modal">
<div
v-if="isShowCount"
:class="['yk-badge__count', countClass]"
:style="countStyle"
>
{{ showCount }}
</div>
</transition>
</div>
<slot></slot>
</div>
</template>
我们使用
transition包裹圆点徽标,通过v-if来判断,当isShowDot为true时才渲染
同理我们再次使用
transition包裹数字徽标,当isShowCount为true时才渲染
transition在这里的作用是为徽标的出现和消失添加过渡效果,也就是上面隐藏的效果
逻辑部分
我们首先设置一些默认值
const props = withDefaults(defineProps<BadgeProps>(), {
count: 0,
border: 0,
overflowCount: 99,
showZero: false,
status: 'danger',
hidden: false,
})
首先我们先书写小红点部分的逻辑
const isDot = computed(() => props?.isDot ?? false)
const isShowDot = computed(() => {
return isDot.value && !props.hidden
})
const dotStyle = computed<CSSProperties>(() => {
const styles: CSSProperties = {}
if (props.color) {
styles.background = props.color
}
if (props.border !== 2) {
styles.boxShadow = `#ffffff 0 0 0 ${props.border}px`
}
return styles
})
const dotClass = computed(() => {
return {
'yk-badge__dot--inner': props.inDot,
[`yk-badge__dot--${props.status}`]: props.status !== undefined,
'yk-badge__dot--stand': !!useSlots().default === false,
}
})
isDot用于判断是否是点类型的徽标
isShowDot用于判断是否展示徽标
dotStyle则是用于设置点类型徽标的样式,并且根据props.color的值设置背景颜色。当props.border不等于2时,设置盒子阴影效果。
然后就到了数字徽标了
const isShowCount = computed(() => {
const countZeroHidden = props.count === 0 && !props.showZero
const countNormalHidden = props.count < 0
if (props.hidden || isShowDot.value || countZeroHidden || countNormalHidden) {
return false
}
return true
})
const showCount = computed(() => {
if (props.count && props.count > props.overflowCount) {
return Math.min(props.count!, props.overflowCount) + '+'
} else {
return props.count + ''
}
})
这两段用于判断是否展示数字徽标以及确定展示的数字
const isRound = () => {
if (isDot.value) {
return true
} else {
return showCount.value && showCount.value.toString().length === 1
}
}
这段定义了一个函数
isRound,用于判断展示的数字徽标是否是圆形
const countStyle = computed<CSSProperties>(() => {
const styles: CSSProperties = {}
// 默认右上角
styles.translate = `50% -50%`
// count的方位 定位右边,就左移动,左边,就右边移动
if (props.offset && props.offset === 'right') {
styles.translate = `-16px ${offsetValue.value}px`
} else if (props.offset && props.offset === 'left') {
styles.translate = `16px ${offsetValue.value}px`
}
// 自定义border时候
if (props.border !== 2) {
styles.boxShadow = `#ffffff 0 0 0 ${props.border}px`
}
// 自定义offset时候
if (Array.isArray(props.offset)) {
styles.translate = `${props.offset[0]}px ${props.offset[1]}px`
}
// color
if (props.color) {
styles.background = props.color
}
if (!!useSlots().default === false) {
return { background: props.color }
}
return styles
})
- 创建一个空对象
styles来存储样式属性。 - 默认情况下,数字徽标被定位在容器右上角
- 如果
props.offset存在且等于'right',则将徽标向左移动 - 如果
props.offset存在且等于'left',则将徽标向右移动 props.offset是一个数组,通过使用数组中的值来设置数字徽标的偏移量- 如果
props.color存在,则将背景颜色设置为props.color。如果没有默认插槽内容存在(即useSlots().default返回false),则返回一个只包含背景颜色的样式对象{ background: props.color }。
let badgeHeight = ref(0)
const offsetValue = computed({
get() {
return badgeHeight.value
},
set(v) {
badgeHeight.value = v
},
})
这段用于存储数字徽标的高度
const badgeRef = ref()
onMounted(() => {
const badgeDom: HTMLDivElement = badgeRef.value
const supDomHeight = ref(0)
if (props.count && props.count > props.overflowCount) {
supDomHeight.value = 10
} else {
supDomHeight.value = 10
}
// 移动父级的50% - 自身高度的 50%
offsetValue.value = badgeDom.offsetHeight / 2 - supDomHeight.value
})
获取了数字徽标的 DOM
badgeDom,并定义了一个变量supDomHeight用于存储高度值。
const countClass = computed(() => {
return {
isRound: isRound(),
[`yk-badge__count--${props.status}`]: props.status !== undefined,
[`yk-badge__count--${props.offset}`]:
props.offset !== undefined && typeof props.offset == 'string',
'yk-badge__count--stand': !!useSlots().default === false,
}
})
isRound: isRound()将isRound()的返回值作为isRound类名的存在与否。- 如果
props.status存在,则生成yk-badge__count--${props.status}类名,用于根据状态设置样式。 - 如果
props.offset存在且类型为字符串,则生成yk-badge__count--${props.offset}类名,用于根据偏移位置设置样式。 - 如果没有默认插槽内容存在(即
useSlots().default返回false),则生成yk-badge__count--stand类名,用于设置没有内容时的样式。
样式
@import '../../styles/color/colors.less';
.modal {
&-enter {
&-from {
scale: 0;
opacity: 0;
}
&-active {
transition: all 0.2s ease-in;
}
&-to {
scale: 1;
opacity: 1;
}
}
&-leave {
&-from {
scale: 1;
opacity: 1;
}
&-active {
transition: all 0.2s ease-out;
}
&-to {
scale: 0;
opacity: 0;
}
}
}
.yk-badge {
position: relative;
display: inline-flex;
flex-direction: column;
box-sizing: border-box;
line-height: 1;
.yk-badge__dot {
position: absolute;
top: -@space-ss;
right: -@space-ss;
z-index: 1;
width: 8px;
height: 8px;
border-radius: 8px;
background: @ecolor;
box-shadow: 0 0 0 2px #ffffff;
&--inner {
top: @space-ss;
right: @space-ss;
}
&--stand {
position: static;
}
}
.yk-badge__dot.yk-badge__dot--primary {
background: @pcolor;
}
.yk-badge__dot.yk-badge__dot--success {
background: @scolor;
}
.yk-badge__dot.yk-badge__dot--warning {
background: @wcolor;
}
.yk-badge__dot.yk-badge__dot--danger {
background: @ecolor;
}
.yk-badge__count {
position: absolute;
top: 0px;
right: 0px;
z-index: 1;
// 由外到内 设置margin -> padding -> width -> height
padding: 0 6px;
font-size: @size-ss;
font-family: PingFangSC-Regular;
font-weight: 400;
border-radius: 10px;
text-align: center;
color: @white;
background: @ecolor;
box-shadow: 0 0 0 2px #ffffff;
line-height: 20px;
&--left {
top: 0;
left: 0;
}
&--right {
top: 0;
right: 0;
}
&--stand {
position: static;
}
}
.yk-badge__count.yk-badge__count--primary {
background: @pcolor;
}
.yk-badge__count.yk-badge__count--success {
background: @scolor;
}
.yk-badge__count.yk-badge__count--warning {
background: @wcolor;
}
.yk-badge__count.yk-badge__count--danger {
background: @ecolor;
}
.yk-badge__count.isRound {
padding: 0;
width: 20px;
height: 20px;
border-radius: 10px;
}
}
首先,我们把进入和离开动画的相关样式完善了
.yk-badge:表示徽标组件的根元素样式。
.yk-badge__dot:表示徽标组件的小圆点样式。
.yk-badge__count:表示徽标组件的数字计数样式
stand是独立使用时的样式,像下面这样
这样我们所有的功能就都完善啦