APIs
参数 说明 类型 默认值 必传 animated 是否展示动画效果 boolean true false button 是否使用按钮占位图 boolean | SkeletonButtonProps false false avatar 是否显示头像占位图 boolean | SkeletonAvatarProps false false input 是否使用输入框占位图 boolean | SkeletonInputProps false false image 是否使用图像占位图 boolean false false title 是否显示标题占位图 boolean | SkeletonTitleProps true false paragraph 是否显示段落占位图 boolean | SkeletonParagraphProps true false loading 为 true
时,显示占位图,反之则直接展示子组件 boolean true false
SkeletonButtonProps Type
名称 说明 类型 必传 shape 指定按钮的形状 ‘default’ | ‘round’ | ‘circle’ false size 设置按钮的大小 ‘default’ | ‘small’ | ‘large’ false block 将按钮宽度调整为其父宽度的选项 boolean false
SkeletonAvatarProps Type
名称 说明 类型 必传 shape 指定头像的形状 ‘circle’ | ‘square’ false size 设置头像占位图的大小 number | ‘default’ | ‘small’ | ‘large’ false
SkeletonInputProps Type
名称 说明 类型 必传 size 设置输入框的大小 ‘default’ | ‘small’ | ‘large’ false
SkeletonTitleProps Type
名称 说明 类型 必传 width 设置标题占位图的宽度 number | string false
SkeletonParagraphProps Type
名称 说明 类型 必传 rows 设置段落占位图的行数 number | string false 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
}
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 = true setTimeout ( ( ) => { 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>