组件库开发:yk-avatar (头像)组件
avatar组件
头像组件是我们比较常见的,而且开发起来并不复杂
我们先看一下大致的效果
其实大家可能直接就有思路了,就是一个盒子,然后可以通过传值定义形状
、大小
、然后最重要的是可以上传对应的头像
即可
是的,并没有那么复杂,然后一般头像会有一个头像组
,就像下面这样
OK,大致的想法有了,那么我们就开始吧~
avatar组件属性
我们先想一下大致需要哪些属性
- size:头像大小
- icon:图标显示,对应的图标名称
- imgUrl:图片显示,对应的图片地址
- shape:头像形状
那么我们的props
就可以这么写出来了
import { Size } from '../../utils/constant';
export type AvatarProps = {
shape?: 'circle' | 'square' | number;
size?: number | Size;
imgUrl?: string;
};
这里的shape
我们加上number
是想要用户可以手动控制圆角大小
开发avatar组件
我们把模板结构先写出来
<template>
<div :class="['yk-avatar', size && 'yk-avatar-group']" :style="style">
<img v-if="props.imgUrl" :src="imgUrl" />
<span v-else ref="$text" class="yk-avatar-text">
<slot />
</span>
</div>
</template>
这里的yk-avatar-group
是为了我们的头像组进行准备
这里的img
是我们正常显示的图片,span
则是我们如果想自己传入文字,想显示文字效果,像下面这样
<yk-avatar style="background-color: hsl(198deg 90% 54%)">
李太白
</yk-avatar>
封装util
我们的头像大小
和头像圆角的大小
,我们可以封装出两个函数,方便我们传值
比如,我们大小想传值s
、m
、l
、xl
等,然后可以返回对应的大小
我们想传对应的形状。比如circle
、square
,它也会返回对应的大小
我们可以在src
下新建一个util.ts
来进行这两个函数的封装
utils
:
//头像大小
export const getSize = (size: number | string): number => {
if (typeof size === 'number') {
return size
}
switch (size) {
case 's':
return 24
case 'm':
return 32
case 'l':
return 40
case 'xl':
return 80
default:
return 40
}
}
//圆角大小
export const getShape = (shape: number | string): number => {
if (typeof shape === 'number') {
return shape
}
switch (shape) {
case 'circle':
return 200
case 'square':
return 8
default:
return 200
}
}
完善逻辑
然后我们可以丰富一下逻辑
defineOptions({
name: 'YkAvatar',
})
const props = withDefaults(defineProps<AvatarProps>(), {
shape: 'circle',
size: 40,
})
const style = computed<CSSProperties>(() => {
const finalSize = getSize(size || props.size)
return {
width: finalSize + 'px',
height: finalSize + 'px',
borderRadius: getShape(shape || props.shape) + 'px',
}
})
我们给了一个默认的
props
并且通过计算属性
style
动态地生成了一个包含头像容器样式的对象。它根据头像的大小和形状来设置容器的宽度、高度和边框半径。这样可以确保头像容器的样式与传入的属性保持一致,并且能够自动适应不同的大小和形状。
然后我们可以处理一下对应的文字
效果了
const $text = shallowRef<HTMLSpanElement>()
onMounted(() => {
if (!$text.value) return
const textWidth = $text.value.clientWidth
const wrapperWidth = getSize(size || props.size)
if (textWidth > wrapperWidth) {
$text.value.style.transform = `scale(${wrapperWidth / textWidth - 0.1})`
}
})
我们检查
<span>
元素的宽度是否超过容器的宽度,如果超过,则通过缩放文本来使其适应容器的宽度。这样可以确保文本在头像容器中显示时不会溢出。
avatar-group组件
我们avatar-group
比较重要的是,比如我们一个头像组有5个头像
,但是只显示三个头像
,剩下的隐藏
,并显示隐藏的个数
也就是这样
而且我们需要处理一下对应的头像组的样式,好的,那我们开始吧~
avatar-group属性
avatar-group
会多出一个max
属性,这个就是用来控制最大显示的个数,于是我们定义出props
export type AvatarGroupProps = {
shape?: 'circle' | 'square' | number;
size?: number | Size;
max?: number;
};
模板结构
<template>
<div ref="$avatarGroup" class="yk-avatar-group">
<slot></slot>
<div class="yk-avatar-group-more" :style="{
width: size,
height: size,
borderRadius: shape,
display: overstep > 1 ? 'flex' : 'none',
}">
<span>+{{ overstep - 1 }}</span>
</div>
</div>
</template>
这个模板结构相信大家已经看懂了,
yk-avatar-group
是我们的头像组,里面我们可以来传入对应的yk-avatar
,然后超出的时候,我们会有yk-avatar-group-more
来显示。
然后这里会有一个overstep
是最重要的
完善逻辑
defineOptions({
name: 'YkAvatarGroup',
})
const props = withDefaults(defineProps<AvatarGroupProps>(), {
max: 3,
shape: 'circle',
size: 40,
})
我们先把最基础的给写出来
然后现在我们需要和Avatar
进行传值,因为我们AvatarGroup
是基于Avatar
的,于是我们会传递给头像信息
provide('size', props.size)
provide('shape', props.shape)
然后Avatar需要进行接收
avatar.vue
:
const size = inject<AvatarProps['size']>('size', undefined)
const shape = inject<AvatarProps['shape']>('shape', undefined)
好的,然后我们可以继续完善头像组的逻辑了,我们定义大小
和形状
const size = getSize(props.size) + 'px'
const shape = getShape(props.shape) + 'px'
然后我们需要进行
const $avatarGroup = ref()
const overstep = ref(0)
const addStyle = () => {
let boxId: HTMLDivElement = $avatarGroup.value
Array.from(boxId.children).map((child, index) => {
const avatar: HTMLDivElement = child as HTMLDivElement
avatar.style.marginRight = -getSize(props.size) / 3 + 'px'
if (index >= props.max) {
overstep.value++
avatar.style.display = 'none'
}
})
}
onMounted(addStyle)
我们首先拿到$avatarGroup
的DOM
然后我们创建一个overstep
,用于表示是否超出了最大限制的头像数量。
接下来定义了一个名为 addStyle
的函数,用于添加特定样式到获取的 DOM 元素。
通过 Array.from(boxId.children)
将 boxId
的子元素转换为数组,并使用 map
方法遍历每个子元素。
在遍历过程中,将每个子元素断言为 HTMLDivElement
类型,并将其赋值给 avatar
变量。
接下来,为每个头像元素设置特定的样式,其中 avatar.style.marginRight
设置了右边距样式为 -getSize(props.size) / 3 + 'px'
。这里的 getSize(props.size)
是一个函数调用,根据传入的 props.size
参数计算出一个大小值。
然后,在判断 index >= props.max
的条件下,如果当前索引大于等于最大限制值,就执行以下代码块:
overstep.value++
:将overstep
的值递增,表示超出最大限制的头像数量加一。avatar.style.display = 'none'
:将头像元素的显示样式设置为'none'
,即隐藏该元素。
通过上述操作,会遍历头像容器
的子元素
,为每个头像元素
添加样式,同时根据最大限制值
判断是否隐藏
超出限制的头像元素。
样式
这里的样式是比较简单的
@import '../../../styles/color/colors.less';
.yk-avatar {
display: inline-flex;
justify-content: center;
align-items: center;
overflow: hidden;
white-space: nowrap;
color: #fff;
background-color: @font-color-ss;
vertical-align: top;
img {
float: left;
margin: 0;
padding: 0;
height: 100%;
}
&-text {
font-weight: bold;
}
&-group {
border: 2px solid @bg-color-l;
}
}
.yk-avatar-group {
display: flex;
&-more {
display: flex;
justify-content: center;
align-items: center;
font-weight: 500;
border: 2px solid @bg-color-l;
text-align: center;
color: @white;
background-color: @pcolor;
transition: border @animats;
}
}
主要定义了.yk-avatar
的样式,并对里面的img
和.yk-avatar-text
进行了样式修改
.yk-avatar-group
类是用于定义头像组容器的样式
display: flex;
:将头像组容器设置为弹性布局,使其内部的头像元素水平排列。.yk-avatar-group-more
类是用于定义头像组中超过最大限制的样式。
使用
<yk-avatar-group>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
</yk-avatar-group>
完成啦!