当前位置: 首页 > news >正文

Vue3骨架屏(Skeleton)

效果如下图:在线预览

在这里插入图片描述
在这里插入图片描述

APIs

参数说明类型默认值必传
animated是否展示动画效果booleantruefalse
button是否使用按钮占位图boolean | SkeletonButtonPropsfalsefalse
avatar是否显示头像占位图boolean | SkeletonAvatarPropsfalsefalse
input是否使用输入框占位图boolean | SkeletonInputPropsfalsefalse
image是否使用图像占位图booleanfalsefalse
title是否显示标题占位图boolean | SkeletonTitlePropstruefalse
paragraph是否显示段落占位图boolean | SkeletonParagraphPropstruefalse
loadingtrue 时,显示占位图,反之则直接展示子组件booleantruefalse

SkeletonButtonProps Type

名称说明类型必传
shape指定按钮的形状‘default’ | ‘round’ | ‘circle’false
size设置按钮的大小‘default’ | ‘small’ | ‘large’false
block将按钮宽度调整为其父宽度的选项booleanfalse

SkeletonAvatarProps Type

名称说明类型必传
shape指定头像的形状‘circle’ | ‘square’false
size设置头像占位图的大小number | ‘default’ | ‘small’ | ‘large’false

SkeletonInputProps Type

名称说明类型必传
size设置输入框的大小‘default’ | ‘small’ | ‘large’false

SkeletonTitleProps Type

名称说明类型必传
width设置标题占位图的宽度number | stringfalse

SkeletonParagraphProps Type

名称说明类型必传
rows设置段落占位图的行数number | stringfalse
width设置段落占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度number | string | Array<number|string>false

创建骨架屏组件Skeleton.vue

