Commit 4f00d7595c578dbd30c86913a13f65eb16ee99ac

Authored by “wangming”
1 parent 844ae2b7

优化个人中心页面:实现玻璃态卡片效果、3D悬浮动效和流动导航指示器

- 增强玻璃态卡片效果:半透明背景+模糊效果+精致边框+内层高光
- 添加3D悬浮动效:所有卡片支持点击缩放和阴影变化,光晕扫过动画
- 优化底部导航栏:胶囊式设计+流动选中指示器,平滑过渡动画
- 提升整体视觉体验:轻盈高级的玻璃质感,流畅的交互动效
绿纤uni-app/components/custom-tab-bar/index.vue
1 1 <template>
2 2 <view class="tab-block">
3 3 <view class='tab-list flex flex-center' :class="showMiddleButton === true?'tab-list-middle':'tab-list-default'">
  4 + <!-- 流动选中指示器 -->
  5 + <view class="tab-indicator" :style="{ left: indicatorLeft + 'px', width: indicatorWidth + 'px' }"></view>
4 6 <view v-for="(item, index) in tabList"
5 7 :class="'list-item flex flex-column flex-middle ' + item.middleClass"
6 8 @tap="handlePush(item, index)"
7   - :key="index">
  9 + :key="index"
  10 + :ref="'tabItem' + index">
8 11 <view class="item-img-box">
9 12 <image
10 13 class="item-img"
... ... @@ -84,7 +87,42 @@
84 87 return matchedIndex >= 0 ? matchedIndex : 0;
85 88 }
86 89 },
  90 + mounted() {
  91 + this.updateIndicator()
  92 + },
  93 + watch: {
  94 + currentTabIndex() {
  95 + this.$nextTick(() => {
  96 + setTimeout(() => {
  97 + this.updateIndicator()
  98 + }, 100)
  99 + })
  100 + }
  101 + },
