组件库开发:yk-avatar (头像)组件

avatar组件

头像组件是我们比较常见的,而且开发起来并不复杂

我们先看一下大致的效果

image.png

其实大家可能直接就有思路了,就是一个盒子,然后可以通过传值定义形状大小、然后最重要的是可以上传对应的头像即可

是的,并没有那么复杂,然后一般头像会有一个头像组,就像下面这样

image.png

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>

image.png

封装util

我们的头像大小头像圆角的大小,我们可以封装出两个函数,方便我们传值

比如,我们大小想传值smlxl等,然后可以返回对应的大小

我们想传对应的形状。比如circlesquare,它也会返回对应的大小

我们可以在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个头像,但是只显示三个头像剩下的隐藏并显示隐藏的个数

也就是这样

image.png

而且我们需要处理一下对应的头像组的样式,好的,那我们开始吧~

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)

我们首先拿到$avatarGroupDOM

然后我们创建一个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>

image.png

完成啦!

全部评论

相关推荐

拉丁是我干掉的:把上海理工大学改成北京理工大学。成功率增加200%
点赞 评论 收藏
分享
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务