<script setup lang="ts">
import { computed } from 'vue'interface SkeletonButtonProps {shape?: 'default'|'round'|'circle' // 指定按钮的形状size?: 'default'|'small'|'large' // 设置按钮的大小block?: boolean // 将按钮宽度调整为其父宽度的选项
}
interface SkeletonAvatarProps {shape?: 'circle'|'square' // 指定头像的形状size?: number|'default'|'small'|'large' // 设置头像占位图的大小
}
interface SkeletonInputProps {size: 'default'|'small'|'large' // 设置输入框的大小
}
interface SkeletonTitleProps {width?: number|string // 设置标题占位图的宽度
}
interface SkeletonParagraphProps {rows?: number|string // 设置段落占位图的行数width?: number|string|Array<number|string>	// 设置段落占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度
}
interface Props {animated?: boolean // 是否展示动画效果button?: boolean|SkeletonButtonProps // 是否使用按钮占位图avatar?: boolean|SkeletonAvatarProps // 是否显示头像占位图input?: boolean|SkeletonInputProps // 是否使用输入框占位图image?: boolean // 是否使用图像占位图title?: boolean|SkeletonTitleProps // 是否显示标题占位图paragraph?: boolean|SkeletonParagraphProps // 是否显示段落占位图loading?: boolean // 为 true 时,显示占位图,反之则直接展示子组件
}
const props = withDefaults(defineProps<Props>(), {animated: true,button: false,image: false,avatar: false,input: false,title: true,paragraph: true,loading: true
})
const buttonSize = computed(() => {if (typeof props.button === 'object') {if (props.button.size === 'large') {return 40}if (props.button.size === 'small') {return 24}return 32}
})
const titleTop = computed(() => {if (typeof props.avatar === 'boolean') {return 8} else {if (typeof props.avatar.size === 'number') {return (props.avatar.size - 16) / 2} else {const topMap = {default: 8,small: 4,large: 12}return topMap[props.avatar.size || 'default']}}
})
const titleWidth = computed(() => {if (typeof props.title === 'boolean') {return '38%'} else {if (typeof props.title.width === 'number') {return props.title.width + 'px'}return props.title.width || '38%'}
})
const paragraphRows = computed(() => {if (typeof props.paragraph === 'boolean') {return 3}return props.paragraph.rows
})
const paragraphWidth = computed(() => {if (typeof props.paragraph === 'boolean') {return Array(paragraphRows.value)} else {if (Array.isArray(props.paragraph.width)) {return props.paragraph.width.map((width: number|string) => {if (typeof width === 'number') {return width + 'px'} else {return width}})} else if (typeof props.paragraph.width === 'number') {return Array(paragraphRows.value).fill(props.paragraph.width + 'px')} else {return Array(paragraphRows.value).fill(props.paragraph.width)}}
})
</script>
<template><divv-if="loading":class="['m-skeleton', {'m-skeleton-avatar': avatar, 'm-skeleton-animated': animated}]":style="`--button-size: ${buttonSize}px; --title-top: ${titleTop}px;`"><spanv-if="button":class="['u-skeleton-button',{'u-button-round': typeof button !== 'boolean' && button.shape === 'round','u-button-circle': typeof button !== 'boolean' && button.shape === 'circle','u-button-sm': typeof button !== 'boolean' && button.size === 'small','u-button-lg': typeof button !== 'boolean' && button.size === 'large','u-button-block': typeof button !== 'boolean' && button.shape !== 'circle' && button.block,}]"></span><span:class="['u-skeleton-input',{'u-input-sm': typeof input !== 'boolean' && input.size === 'small','u-input-lg': typeof input !== 'boolean' && input.size === 'large',}]" v-if="input"></span><div class="m-skeleton-image" v-if="image"><svg viewBox="0 0 1098 1024" xmlns="http://www.w3.org/2000/svg" class="m-skeleton-image-svg"><path class="u-skeleton-image-path" d="M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z"></path></svg></div><div class="m-skeleton-header" v-if="avatar"><span:class="['u-skeleton-avatar',{'u-avatar-sm': typeof avatar !== 'boolean' && avatar.size === 'small','u-avatar-lg': typeof avatar !== 'boolean' && avatar.size === 'large','u-avatar-square': typeof avatar !== 'boolean' && avatar.shape === 'square',}]"></span></div><template v-if="!button && !image && !input"><div class="m-skeleton-content"><h3 class="u-skeleton-title" :style="{ width: titleWidth }"></h3><ul class="u-skeleton-paragraph"><li v-for="n in paragraphRows" :key="n" :style="`width: ${paragraphWidth[(n as number) - 1]};`"></li></ul></div></template></div><slot v-else></slot>
</template>
<style lang="less" scoped>
.m-skeleton {display: table;width: 100%;.u-skeleton-button {display: inline-block;vertical-align: top;background: rgba(0, 0, 0, .06);border-radius: 4px;width: 64px;min-width: 64px;height: 32px;line-height: 32px;}.u-button-sm {width: 48px;min-width: 48px;height: 24px;line-height: 24px;}.u-button-lg {width: 80px;min-width: 80px;height: 40px;line-height: 40px;}.u-button-round {border-radius: var(--button-size);}.u-button-circle {width: var(--button-size);min-width: var(--button-size);border-radius: 50%;}.u-button-block {width: 100%;}.u-skeleton-input {display: inline-block;vertical-align: top;background: rgba(0, 0, 0, 0.06);border-radius: 4px;width: 160px;min-width: 160px;height: 32px;line-height: 32px;}.u-input-sm {width: 120px;min-width: 120px;height: 24px;line-height: 24px;}.u-input-lg {width: 200px;min-width: 200px;height: 40px;line-height: 40px;}.m-skeleton-image {display: flex;align-items: center;justify-content: center;vertical-align: top;background: rgba(0, 0, 0, .06);border-radius: 4px;width: 96px;height: 96px;line-height: 96px;.m-skeleton-image-svg {width: 48px;height: 48px;line-height: 48px;max-width: 192px;max-height: 192px;.u-skeleton-image-path {fill: #bfbfbf;}}}.m-skeleton-header {display: table-cell;padding-right: 16px;vertical-align: top;.u-skeleton-avatar {display: inline-block;vertical-align: top;background: rgba(0, 0, 0, .06);width: 32px;height: 32px;line-height: 32px;border-radius: 50%;}.u-avatar-sm {width: 24px;height: 24px;line-height: 24px;}.u-avatar-lg {width: 40px;height: 40px;line-height: 40px;}.u-avatar-square {border-radius: 6px;}}.m-skeleton-content {display: table-cell;width: 100%;vertical-align: top;.u-skeleton-title {margin: 0;height: 16px;background: rgba(0, 0, 0, .06);border-radius: 4px;}.u-skeleton-paragraph {margin-top: 24px;padding: 0;li {height: 16px;list-style: none;background: rgba(0, 0, 0, .06);border-radius: 4px;&:not(:first-child) {margin-top: 16px;}&:last-child {width: 61%;}}}}
}
.m-skeleton-avatar {.m-skeleton-content {.u-skeleton-title {margin-top: var(--title-top);}.u-skeleton-paragraph {margin-top: 28px;}}
}
.m-skeleton-animated {.u-skeleton-button,.u-skeleton-input,.m-skeleton-image,.m-skeleton-header .u-skeleton-avatar,.m-skeleton-content .u-skeleton-title,.m-skeleton-content .u-skeleton-paragraph li {position: relative;z-index: 0;overflow: hidden;background: transparent;&::after {position: absolute;top: 0;left: -150%;bottom: 0;right: -150%;background: linear-gradient(90deg, rgba(0, 0, 0, .06) 25%, rgba(0, 0, 0, .15) 37%, rgba(0, 0, 0, .06) 63%);animation-name: skeleton-loading;animation-duration: 1.4s;animation-timing-function: ease;animation-iteration-count: infinite;content: "";}@keyframes skeleton-loading {0% {transform: translateX(-37.5%);}100% {transform: translateX(37.5%);}}}
}
</style>

在要使用的页面引入

<script setup lang="ts">
import Skeleton from './Skeleton.vue'
import { ref } from 'vue'const loading = ref<boolean>(false)const showSkeleton = () => {loading.value = truesetTimeout(() => {loading.value = false}, 2000)
}
const animated = ref(false)
const block = ref(false)
const size = ref('default')
const buttonShape = ref('default')
const avatarShape = ref('circle')
const sizeOptions = ref([{label: 'Default',value: 'default'},{label: 'Large',value: 'large'},{label: 'Small',value: 'small'}
])
const buttonShapeOptions = ref([{label: 'Default',value: 'default'},{label: 'Round',value: 'round'},{label: 'Circle',value: 'circle'}
])
const avatarShapeOptions = ref([{label: 'Square',value: 'square'},{label: 'Circle',value: 'circle'}
])
</script>
<template><div><h1>{{ $route.name }} {{ $route.meta.title }}</h1><h2 class="mt30 mb10">基本使用</h2><Skeleton /><h2 class="mt30 mb10">复杂的组合</h2><Skeleton avatar :paragraph="{ rows: 4 }" /><h2 class="mt30 mb10">包含子组件</h2><Button :loading="loading" @click="showSkeleton">Show Skeleton</Button><br/><br/><Skeleton :loading="loading"><div><h4>Vue Amazing UI, a design language</h4><br/><p>We supply a series of design principles, practical patterns and high quality designresources, to help people create their product prototypes beautifully and efficiently.</p></div></Skeleton><h2 class="mt30 mb10">自定义标题和段落</h2><Skeleton avatar :title="{ width: '24%' }" :paragraph="{ rows: 4, width: ['48%', '72%', '96%', '60%'] }" /><h2 class="mt30 mb10">按钮 / 输入框 / 图像 / 头像</h2><Flex :gap="32"><Flex vertical :gap="12" width="50%"><Skeleton :animated="animated" :button="{ shape: buttonShape, size: size, block: block}" /><Skeleton style="width: 200px" :animated="animated" :input="{ size: size }" /><Skeleton :animated="animated" image /><Skeleton :avatar="{ shape: avatarShape, size: size }" :paragraph="{ rows: 2 }" /></Flex><Flex vertical :gap="36" width="50%"><Space :size="32"><Space align="center">animated: <Switch v-model:checked="animated" /></Space><Space align="center">Button Block: <Switch v-model:checked="block" /></Space></Space><Space align="center">Size: <Radio :options="sizeOptions" v-model:value="size" /></Space><Space align="center">Button Shape: <Radio :options="buttonShapeOptions" v-model:value="buttonShape" /></Space><Space align="center">Avatar Shape: <Radio :options="avatarShapeOptions" v-model:value="avatarShape" /></Space></Flex></Flex></div>
</template>
http://www.lryc.cn/news/353207.html

相关文章:

  • 【文末附gpt升级方案】亚马逊与Hugging Face合作:定制芯片低成本运行AI模型的创新探索
  • 二叉树的链式实现
  • STM32中断编程入门
  • 《我的阿勒泰》读后感
  • Android.mk简单介绍、规则与基本格式
  • 【MySQL精通之路】InnoDB(3)-MVCC多版本管理
  • uniapp 对接 微信App/支付宝App 支付
  • cmake配置opencv与boost库
  • 【Kotlin 一】Kotlin入门知识简介、变量声明、数字类型
  • Java 微信小程序登录(openId方式)
  • 为何程序员35岁就开始被嫌弃了?程序员该如何避免中年危机?
  • 【2024软考】史上最全!软考刷题+解析大合集(9万字全手工打,货真价实)
  • 【Spring Security + OAuth2】授权
  • 失落的方舟台服预下载教程 一键下载+账号注册教程
  • 【启明智显技术分享】SOM2D02-2GW核心板适配ALSA(适用Sigmastar ssd201/202D)
  • 人工智能的发展现状,AI将如何改变IT行业,哪些职业将最先失业
  • request.js使用Promise.all等待所有请求完成再进行数据赋值
  • Java开发者必知的时间处理工具:SimpleDateFormat类详解
  • 构造函数的用法
  • 环形链表Ⅱ-力扣
  • 【microros】解决 microros安装过程中的 undefined reference to `fmt::v6 问题
  • 29. 相似矩阵,若尔当型
  • 【论文阅读】 YOLOv10: Real-Time End-to-End Object Detection
  • Python读写文件
  • docker-如何将容器外的脚本放入容器内,将容器内的脚本放入容器外
  • 算法训练营第三十九天 | LeetCode 738 单调递增的数字、LeetCode 968 监控二叉树
  • Hive语法学习总结
  • 【Linux】TCP协议【中】{确认应答机制/超时重传机制/连接管理机制}
  • solidworks画螺母学习笔记
  • WebGL的医学培训软件开发