87 102 methods: {
  103 + // 更新流动指示器位置
  104 + updateIndicator() {
  105 + this.$nextTick(() => {
  106 + try {
  107 + const query = uni.createSelectorQuery().in(this)
  108 + query.select('.tab-list').boundingClientRect((tabListRect) => {
  109 + query.selectAll('.list-item').boundingClientRect((rects) => {
  110 + if (rects && rects.length > 0 && this.currentTabIndex >= 0 && this.currentTabIndex < rects.length) {
  111 + const currentRect = rects[this.currentTabIndex]
  112 +
  113 + if (currentRect && tabListRect) {
  114 + // 计算指示器的位置和宽度
  115 + this.indicatorWidth = currentRect.width * 0.6
  116 + this.indicatorLeft = currentRect.left - tabListRect.left + (currentRect.width - this.indicatorWidth) / 2
  117 + }
  118 + }
  119 + }).exec()
  120 + }).exec()
  121 + } catch (e) {
  122 + console.error('更新指示器失败:', e)
  123 + }
  124 + })
  125 + },
88 126 // 点击按钮
89 127 handlePush(item, index) {
90 128 if (this.currentTabIndex !== index) {
... ... @@ -134,29 +172,60 @@
134 172 .tab-list{
135 173 height: 120rpx;
136 174 }
137   - .tab-list-default{
138   - background-color: rgba(255, 255, 255, 0.85);
139   - border-radius: 28rpx;
140   - box-shadow: 0rpx 4rpx 20rpx rgba(0, 0, 0, 0.1);
141   - backdrop-filter: blur(10rpx);
142   - }
  175 + .tab-list-default{
  176 + background: rgba(255, 255, 255, 0.7);
  177 + backdrop-filter: blur(20px) saturate(180%);
  178 + -webkit-backdrop-filter: blur(20px) saturate(180%);
  179 + border-radius: 28rpx;
  180 + box-shadow: 0rpx 8rpx 32rpx rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;
  181 + border: 1px solid rgba(255, 255, 255, 0.4);
  182 + position: relative;
  183 + transition: all 0.3s ease;
  184 + }
143 185 .tab-list-middle {
144 186 position: relative;
145 187 background: url('https://res.paquapp.com/popmartvip/home/nav_bar_bg_2x.png') no-repeat center center;
146 188 background-size: cover;
147 189 }
  190 + /* 流动选中指示器 */
  191 + .tab-indicator {
  192 + position: absolute;
  193 + bottom: 8rpx;
  194 + height: 4rpx;
  195 + background: linear-gradient(90deg, #43a047 0%, #66bb6a 100%);
  196 + border-radius: 2rpx;
  197 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  198 + box-shadow: 0 2rpx 8rpx rgba(67, 160, 71, 0.4);
  199 + z-index: 1;
  200 + }
  201 +
148 202 .list-item {
149 203 flex: 1;
  204 + position: relative;
  205 + z-index: 2;
  206 + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  207 +
150 208 .item-img-box {
151 209 width: 40rpx;
152 210 height: 40rpx;
153 211 margin-bottom: 6rpx;
154 212 position: relative;
  213 + transition: transform 0.3s ease;
155 214 }
156 215  
157 216 .item-img {
158 217 width: 40rpx;
159 218 height: 40rpx;
  219 + transition: transform 0.3s ease;
  220 + }
  221 +
  222 + /* 3D悬浮动效 */
  223 + &:active {
  224 + transform: translateY(-2rpx) scale(0.95);
  225 +
  226 + .item-img-box {
  227 + transform: scale(1.1);
  228 + }
160 229 }
161 230 }
162 231  
... ...
绿纤uni-app/pages/me/me.vue
... ... @@ -691,14 +691,40 @@ export default {
691 691 }
692 692  
693 693 .warpboxss {
694   - background-color: rgba(255, 255, 255, 0.4);
695   - backdrop-filter: blur(16px) saturate(180%);
696   - -webkit-backdrop-filter: blur(16px) saturate(180%);
  694 + background: rgba(255, 255, 255, 0.25);
  695 + backdrop-filter: blur(20px) saturate(180%);
  696 + -webkit-backdrop-filter: blur(20px) saturate(180%);
697 697 padding: 30rpx;
698 698 border-radius: 24rpx;
699   - box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
700   - border: 1px solid rgba(255, 255, 255, 0.125);
  699 + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;
  700 + border: 1px solid rgba(255, 255, 255, 0.4);
701 701 margin-bottom: 24rpx;
  702 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  703 + transform: translateY(0) scale(1);
  704 + position: relative;
  705 + overflow: hidden;
  706 +}
  707 +
  708 +/* 3D悬浮动效 - 所有卡片 */
  709 +.warpboxss::before {
  710 + content: '';
  711 + position: absolute;
  712 + top: 0;
  713 + left: -100%;
  714 + width: 100%;
  715 + height: 100%;
  716 + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  717 + transition: left 0.6s ease;
  718 +}
  719 +
  720 +.warpboxss:active {
  721 + transform: translateY(-4rpx) scale(0.98);
  722 + box-shadow: 0 12rpx 48rpx rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
  723 + border-color: rgba(255, 255, 255, 0.5);
  724 +}
  725 +
  726 +.warpboxss:active::before {
  727 + left: 100%;
702 728 }
703 729  
704 730 .warpboxs-small-title {
... ... @@ -847,12 +873,17 @@ export default {
847 873 }
848 874  
849 875 .kpi-card {
850   - background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
  876 + background: linear-gradient(135deg, rgba(76, 175, 80, 0.85) 0%, rgba(102, 187, 106, 0.85) 100%);
  877 + backdrop-filter: blur(20px) saturate(180%);
  878 + -webkit-backdrop-filter: blur(20px) saturate(180%);
851 879 border-radius: 24rpx;
852 880 padding: 40rpx 30rpx;
853   - box-shadow: 0 8rpx 24rpx rgba(76, 175, 80, 0.3);
  881 + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.2) inset;
  882 + border: 1px solid rgba(255, 255, 255, 0.3);
854 883 position: relative;
855 884 overflow: hidden;
  885 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  886 + transform: translateY(0) scale(1);
856 887  
857 888 &::before {
858 889 content: '';
... ... @@ -861,13 +892,25 @@ export default {
861 892 right: -50%;
862 893 width: 200%;
863 894 height: 200%;
864   - background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
  895 + background: radial-gradient(circle, rgba(255, 255, 255, 0.15) 0%, transparent 70%);
  896 + transition: transform 0.6s ease;
  897 + }
  898 +
  899 + &:active {
  900 + transform: translateY(-6rpx) scale(0.97);
  901 + box-shadow: 0 16rpx 48rpx rgba(76, 175, 80, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;
  902 + }
  903 +
  904 + &:active::before {
  905 + transform: rotate(180deg);
865 906 }
866 907  
867 908 .kpi-title {
868 909 font-size: 26rpx;
869   - color: rgba(255, 255, 255, 0.9);
  910 + color: rgba(255, 255, 255, 0.95);
870 911 margin-bottom: 16rpx;
  912 + position: relative;
  913 + z-index: 1;
871 914 }
872 915  
873 916 .kpi-value {
... ... @@ -875,6 +918,8 @@ export default {
875 918 font-weight: 700;
876 919 color: #ffffff;
877 920 line-height: 1.2;
  921 + position: relative;
  922 + z-index: 1;
878 923 }
879 924 }
880 925  
... ... @@ -895,6 +940,13 @@ export default {
895 940 flex-direction: column;
896 941 align-items: center;
897 942 text-align: center;
  943 + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  944 + transform: translateY(0) scale(1);
  945 + cursor: pointer;
  946 + }
  947 +
  948 + .small-stat-item:active {
  949 + transform: translateY(-4rpx) scale(0.95);
898 950 }
899 951  
900 952 .small-stat-icon {
... ... @@ -951,13 +1003,38 @@ export default {
951 1003 }
952 1004  
953 1005 .data-card {
954   - background-color: rgba(255, 255, 255, 0.4);
955   - backdrop-filter: blur(16px) saturate(180%);
956   - -webkit-backdrop-filter: blur(16px) saturate(180%);
  1006 + background: rgba(255, 255, 255, 0.25);
  1007 + backdrop-filter: blur(20px) saturate(180%);
  1008 + -webkit-backdrop-filter: blur(20px) saturate(180%);
957 1009 border-radius: 24rpx;
958 1010 padding: 30rpx;
959   - box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
960   - border: 1px solid rgba(255, 255, 255, 0.125);
  1011 + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;
  1012 + border: 1px solid rgba(255, 255, 255, 0.4);
  1013 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  1014 + transform: translateY(0) scale(1);
  1015 + position: relative;
  1016 + overflow: hidden;
  1017 +}
  1018 +
  1019 +.data-card::before {
  1020 + content: '';
  1021 + position: absolute;
  1022 + top: 0;
  1023 + left: -100%;
  1024 + width: 100%;
  1025 + height: 100%;
  1026 + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  1027 + transition: left 0.6s ease;
  1028 +}
  1029 +
  1030 +.data-card:active {
  1031 + transform: translateY(-4rpx) scale(0.98);
  1032 + box-shadow: 0 12rpx 48rpx rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
  1033 + border-color: rgba(255, 255, 255, 0.5);
  1034 +}
  1035 +
  1036 +.data-card:active::before {
  1037 + left: 100%;
961 1038 }
962 1039  
963 1040 .data-card-title {
... ...