Skeleton.vue 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. <template>
  2. <view class="skeleton" :class="type">
  3. <view
  4. class="skeleton-line"
  5. v-for="i in lines"
  6. :key="i"
  7. :style="{ width: getLineLength(i), height: lineHeight }"
  8. ></view>
  9. </view>
  10. </template>
  11. <script setup lang="ts">
  12. interface Props {
  13. type?: 'default' | 'avatar' | 'card' | 'list';
  14. lines?: number;
  15. lineHeight?: string;
  16. }
  17. const props = withDefaults(defineProps<Props>(), {
  18. type: 'default',
  19. lines: 3,
  20. lineHeight: '24rpx'
  21. });
  22. const getLineLength = (index: number) => {
  23. if (props.type === 'list') {
  24. // 列表类型:长度递减
  25. const lengths = ['100%', '90%', '80%', '70%'];
  26. return lengths[(index - 1) % lengths.length];
  27. }
  28. if (props.type === 'card') {
  29. // 卡片类型:交替长度
  30. return index % 2 === 0 ? '100%' : '75%';
  31. }
  32. return '100%';
  33. };
  34. </script>
  35. <style lang="scss">
  36. .skeleton {
  37. width: 100%;
  38. &.avatar {
  39. display: flex;
  40. align-items: center;
  41. gap: $spacing-md;
  42. .skeleton-line:first-child {
  43. width: 80rpx !important;
  44. height: 80rpx !important;
  45. border-radius: $radius-circle;
  46. flex-shrink: 0;
  47. }
  48. }
  49. &.card {
  50. padding: $spacing-md;
  51. background: $color-bg-primary;
  52. border-radius: $radius-lg;
  53. }
  54. &-line {
  55. height: 24rpx;
  56. background: $color-bg-tertiary;
  57. border-radius: $radius-sm;
  58. margin-bottom: $spacing-sm;
  59. animation: skeleton 1.5s cubic-bezier(0.42, 0, 0.58, 1) infinite;
  60. &:last-child {
  61. margin-bottom: 0;
  62. }
  63. }
  64. }
  65. </style>