Commit 4f00d7595c578dbd30c86913a13f65eb16ee99ac

Authored by “wangming”
1 parent 844ae2b7

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

- 增强玻璃态卡片效果:半透明背景+模糊效果+精致边框+内层高光
- 添加3D悬浮动效:所有卡片支持点击缩放和阴影变化,光晕扫过动画
- 优化底部导航栏:胶囊式设计+流动选中指示器,平滑过渡动画
- 提升整体视觉体验:轻盈高级的玻璃质感,流畅的交互动效
绿纤uni-app/components/custom-tab-bar/index.vue
1 <template> 1 <template>
2 <view class="tab-block"> 2 <view class="tab-block">
3 <view class='tab-list flex flex-center' :class="showMiddleButton === true?'tab-list-middle':'tab-list-default'"> 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 <view v-for="(item, index) in tabList" 6 <view v-for="(item, index) in tabList"
5 :class="'list-item flex flex-column flex-middle ' + item.middleClass" 7 :class="'list-item flex flex-column flex-middle ' + item.middleClass"
6 @tap="handlePush(item, index)" 8 @tap="handlePush(item, index)"
7 - :key="index"> 9 + :key="index"
  10 + :ref="'tabItem' + index">
8 <view class="item-img-box"> 11 <view class="item-img-box">
9 <image 12 <image
10 class="item-img" 13 class="item-img"
@@ -84,7 +87,42 @@ @@ -84,7 +87,42 @@
84 return matchedIndex >= 0 ? matchedIndex : 0; 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 methods: { 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 handlePush(item, index) { 127 handlePush(item, index) {
90 if (this.currentTabIndex !== index) { 128 if (this.currentTabIndex !== index) {
@@ -134,29 +172,60 @@ @@ -134,29 +172,60 @@
134 .tab-list{ 172 .tab-list{
135 height: 120rpx; 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 .tab-list-middle { 185 .tab-list-middle {
144 position: relative; 186 position: relative;
145 background: url('https://res.paquapp.com/popmartvip/home/nav_bar_bg_2x.png') no-repeat center center; 187 background: url('https://res.paquapp.com/popmartvip/home/nav_bar_bg_2x.png') no-repeat center center;
146 background-size: cover; 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 .list-item { 202 .list-item {
149 flex: 1; 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 .item-img-box { 208 .item-img-box {
151 width: 40rpx; 209 width: 40rpx;
152 height: 40rpx; 210 height: 40rpx;
153 margin-bottom: 6rpx; 211 margin-bottom: 6rpx;
154 position: relative; 212 position: relative;
  213 + transition: transform 0.3s ease;
155 } 214 }
156 215
157 .item-img { 216 .item-img {
158 width: 40rpx; 217 width: 40rpx;
159 height: 40rpx; 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,14 +691,40 @@ export default {
691 } 691 }
692 692
693 .warpboxss { 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 padding: 30rpx; 697 padding: 30rpx;
698 border-radius: 24rpx; 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 margin-bottom: 24rpx; 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 .warpboxs-small-title { 730 .warpboxs-small-title {
@@ -847,12 +873,17 @@ export default { @@ -847,12 +873,17 @@ export default {
847 } 873 }
848 874
849 .kpi-card { 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 border-radius: 24rpx; 879 border-radius: 24rpx;
852 padding: 40rpx 30rpx; 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 position: relative; 883 position: relative;
855 overflow: hidden; 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 &::before { 888 &::before {
858 content: ''; 889 content: '';
@@ -861,13 +892,25 @@ export default { @@ -861,13 +892,25 @@ export default {
861 right: -50%; 892 right: -50%;
862 width: 200%; 893 width: 200%;
863 height: 200%; 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 .kpi-title { 908 .kpi-title {
868 font-size: 26rpx; 909 font-size: 26rpx;
869 - color: rgba(255, 255, 255, 0.9); 910 + color: rgba(255, 255, 255, 0.95);
870 margin-bottom: 16rpx; 911 margin-bottom: 16rpx;
  912 + position: relative;
  913 + z-index: 1;
871 } 914 }
872 915
873 .kpi-value { 916 .kpi-value {
@@ -875,6 +918,8 @@ export default { @@ -875,6 +918,8 @@ export default {
875 font-weight: 700; 918 font-weight: 700;
876 color: #ffffff; 919 color: #ffffff;
877 line-height: 1.2; 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,6 +940,13 @@ export default {
895 flex-direction: column; 940 flex-direction: column;
896 align-items: center; 941 align-items: center;
897 text-align: center; 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 .small-stat-icon { 952 .small-stat-icon {
@@ -951,13 +1003,38 @@ export default { @@ -951,13 +1003,38 @@ export default {
951 } 1003 }
952 1004
953 .data-card { 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 border-radius: 24rpx; 1009 border-radius: 24rpx;
958 padding: 30rpx; 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 .data-card-title { 1040 .data-card-title {