J-skeleton.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <template>
  2. <view>
  3. <view v-if="loading" class="skeleton" :class="{ animate: animate }" :style="{ justifyContent: flexType}">
  4. <!-- 轮播图 -->
  5. <view
  6. v-if="imgTitle"
  7. class="skeleton-imgTitle"
  8. style="width: 95%;border-radius: 10px;height: 100px;display: block;"
  9. ></view>
  10. <!-- 头像图 -->
  11. <view
  12. v-if="showAvatar && !imgTitle"
  13. class="skeleton-avatar"
  14. v-for="(item, index) in nameRow"
  15. :key="index"
  16. :class="[avatarShape]"
  17. :style="{ width: avatarSize, height: avatarSize}"
  18. ></view>
  19. <!-- 文字条 -->
  20. <view class="skeleton-content" v-if="showTitle && !imgTitle">
  21. <view class="skeleton-title" :style="{ width: titleWidth }"></view>
  22. <view class="skeleton-rows">
  23. <view
  24. class="skeleton-row-item"
  25. v-for="(item, index) in rowList"
  26. :key="index"
  27. :style="{ width: item.width }"
  28. ></view>
  29. </view>
  30. </view>
  31. </view>
  32. <view v-else><slot></slot></view>
  33. </view>
  34. </template>
  35. <script>
  36. const DEFAULT_ROW_WIDTH = '100%'
  37. const DEFAULT_LAST_ROW_WIDTH = '100%'
  38. export default {
  39. props: {
  40. loading: {
  41. type: Boolean,
  42. default: true,
  43. },
  44. imgTitle: {
  45. type: Boolean,
  46. default: false,
  47. },
  48. nameRow:{
  49. type: Number,
  50. default: 1,
  51. },
  52. flexType:{
  53. type: String,
  54. default: 'flex-start', // center 居中 √ space-between 两端对齐 √ space-around 子元素拉手分布 √ flex-start 居左 flex-end 居右
  55. },
  56. showAvatar: {
  57. type: Boolean,
  58. default: true,
  59. },
  60. avatarSize: {
  61. type: String,
  62. default: '50px',
  63. },
  64. avatarShape: {
  65. type: String,
  66. default: 'round', // square | round
  67. },
  68. showTitle: {
  69. type: Boolean,
  70. default: false,
  71. },
  72. titleWidth: {
  73. type: String,
  74. default: '40%',
  75. },
  76. row: {
  77. type: Number,
  78. default: 3,
  79. },
  80. animate: {
  81. type: Boolean,
  82. default: true,
  83. },
  84. },
  85. data() {
  86. return {}
  87. },
  88. computed: {
  89. rowList() {
  90. let list = []
  91. for (let i = 0; i < this.row; i++) {
  92. list.push({
  93. width: i === this.row - 1 && i !== 0 ? DEFAULT_LAST_ROW_WIDTH : DEFAULT_ROW_WIDTH,
  94. })
  95. }
  96. return list
  97. },
  98. },
  99. }
  100. </script>
  101. <style scoped>
  102. .skeleton {
  103. display: flex;
  104. margin: 16px;
  105. --bg-color: #f2f3f5;
  106. --row-height: 35px;
  107. --row-margin-top: 16px;
  108. }
  109. .skeleton-imgTitle {
  110. flex-wrap: wrap;
  111. background: var(--bg-color);
  112. margin: 10px auto;
  113. }
  114. .skeleton-avatar {
  115. flex-shrink: 0;
  116. background: var(--bg-color);
  117. margin-right: 8px;
  118. }
  119. .skeleton-avatar.round {
  120. border-radius: 50%;
  121. }
  122. .skeleton-content {
  123. width: 100%;
  124. }
  125. .skeleton-title {
  126. background-color: var(--bg-color);
  127. height: var(--row-height);
  128. }
  129. .skeleton-title + .skeleton-rows {
  130. margin-top: var(--row-margin-top);
  131. }
  132. .skeleton-rows {
  133. }
  134. .skeleton-row-item {
  135. background-color: var(--bg-color);
  136. height: var(--row-height);
  137. }
  138. .skeleton-row-item:not(:first-child) {
  139. margin-top: var(--row-margin-top);
  140. }
  141. .skeleton.animate {
  142. animation: skeleton-blink 1.2s ease-in-out infinite;
  143. }
  144. @keyframes skeleton-blink {
  145. 0% {
  146. opacity: 1;
  147. }
  148. 50% {
  149. opacity: 0.6;
  150. }
  151. 100% {
  152. opacity: 1;
  153. }
  154. }
  155. </style>