| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- <template>
- <view class="skeleton-wrapper">
- <block v-for="(item, index) in computedItems" :key="index">
- <!-- Banner: 通栏大矩块 -->
- <view v-if="type === 'banner'" class="skeleton-banner" :style="bannerStyle"></view>
- <!-- Card: 中等卡片 + 内部小矩块 -->
- <view v-else-if="type === 'card'" class="skeleton-card" :style="cardStyle">
- <view class="skeleton-card-inner"></view>
- <view class="skeleton-card-line"></view>
- <view class="skeleton-card-line skeleton-card-line--short"></view>
- </view>
- <!-- Row: 单行横条 -->
- <view v-else-if="type === 'row'" class="skeleton-row" :style="rowStyle"></view>
- <!-- Circle: 圆形 -->
- <view v-else-if="type === 'circle'" class="skeleton-circle" :style="circleStyle"></view>
- <!-- Text: 多行文字占位 -->
- <view v-else class="skeleton-text-group">
- <view
- v-for="(row, ri) in textRows"
- :key="ri"
- class="skeleton-text-row"
- :style="{ width: row.width }"
- ></view>
- </view>
- </block>
- </view>
- </template>
- <script>
- export default {
- name: 'SkeletonBlock',
- props: {
- type: {
- type: String,
- default: 'row'
- },
- width: {
- type: String,
- default: ''
- },
- height: {
- type: String,
- default: ''
- },
- count: {
- type: Number,
- default: 1
- }
- },
- computed: {
- computedItems() {
- var arr = []
- for (var i = 0; i < this.count; i++) {
- arr.push(i)
- }
- return arr
- },
- baseShimmer() {
- // Reuse design system shimmer from uni.scss
- return 'linear-gradient(90deg, #E2E8F0 25%, #EDF2F7 50%, #E2E8F0 75%)'
- },
- bannerStyle() {
- var style = 'width: 100%; height: 200rpx; border-radius: 16rpx;'
- if (this.width) {
- style += 'width: ' + this.width + ';'
- }
- if (this.height) {
- style += 'height: ' + this.height + ';'
- }
- return style
- },
- cardStyle() {
- var style = 'width: 100%; height: 160rpx;'
- if (this.width) {
- style += 'width: ' + this.width + ';'
- }
- if (this.height) {
- style += 'height: ' + this.height + ';'
- }
- return style
- },
- rowStyle() {
- var style = 'width: 100%; height: 32rpx; margin-bottom: 12rpx;'
- if (this.width) {
- style += 'width: ' + this.width + ';'
- }
- if (this.height) {
- style += 'height: ' + this.height + ';'
- }
- return style
- },
- circleStyle() {
- var style = 'width: 80rpx; height: 80rpx; border-radius: 50%;'
- if (this.width) {
- style += 'width: ' + this.width + ';'
- }
- if (this.height) {
- style += 'height: ' + this.height + ';'
- }
- return style
- },
- textRows() {
- return [
- { width: '100%' },
- { width: '100%' },
- { width: '88%' },
- { width: '60%' }
- ]
- }
- }
- }
- </script>
- <style scoped>
- .skeleton-wrapper {
- display: flex;
- flex-direction: column;
- }
- /* ===== Shimmer 基础 ===== */
- .skeleton-banner,
- .skeleton-card,
- .skeleton-row,
- .skeleton-circle,
- .skeleton-card-inner,
- .skeleton-card-line,
- .skeleton-text-row {
- background: linear-gradient(90deg, #E2E8F0 25%, #F5F9FC 50%, #E2E8F0 75%);
- background-size: 200% 100%;
- animation: skeleton-shimmer 1.5s ease-in-out infinite;
- will-change: transform;
- }
- @keyframes skeleton-shimmer {
- 0% {
- background-position: -200% 0;
- }
- 100% {
- background-position: 200% 0;
- }
- }
- /* ===== Banner ===== */
- .skeleton-banner {
- margin-bottom: 20rpx;
- }
- /* ===== Card ===== */
- .skeleton-card {
- background: none !important;
- border-radius: 16rpx;
- padding: 20rpx;
- margin-bottom: 20rpx;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- border: 1rpx solid #E2E8F0;
- }
- .skeleton-card-inner {
- width: 60%;
- height: 48rpx;
- border-radius: 8rpx;
- }
- .skeleton-card-line {
- width: 100%;
- height: 20rpx;
- border-radius: 6rpx;
- margin-top: 10rpx;
- }
- .skeleton-card-line--short {
- width: 40%;
- }
- /* ===== Row ===== */
- .skeleton-row {
- border-radius: 8rpx;
- }
- /* ===== Circle ===== */
- .skeleton-circle {
- }
- /* ===== Text ===== */
- .skeleton-text-group {
- display: flex;
- flex-direction: column;
- gap: 12rpx;
- }
- .skeleton-text-row {
- height: 24rpx;
- border-radius: 6rpx;
- }
- </style>
|