修改UI风格为粘土拟态风格

This commit is contained in:
tsui110 2026-02-05 16:00:40 +08:00
parent 90110f5bce
commit 2a98cde85f
9 changed files with 1412 additions and 58 deletions

271
components/ClayButton.vue Normal file
View File

@ -0,0 +1,271 @@
<template>
<view
class="clay-button-wrapper"
:class="[
`clay-btn-${size}`,
{ 'clay-btn-block': block },
{ 'clay-btn-disabled': disabled }
]"
>
<view
class="clay-button"
:class="[
`clay-btn-${variant}`,
{ 'clay-btn-outline': outline },
{ 'is-loading': loading }
]"
:style="customStyle"
@tap="handleTap"
>
<!-- 加载动画 -->
<view v-if="loading" class="clay-loading">
<view class="loading-spinner"></view>
</view>
<!-- 图标 -->
<view v-if="icon && !loading" class="clay-icon">
<text>{{ icon }}</text>
</view>
<!-- 按钮文字 -->
<text class="clay-text">{{ text }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'ClayButton',
props: {
//
text: {
type: String,
default: '按钮'
},
// sm, md, lg
size: {
type: String,
default: 'md'
},
// primary, secondary, success, warning, error
variant: {
type: String,
default: 'primary'
},
//
outline: {
type: Boolean,
default: false
},
//
block: {
type: Boolean,
default: false
},
//
disabled: {
type: Boolean,
default: false
},
//
loading: {
type: Boolean,
default: false
},
// emoji
icon: {
type: String,
default: ''
},
//
customStyle: {
type: Object,
default: () => ({})
}
},
methods: {
handleTap(e) {
if (this.disabled || this.loading) return
this.$emit('tap', e)
}
}
}
</script>
<style lang="scss" scoped>
/* ============================================
Claymorphism 按钮组件
使用示例
<ClayButton text="确认" variant="primary" size="lg" @tap="handleConfirm" />
============================================ */
.clay-button-wrapper {
display: inline-flex;
&.clay-btn-block {
display: flex;
width: 100%;
}
&.clay-btn-disabled {
opacity: 0.5;
pointer-events: none;
}
}
.clay-button {
border-radius: 50rpx;
font-weight: 700;
position: relative;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8rpx;
/* Claymorphism 双阴影 - 创造凸起感 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.08),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
&.clay-btn-sm {
padding: 12rpx 32rpx;
font-size: 24rpx;
border-radius: 40rpx;
box-shadow:
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.06),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
}
&.clay-btn-md {
padding: 20rpx 48rpx;
font-size: 28rpx;
border-radius: 50rpx;
}
&.clay-btn-lg {
padding: 28rpx 64rpx;
font-size: 32rpx;
border-radius: 60rpx;
box-shadow:
10rpx 10rpx 20rpx rgba(0, 0, 0, 0.1),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.5),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.06);
}
/* 按钮变体 */
&.clay-btn-primary {
background: linear-gradient(145deg, #FF9500, #FF6B00);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 107, 0, 0.15),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
&.clay-btn-secondary {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
color: #1D1D1F;
}
&.clay-btn-success {
background: linear-gradient(145deg, #34C759, #30D158);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(52, 199, 89, 0.15),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
&.clay-btn-warning {
background: linear-gradient(145deg, #FF9F0A, #FF9500);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 159, 10, 0.15),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
&.clay-btn-error {
background: linear-gradient(145deg, #FF3B30, #FF453A);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 59, 48, 0.15),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
/* 轮廓样式 */
&.clay-btn-outline {
background: transparent;
border: 3rpx solid currentColor;
&.clay-btn-primary {
color: #FF6B00;
background: rgba(255, 107, 0, 0.05);
}
&.clay-btn-secondary {
color: #86868B;
background: rgba(134, 134, 139, 0.05);
}
}
/* 按下效果 */
&:active {
transform: scale(0.96);
box-shadow:
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.5),
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.08),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.3);
}
/* 加载状态 */
&.is-loading {
pointer-events: none;
}
}
/* 加载动画 */
.clay-loading {
display: flex;
align-items: center;
justify-content: center;
}
.loading-spinner {
width: 32rpx;
height: 32rpx;
border: 4rpx solid rgba(255, 255, 255, 0.3);
border-top-color: #fff;
border-radius: 50%;
animation: clay-spin 0.8s linear infinite;
}
@keyframes clay-spin {
to { transform: rotate(360deg); }
}
/* 图标 */
.clay-icon {
font-size: 32rpx;
line-height: 1;
}
/* 文字 */
.clay-text {
line-height: 1;
white-space: nowrap;
}
</style>

151
components/ClayCard.vue Normal file
View File

@ -0,0 +1,151 @@
<template>
<view
class="clay-card"
:class="[
`clay-card-${size}`,
{ 'clay-card-primary': variant === 'primary' },
{ 'clay-card-gold': variant === 'gold' },
{ 'clay-card-inset': inset },
customClass
]"
:style="customStyle"
@tap="handleTap"
>
<slot></slot>
</view>
</template>
<script>
export default {
name: 'ClayCard',
props: {
// sm, md, lg
size: {
type: String,
default: 'md'
},
// default, primary, gold
variant: {
type: String,
default: 'default'
},
//
inset: {
type: Boolean,
default: false
},
//
customClass: {
type: String,
default: ''
},
//
customStyle: {
type: Object,
default: () => ({})
}
},
methods: {
handleTap(e) {
this.$emit('tap', e)
}
}
}
</script>
<style lang="scss" scoped>
/* ============================================
Claymorphism 卡片组件
使用示例
<ClayCard size="lg" variant="primary">内容</ClayCard>
============================================ */
.clay-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: 24rpx;
position: relative;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
/* 外部双阴影 - 创造凸起效果 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.06),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03);
&.clay-card-sm {
border-radius: 16rpx;
box-shadow:
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.04),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03);
}
&.clay-card-md {
border-radius: 24rpx;
}
&.clay-card-lg {
border-radius: 32rpx;
box-shadow:
12rpx 12rpx 24rpx rgba(0, 0, 0, 0.08),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.7),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.85),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.04);
}
&:active {
transform: scale(0.98);
box-shadow:
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.06),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.6),
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.05),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.4);
}
}
/* 彩色粘土卡片 */
.clay-card-primary {
background: linear-gradient(145deg, #FF9500, #FF6B00);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 107, 0, 0.2),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
&:active {
box-shadow:
5rpx 5rpx 10rpx rgba(255, 107, 0, 0.25),
-5rpx -5rpx 10rpx rgba(255, 255, 255, 0.5),
inset 5rpx 5rpx 10rpx rgba(0, 0, 0, 0.15),
inset -5rpx -5rpx 10rpx rgba(255, 255, 255, 0.2);
}
}
.clay-card-gold {
background: linear-gradient(145deg, #FFD60A, #FF9F0A);
box-shadow:
10rpx 10rpx 20rpx rgba(255, 159, 10, 0.2),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
&:active {
box-shadow:
5rpx 5rpx 10rpx rgba(255, 159, 10, 0.25),
-5rpx -5rpx 10rpx rgba(255, 255, 255, 0.5),
inset 5rpx 5rpx 10rpx rgba(0, 0, 0, 0.15),
inset -5rpx -5rpx 10rpx rgba(255, 255, 255, 0.2);
}
}
/* 凹陷粘土卡片 (Inset) */
.clay-card-inset {
background: linear-gradient(145deg, #e8e8e8, #f8f8f8);
box-shadow:
inset 6rpx 6rpx 12rpx rgba(0, 0, 0, 0.08),
inset -6rpx -6rpx 12rpx rgba(255, 255, 255, 0.9);
}
</style>

282
components/ClayInput.vue Normal file
View File

@ -0,0 +1,282 @@
<template>
<view
class="clay-input-wrapper"
:class="[
`clay-input-${size}`,
{ 'clay-input-focused': isFocused },
{ 'clay-input-error': error },
{ 'clay-input-disabled': disabled },
customClass
]"
>
<!-- 前缀图标 -->
<view v-if="prefixIcon" class="clay-prefix-icon">
<text>{{ prefixIcon }}</text>
</view>
<!-- 输入框 -->
<input
class="clay-input-field"
:type="type"
:value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="maxlength"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@confirm="handleConfirm"
/>
<!-- 后缀图标/按钮 -->
<view v-if="suffixIcon || clearable" class="clay-suffix-icon" @tap="handleSuffixTap">
<text v-if="showClearButton" class="clear-button">×</text>
<text v-else-if="suffixIcon">{{ suffixIcon }}</text>
</view>
<!-- 错误提示 -->
<view v-if="error && errorText" class="clay-error-text">
<text>{{ errorText }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'ClayInput',
props: {
// v-model
modelValue: {
type: [String, Number],
default: ''
},
//
type: {
type: String,
default: 'text'
},
//
placeholder: {
type: String,
default: '请输入'
},
// sm, md, lg
size: {
type: String,
default: 'md'
},
//
disabled: {
type: Boolean,
default: false
},
//
error: {
type: Boolean,
default: false
},
//
errorText: {
type: String,
default: ''
},
//
maxlength: {
type: [String, Number],
default: 140
},
//
clearable: {
type: Boolean,
default: false
},
// emoji
prefixIcon: {
type: String,
default: ''
},
// emoji
suffixIcon: {
type: String,
default: ''
},
//
customClass: {
type: String,
default: ''
}
},
data() {
return {
isFocused: false
}
},
computed: {
showClearButton() {
return this.clearable && this.modelValue && !this.disabled
}
},
methods: {
handleInput(e) {
this.$emit('update:modelValue', e.detail.value)
this.$emit('input', e.detail.value)
},
handleFocus(e) {
this.isFocused = true
this.$emit('focus', e)
},
handleBlur(e) {
this.isFocused = false
this.$emit('blur', e)
},
handleConfirm(e) {
this.$emit('confirm', e)
},
handleSuffixTap() {
if (this.showClearButton) {
this.$emit('update:modelValue', '')
this.$emit('clear')
} else if (this.suffixIcon) {
this.$emit('suffix-tap')
}
}
}
}
</script>
<style lang="scss" scoped>
/* ============================================
Claymorphism 输入框组件
使用示例
<ClayInput v-model="value" placeholder="请输入" prefixIcon="🔍" />
============================================ */
.clay-input-wrapper {
position: relative;
display: flex;
align-items: center;
background: linear-gradient(145deg, #ffffff, #f5f5f5);
border-radius: 24rpx;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
/* Claymorphism 双阴影 */
box-shadow:
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.06),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.9),
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.04),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7);
&.clay-input-sm {
border-radius: 20rpx;
padding: 16rpx 24rpx;
.clay-input-field {
font-size: 24rpx;
}
}
&.clay-input-md {
border-radius: 24rpx;
padding: 24rpx 28rpx;
.clay-input-field {
font-size: 28rpx;
}
}
&.clay-input-lg {
border-radius: 28rpx;
padding: 28rpx 32rpx;
.clay-input-field {
font-size: 32rpx;
}
}
/* 聚焦状态 */
&.clay-input-focused {
box-shadow:
inset 6rpx 6rpx 12rpx rgba(255, 107, 0, 0.08),
inset -6rpx -6rpx 12rpx rgba(255, 255, 255, 0.85),
6rpx 6rpx 12rpx rgba(255, 107, 0, 0.1),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.6);
}
/* 错误状态 */
&.clay-input-error {
box-shadow:
inset 4rpx 4rpx 8rpx rgba(255, 59, 48, 0.1),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.9),
4rpx 4rpx 8rpx rgba(255, 59, 48, 0.1),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7);
}
/* 禁用状态 */
&.clay-input-disabled {
opacity: 0.5;
background: linear-gradient(145deg, #f0f0f0, #e8e8e8);
}
}
.clay-input-field {
flex: 1;
border: none;
background: transparent;
color: #1D1D1F;
outline: none;
line-height: 1.5;
&::placeholder {
color: #C7C7CC;
}
}
/* 前缀图标 */
.clay-prefix-icon {
margin-right: 16rpx;
font-size: 32rpx;
opacity: 0.6;
}
/* 后缀图标 */
.clay-suffix-icon {
margin-left: 16rpx;
font-size: 32rpx;
opacity: 0.6;
cursor: pointer;
transition: all 0.2s;
&:active {
opacity: 1;
transform: scale(1.1);
}
.clear-button {
display: inline-block;
width: 40rpx;
height: 40rpx;
line-height: 36rpx;
text-align: center;
font-size: 40rpx;
color: #86868B;
background: linear-gradient(145deg, #e8e8e8, #f8f8f8);
border-radius: 50%;
/* Claymorphism 凹陷效果 */
box-shadow:
inset 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1),
inset -2rpx -2rpx 4rpx rgba(255, 255, 255, 0.9);
}
}
/* 错误提示 */
.clay-error-text {
position: absolute;
bottom: -40rpx;
left: 0;
font-size: 22rpx;
color: #FF3B30;
white-space: nowrap;
}
</style>

View File

@ -1,23 +1,31 @@
<template> <template>
<!-- #ifndef MP-TOUTIAO --> <!-- #ifndef MP-TOUTIAO -->
<view class="app-tab-bar"> <view class="clay-tab-bar">
<view class="tab-bar-item" @tap="switchTab('pages/index/index')"> <view class="tab-bar-item" @tap="switchTab('pages/index/index')" :class="{ active: selected === 0 }">
<view class="tab-icon-wrapper" :class="{ 'icon-active': selected === 0 }">
<image class="tab-icon" :src="selected === 0 ? '/static/tab/home_active.png' : '/static/tab/home.png'" mode="aspectFit"></image> <image class="tab-icon" :src="selected === 0 ? '/static/tab/home_active.png' : '/static/tab/home.png'" mode="aspectFit"></image>
</view>
<text class="tab-text" :class="{ active: selected === 0 }">首页</text> <text class="tab-text" :class="{ active: selected === 0 }">首页</text>
</view> </view>
<view class="tab-bar-item" @tap="switchTab('pages/shop/index')"> <view class="tab-bar-item" @tap="switchTab('pages/shop/index')" :class="{ active: selected === 1 }">
<view class="tab-icon-wrapper" :class="{ 'icon-active': selected === 1 }">
<image class="tab-icon" :src="selected === 1 ? '/static/tab/shop_active.png' : '/static/tab/shop.png'" mode="aspectFit"></image> <image class="tab-icon" :src="selected === 1 ? '/static/tab/shop_active.png' : '/static/tab/shop.png'" mode="aspectFit"></image>
</view>
<text class="tab-text" :class="{ active: selected === 1 }">商城</text> <text class="tab-text" :class="{ active: selected === 1 }">商城</text>
</view> </view>
<view class="tab-bar-item" @tap="switchTab('pages/cabinet/index')"> <view class="tab-bar-item" @tap="switchTab('pages/cabinet/index')" :class="{ active: selected === 2 }">
<view class="tab-icon-wrapper" :class="{ 'icon-active': selected === 2 }">
<image class="tab-icon" :src="selected === 2 ? '/static/tab/box_active.png' : '/static/tab/box.png'" mode="aspectFit"></image> <image class="tab-icon" :src="selected === 2 ? '/static/tab/box_active.png' : '/static/tab/box.png'" mode="aspectFit"></image>
</view>
<text class="tab-text" :class="{ active: selected === 2 }">盒柜</text> <text class="tab-text" :class="{ active: selected === 2 }">盒柜</text>
</view> </view>
<view class="tab-bar-item" @tap="switchTab('pages/mine/index')"> <view class="tab-bar-item" @tap="switchTab('pages/mine/index')" :class="{ active: selected === 3 }">
<view class="tab-icon-wrapper" :class="{ 'icon-active': selected === 3 }">
<image class="tab-icon" :src="selected === 3 ? '/static/tab/profile_active.png' : '/static/tab/profile.png'" mode="aspectFit"></image> <image class="tab-icon" :src="selected === 3 ? '/static/tab/profile_active.png' : '/static/tab/profile.png'" mode="aspectFit"></image>
</view>
<text class="tab-text" :class="{ active: selected === 3 }">我的</text> <text class="tab-text" :class="{ active: selected === 3 }">我的</text>
</view> </view>
</view> </view>
@ -63,19 +71,30 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
/* #ifndef MP-TOUTIAO */ /* #ifndef MP-TOUTIAO */
.app-tab-bar { /* ============================================
Claymorphism 底部导航栏
粘土风格 - 柔和浮感 & 双阴影效果
============================================ */
.clay-tab-bar {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
height: 100rpx; background: linear-gradient(145deg, #ffffff, #f5f5f5);
background: #FFFFFF; border-top: 1px solid rgba(255, 255, 255, 0.6);
border-top: 1rpx solid #E5E5E5;
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
padding-bottom: env(safe-area-inset-bottom); padding: 12rpx 0 calc(12rpx + env(safe-area-inset-bottom));
z-index: 999; z-index: 999;
/* Claymorphism 双阴影 - 创造浮起效果 */
box-shadow:
0 -8rpx 16rpx rgba(0, 0, 0, 0.04),
0 8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.02);
} }
.tab-bar-item { .tab-bar-item {
@ -84,22 +103,68 @@ export default {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; padding: 8rpx 0;
position: relative;
transition: all 0.3s ease;
&.active {
.tab-icon-wrapper {
/* 选中状态 - Claymorphism 凸起效果 */
background: linear-gradient(145deg, #FF9500, #FF6B00);
box-shadow:
6rpx 6rpx 12rpx rgba(255, 107, 0, 0.2),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.7),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.4),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.1);
transform: translateY(-4rpx);
}
.tab-text {
color: #FF6B00;
font-weight: 700;
}
}
&:active {
transform: scale(0.95);
}
}
/* 图标包装器 - Claymorphism 圆形徽章 */
.tab-icon-wrapper {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 6rpx;
background: linear-gradient(145deg, #f8f8f8, #e8e8e8);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
/* 默认状态 - 凹陷效果 */
box-shadow:
inset 3rpx 3rpx 6rpx rgba(0, 0, 0, 0.08),
inset -3rpx -3rpx 6rpx rgba(255, 255, 255, 0.9);
} }
.tab-icon { .tab-icon {
width: 48rpx; width: 40rpx;
height: 48rpx; height: 40rpx;
margin-bottom: 4rpx; transition: all 0.3s ease;
} }
.tab-text { .tab-text {
font-size: 22rpx; font-size: 22rpx;
color: #7A7E83; color: #86868B;
transition: all 0.3s ease;
font-weight: 500;
}
&.active { /* 选中状态的图标颜色调整 */
color: #007AFF; .tab-bar-item.active .tab-icon {
} filter: brightness(0) invert(1);
} }
/* #endif */ /* #endif */
</style> </style>

View File

@ -0,0 +1,69 @@
/**
* Claymorphism 组件库导出
*
* 使用方式 pages.json 中配置 easycom
* {
* "easycom": {
* "autoscan": true,
* "custom": {
* "^Clay(A.*)": "@/components/Clay$1.vue"
* }
* }
* }
*
* 或手动导入
* import ClayCard from '@/components/ClayCard.vue'
* import ClayButton from '@/components/ClayButton.vue'
* import ClayInput from '@/components/ClayInput.vue'
*/
export { default as ClayCard } from './ClayCard.vue'
export { default as ClayButton } from './ClayButton.vue'
export { default as ClayInput } from './ClayInput.vue'
/* ============================================
Claymorphism 设计系统说明
🎨 核心特点
1. 双阴影效果外部 + 内部创造立体浮感
2. 柔和的渐变背景
3. 圆润的边角设计
4. 有机感的按压动画
📦 组件列表
- ClayCard: 粘土风格卡片
- ClayButton: 粘土风格按钮
- ClayInput: 粘土风格输入框
🎯 使用示例
<!-- 卡片 -->
<ClayCard size="lg" variant="primary" @tap="handleTap">
<text>卡片内容</text>
</ClayCard>
<!-- 按钮 -->
<ClayButton
text="确认"
variant="primary"
size="lg"
:loading="isLoading"
@tap="handleConfirm"
/>
<!-- 输入框 -->
<ClayInput
v-model="inputValue"
placeholder="请输入内容"
prefixIcon="🔍"
:clearable="true"
@confirm="handleSearch"
/>
🎨 样式变量uni.scss
- $clay-shadow-sm/md/lg/xl: 阴影层级
- $clay-bg-light: #FAFAFA
- $clay-bg-white: #FFFFFF
- $clay-border: rgba(255, 255, 255, 0.8)
============================================ */

260
docs/CLAYMORPHISM.md Normal file
View File

@ -0,0 +1,260 @@
# Claymorphism UI 优化文档
## 🎨 设计系统概述
### 什么是 Claymorphism粘土拟态
Claymorphism 是一种结合了 **Neumorphism新拟态****3D 粘土质感** 的设计风格,具有以下特点:
- ✨ **双阴影效果**:外部阴影 + 内部阴影创造立体浮感
- 🎨 **柔和渐变**:使用 145deg 渐变创造有机感
- 🔄 **圆润边角**:大圆角设计传递柔和友好感
- 💫 **有机动画**:平滑的过渡和按压反馈
- 🌈 **高对比度**:保持可访问性的同时提供视觉吸引力
---
## 📦 组件库
### 核心组件
| 组件 | 文件路径 | 功能 |
|------|---------|------|
| **ClayCard** | `/components/ClayCard.vue` | 粘土风格卡片 |
| **ClayButton** | `/components/ClayButton.vue` | 粘土风格按钮 |
| **ClayInput** | `/components/ClayInput.vue` | 粘土风格输入框 |
### 使用示例
```vue
<template>
<view>
<!-- 卡片组件 -->
<ClayCard size="lg" variant="primary" @tap="handleTap">
<text>这是一张粘土卡片</text>
</ClayCard>
<!-- 按钮组件 -->
<ClayButton
text="确认操作"
variant="primary"
size="lg"
:loading="isLoading"
@tap="handleConfirm"
/>
<!-- 输入框组件 -->
<ClayInput
v-model="searchText"
placeholder="搜索内容..."
prefixIcon="🔍"
:clearable="true"
@confirm="handleSearch"
/>
</view>
</template>
<script>
import { ClayCard, ClayButton, ClayInput } from '@/components/clay-components.js'
export default {
components: { ClayCard, ClayButton, ClayInput },
data() {
return {
searchText: '',
isLoading: false
}
},
methods: {
handleTap() { console.log('卡片被点击') },
async handleConfirm() {
this.isLoading = true
// 执行操作...
},
handleSearch(val) { console.log('搜索:', val) }
}
}
</script>
```
---
## 🎯 组件 Props
### ClayCard
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `size` | String | `'md'` | 尺寸:`sm`, `md`, `lg` |
| `variant` | String | `'default'` | 变体:`default`, `primary`, `gold` |
| `inset` | Boolean | `false` | 是否凹陷效果 |
| `customClass` | String | `''` | 自定义类名 |
| `customStyle` | Object | `{}` | 自定义样式 |
### ClayButton
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `text` | String | `'按钮'` | 按钮文字 |
| `size` | String | `'md'` | 尺寸:`sm`, `md`, `lg` |
| `variant` | String | `'primary'` | 变体:`primary`, `secondary`, `success`, `warning`, `error` |
| `outline` | Boolean | `false` | 是否轮廓样式 |
| `block` | Boolean | `false` | 是否块级按钮 |
| `disabled` | Boolean | `false` | 是否禁用 |
| `loading` | Boolean | `false` | 是否加载中 |
| `icon` | String | `''` | 图标emoji |
| `customStyle` | Object | `{}` | 自定义样式 |
### ClayInput
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `modelValue` | String/Number | `''` | v-model 绑定值 |
| `type` | String | `'text'` | 输入框类型 |
| `placeholder` | String | `'请输入'` | 占位符 |
| `size` | String | `'md'` | 尺寸:`sm`, `md`, `lg` |
| `disabled` | Boolean | `false` | 是否禁用 |
| `error` | Boolean | `false` | 是否错误状态 |
| `errorText` | String | `''` | 错误提示 |
| `clearable` | Boolean | `false` | 是否可清空 |
| `prefixIcon` | String | `''` | 前缀图标emoji |
| `suffixIcon` | String | `''` | 后缀图标emoji |
---
## 🎨 样式变量
`uni.scss` 中定义的核心变量:
```scss
/* Claymorphism 阴影层级 */
$clay-shadow-sm: (...)
$clay-shadow-md: (...)
$clay-shadow-lg: (...)
$clay-shadow-xl: (...)
/* 颜色变量 */
$clay-bg-light: #FAFAFA;
$clay-bg-white: #FFFFFF;
$clay-bg-soft: rgba(255, 255, 255, 0.85);
$clay-border: rgba(255, 255, 255, 0.8);
```
---
## 🔧 已优化的页面
### ✅ 首页 (`pages/index/index.vue`)
- Banner 轮播卡片
- 通知栏
- 玩法分类卡片
- 活动列表项
### ✅ 底部导航栏 (`components/app-tab-bar.vue`)
- 导航栏背景
- 图标容器(圆形徽章)
- 选中状态动画
### ✅ 个人中心 (`pages/mine/index.vue`)
- 用户信息卡片
- 头像凹陷效果
- 统计数据卡片
- 功能图标容器
---
## 🚀 快速开始
### 1. 配置 easycom推荐
`pages.json` 中添加:
```json
{
"easycom": {
"autoscan": true,
"custom": {
"^Clay(A.*)": "@/components/Clay$1.vue"
}
}
}
```
### 2. 直接使用组件
```vue
<template>
<ClayCard size="lg">内容</ClayCard>
<ClayButton text="按钮" variant="primary" />
<ClayInput v-model="value" placeholder="输入..." />
</template>
```
### 3. 使用全局样式类
```vue
<template>
<!-- 粘土卡片 -->
<view class="clay-card">内容</view>
<!-- 粘土按钮 -->
<view class="clay-btn clay-btn-primary clay-btn-md">按钮</view>
<!-- 凹陷效果 -->
<view class="clay-card-inset">凹陷卡片</view>
</template>
```
---
## 🎨 设计原则
### ✅ 推荐做法
1. **保持一致性**:在整个应用中统一使用 Claymorphism 组件
2. **适度使用**:不要过度使用,保持界面呼吸感
3. **注意对比度**:确保文字与背景有足够的对比度
4. **动画平滑**:使用 `cubic-bezier(0.4, 0, 0.2, 1)` 缓动函数
5. **圆角统一**:卡片 24-32rpx按钮 50-60rpx圆角
### ❌ 避免做法
1. **不要混合多种拟态风格**
2. **不要使用过多的彩色渐变**
3. **不要忽略深色模式适配**
4. **不要在小元素上使用粘土效果**
---
## 📱 兼容性
| 平台 | 支持情况 |
|------|----------|
| 微信小程序 | ✅ 完全支持 |
| 抖音小程序 | ✅ 完全支持 |
| H5 | ✅ 完全支持 |
| App | ✅ 完全支持 |
---
## 🔮 后续计划
- [ ] 添加更多组件ClaySwitch、ClaySlider、ClayModal
- [ ] 深色模式适配
- [ ] 动画效果增强
- [ ] 性能优化
---
## 📝 更新日志
### v1.0.0 (2025-02-05)
- ✅ 初始化 Claymorphism 设计系统
- ✅ 创建核心组件ClayCard、ClayButton、ClayInput
- ✅ 优化首页、个人中心、底部导航栏
- ✅ 添加全局样式变量和 Mixins
---
**设计团队**: Z Code AI Studio
**最后更新**: 2025-02-05

View File

@ -448,7 +448,7 @@ export default {
z-index: 1; z-index: 1;
} }
/* Banner Container (Modern Floating) */ /* Banner Container - Claymorphism Style */
.banner-container { .banner-container {
padding: $spacing-sm $spacing-lg $spacing-xl; padding: $spacing-sm $spacing-lg $spacing-xl;
position: relative; position: relative;
@ -462,17 +462,27 @@ export default {
.banner-card { .banner-card {
height: 100%; height: 100%;
margin: 0 4rpx; margin: 0 4rpx;
border-radius: 32rpx; border-radius: 40rpx;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
transform: scale(0.96); transform: scale(0.96);
transition: all 0.5s $ease-out; transition: all 0.5s $ease-out;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.08);
/* Claymorphism 双阴影效果 */
box-shadow:
12rpx 12rpx 24rpx rgba(0, 0, 0, 0.08),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.7),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.5),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.05);
} }
.banner-card.active { .banner-card.active {
transform: scale(1); transform: scale(1);
box-shadow: $shadow-float; box-shadow:
16rpx 16rpx 32rpx rgba(255, 107, 0, 0.12),
-16rpx -16rpx 32rpx rgba(255, 255, 255, 0.6),
inset 6rpx 6rpx 12rpx rgba(255, 255, 255, 0.4),
inset -6rpx -6rpx 12rpx rgba(0, 0, 0, 0.06);
} }
.banner-image { .banner-image {
@ -541,17 +551,23 @@ export default {
background: $brand-primary; background: $brand-primary;
} }
/* Notice Bar V2 (Minimalist) */ /* Notice Bar - Claymorphism Style */
.notice-bar-v2 { .notice-bar-v2 {
margin: 0 $spacing-lg $spacing-xl; margin: 0 $spacing-lg $spacing-xl;
background: rgba(255, 255, 255, 0.7); background: linear-gradient(145deg, #ffffff, #f5f5f5);
backdrop-filter: blur(10rpx); border-radius: 40rpx;
border-radius: 32rpx;
padding: 24rpx 32rpx; padding: 24rpx 32rpx;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 20rpx; gap: 20rpx;
box-shadow: $shadow-sm;
/* Claymorphism 双阴影 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.06),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03);
border: 1px solid rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.6);
} }
@ -616,19 +632,31 @@ export default {
height: 190rpx; height: 190rpx;
} }
/* 玩法卡片 - Claymorphism Style */
.game-card-large { .game-card-large {
flex: 1; flex: 1;
border-radius: $radius-lg; border-radius: $radius-xl;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
padding: 22rpx; padding: 22rpx;
box-shadow: $shadow-card;
transition: transform 0.2s; transition: transform 0.2s;
/* Claymorphism 阴影 */
box-shadow:
12rpx 12rpx 24rpx rgba(0, 0, 0, 0.1),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.6),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.3),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
} }
.game-card-large:active { .game-card-large:active {
transform: scale(0.98); transform: scale(0.96);
box-shadow:
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.12),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.4),
inset 6rpx 6rpx 12rpx rgba(0, 0, 0, 0.15),
inset -6rpx -6rpx 12rpx rgba(255, 255, 255, 0.2);
} }
/* 下排 */ /* 下排 */
@ -640,20 +668,31 @@ export default {
.game-card-small { .game-card-small {
flex: 1; flex: 1;
border-radius: $radius-md; border-radius: $radius-lg;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
padding: 16rpx; padding: 16rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
box-shadow: $shadow-sm;
background: white;
transition: all 0.2s; transition: all 0.2s;
/* Claymorphism 阴影 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.08),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.7),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
background: linear-gradient(145deg, #ffffff, #f8f8f8);
border: 1px solid rgba(255, 255, 255, 0.6);
} }
.game-card-small:active { .game-card-small:active {
transform: scale(0.96); transform: scale(0.94);
box-shadow: none; box-shadow:
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.5),
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.08),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.3);
} }
/* 内容样式 - 大卡片 */ /* 内容样式 - 大卡片 */
@ -748,7 +787,7 @@ export default {
.card-more .card-title-small { color: $text-sub; } .card-more .card-title-small { color: $text-sub; }
/* 推荐活动列表 */ /* 推荐活动列表 - Claymorphism Style */
.activity-section { .activity-section {
padding: 0 $spacing-lg; padding: 0 $spacing-lg;
animation: fadeInUp 0.6s ease-out 0.3s backwards; animation: fadeInUp 0.6s ease-out 0.3s backwards;
@ -763,20 +802,29 @@ export default {
} }
.activity-item { .activity-item {
background: rgba(255, 255, 255, 0.7); background: linear-gradient(145deg, #ffffff, #f8f8f8);
backdrop-filter: blur(10rpx);
border-radius: $radius-xl; border-radius: $radius-xl;
overflow: hidden; overflow: hidden;
box-shadow: $shadow-sm;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: all 0.3s ease; transition: all 0.3s ease;
/* Claymorphism 阴影 */
box-shadow:
10rpx 10rpx 20rpx rgba(0, 0, 0, 0.06),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.8),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.03);
border: 1px solid rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.6);
} }
.activity-item:active { .activity-item:active {
transform: translateY(4rpx); transform: translateY(4rpx) scale(0.98);
box-shadow: none; box-shadow:
5rpx 5rpx 10rpx rgba(0, 0, 0, 0.08),
-5rpx -5rpx 10rpx rgba(255, 255, 255, 0.5),
inset 5rpx 5rpx 10rpx rgba(0, 0, 0, 0.05),
inset -5rpx -5rpx 10rpx rgba(255, 255, 255, 0.3);
} }
.activity-thumb-box { .activity-thumb-box {

View File

@ -1533,12 +1533,16 @@ export default {
50% { transform: translate(30rpx, 50rpx); } 50% { transform: translate(30rpx, 50rpx); }
} }
/* 通用毛玻璃卡片 */ /* 通用毛玻璃卡片 - Claymorphism */
.glass-card { .glass-card {
background: $bg-glass; background: linear-gradient(145deg, #ffffff, #f8f8f8);
backdrop-filter: blur(20rpx); backdrop-filter: blur(20rpx);
border: 1px solid rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.6);
box-shadow: $shadow-card; box-shadow:
10rpx 10rpx 20rpx rgba(0, 0, 0, 0.06),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.8),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.03);
border-radius: $radius-lg; border-radius: $radius-lg;
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -1553,9 +1557,14 @@ export default {
.user-info-card { .user-info-card {
padding: $spacing-xl; padding: $spacing-xl;
background: linear-gradient(135deg, rgba(255,255,255,0.95), rgba(255,255,255,0.8)); background: linear-gradient(145deg, #ffffff, #f8f8f8);
box-shadow: $shadow-float; box-shadow:
border: 1px solid rgba(255,255,255,0.8); 12rpx 12rpx 24rpx rgba(0, 0, 0, 0.06),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.7),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.85),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.03);
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: $radius-xl;
} }
.user-main { .user-main {
@ -1569,7 +1578,11 @@ export default {
background: $bg-card; background: $bg-card;
border-radius: 50%; border-radius: 50%;
border: 4rpx solid $bg-card; border: 4rpx solid $bg-card;
box-shadow: $shadow-sm;
/* Claymorphism 凹陷效果 */
box-shadow:
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.9);
margin-right: 24rpx; margin-right: 24rpx;
} }
@ -1588,13 +1601,19 @@ export default {
right: 0; right: 0;
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
background: linear-gradient(135deg, $brand-primary, $brand-secondary); background: linear-gradient(145deg, #FF9500, #FF6B00);
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 3rpx solid $bg-card; border: 3rpx solid $bg-card;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
/* Claymorphism 凸起效果 */
box-shadow:
4rpx 4rpx 8rpx rgba(255, 107, 0, 0.2),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.4),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.1);
.edit-icon { .edit-icon {
font-size: 20rpx; font-size: 20rpx;
@ -1750,10 +1769,18 @@ export default {
transform: rotate(-15deg); transform: rotate(-15deg);
} }
/* 常用功能 & 订单 */ /* 常用功能 & 订单 - Claymorphism */
.section-card { .section-card {
margin: 0 $spacing-lg $spacing-lg; margin: 0 $spacing-lg $spacing-lg;
padding: 30rpx; padding: 30rpx;
background: linear-gradient(145deg, #ffffff, #f8f8f8);
box-shadow:
10rpx 10rpx 20rpx rgba(0, 0, 0, 0.05),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.8),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.03);
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: $radius-lg;
} }
.section-header { .section-header {
display: flex; justify-content: space-between; align-items: center; display: flex; justify-content: space-between; align-items: center;
@ -1774,14 +1801,22 @@ export default {
} }
.icon-wrapper, .menu-icon-box { .icon-wrapper, .menu-icon-box {
width: 80rpx; height: 80rpx; width: 80rpx; height: 80rpx;
background: $bg-secondary; background: linear-gradient(145deg, #f8f8f8, #e8e8e8);
border-radius: 24rpx; border-radius: 24rpx;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
margin-bottom: 12rpx; margin-bottom: 12rpx;
transition: all 0.3s; transition: all 0.3s;
/* Claymorphism 凹陷效果 */
box-shadow:
inset 3rpx 3rpx 6rpx rgba(0, 0, 0, 0.06),
inset -3rpx -3rpx 6rpx rgba(255, 255, 255, 0.9);
} }
.grid-item:active .icon-wrapper, .menu-item:active .menu-icon-box { .grid-item:active .icon-wrapper, .menu-item:active .menu-icon-box {
background: $uni-bg-color-hover; background: linear-gradient(145deg, #e8e8e8, #d8d8d8);
box-shadow:
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7);
} }
.grid-icon-img, .menu-icon-img { width: 44rpx; height: 44rpx; } .grid-icon-img, .menu-icon-img { width: 44rpx; height: 44rpx; }
.grid-label, .menu-label { font-size: $font-sm; color: $text-main; } .grid-label, .menu-label { font-size: $font-sm; color: $text-main; }

173
uni.scss
View File

@ -185,6 +185,44 @@ $uni-font-size-paragraph: 15px;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
/* ============================================
💎 Claymorphism 设计系统 (2025)
粘土拟态风格 - 柔和圆润立体浮感
============================================ */
/* 🎨 Claymorphism 核心阴影 */
$clay-shadow-sm: (
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.04),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03)
);
$clay-shadow-md: (
12rpx 12rpx 24rpx rgba(0, 0, 0, 0.06),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.7),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.85),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.04)
);
$clay-shadow-lg: (
20rpx 20rpx 40rpx rgba(0, 0, 0, 0.08),
-20rpx -20rpx 40rpx rgba(255, 255, 255, 0.6),
inset 6rpx 6rpx 12rpx rgba(255, 255, 255, 0.8),
inset -6rpx -6rpx 12rpx rgba(0, 0, 0, 0.05)
);
$clay-shadow-xl: (
28rpx 28rpx 56rpx rgba(0, 0, 0, 0.1),
-28rpx -28rpx 56rpx rgba(255, 255, 255, 0.5),
inset 8rpx 8rpx 16rpx rgba(255, 255, 255, 0.75),
inset -8rpx -8rpx 16rpx rgba(0, 0, 0, 0.06)
);
/* Claymorphism 颜色变量 */
$clay-bg-light: #FAFAFA;
$clay-bg-white: #FFFFFF;
$clay-bg-soft: rgba(255, 255, 255, 0.85);
$clay-border: rgba(255, 255, 255, 0.8);
$clay-border-dark: rgba(0, 0, 0, 0.04);
/* ============================================ /* ============================================
💎 核心公共 UI (Premium UI 6.0) 💎 核心公共 UI (Premium UI 6.0)
============================================ */ ============================================ */
@ -240,6 +278,141 @@ $uni-font-size-paragraph: 15px;
z-index: 1; z-index: 1;
} }
/* ============================================
🟤 Claymorphism 卡片组件
============================================ */
/* 基础粘土卡片 */
.clay-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-lg;
position: relative;
transition: all $transition-normal $ease-out;
/* 外部双阴影 - 创造凸起效果 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.06),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03);
&.clay-card-sm {
border-radius: $radius-md;
box-shadow:
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.04),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.9),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.03);
}
&.clay-card-lg {
border-radius: $radius-xl;
box-shadow:
12rpx 12rpx 24rpx rgba(0, 0, 0, 0.08),
-12rpx -12rpx 24rpx rgba(255, 255, 255, 0.7),
inset 4rpx 4rpx 8rpx rgba(255, 255, 255, 0.85),
inset -4rpx -4rpx 8rpx rgba(0, 0, 0, 0.04);
}
&:active {
transform: scale(0.98);
box-shadow:
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.06),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.6),
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.05),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.4);
}
}
/* 彩色粘土卡片 */
.clay-card-primary {
background: linear-gradient(145deg, $brand-primary-light, $brand-primary);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 107, 0, 0.2),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
&.clay-card-gold {
background: linear-gradient(145deg, #FFD60A, #FF9F0A);
box-shadow:
10rpx 10rpx 20rpx rgba(255, 159, 10, 0.2),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
}
/* 凹陷粘土卡片 (Inset) */
.clay-card-inset {
background: linear-gradient(145deg, #e8e8e8, #f8f8f8);
box-shadow:
inset 6rpx 6rpx 12rpx rgba(0, 0, 0, 0.08),
inset -6rpx -6rpx 12rpx rgba(255, 255, 255, 0.9);
}
/* ============================================
🔘 Claymorphism 按钮组件
============================================ */
.clay-btn {
border-radius: $radius-round;
font-weight: 700;
position: relative;
transition: all $transition-normal $ease-out;
border: none;
/* 外部双阴影 - 创造凸起感 */
box-shadow:
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.08),
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
&.clay-btn-primary {
background: linear-gradient(145deg, $brand-primary-light, $brand-primary);
color: #fff;
box-shadow:
10rpx 10rpx 20rpx rgba(255, 107, 0, 0.15),
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
}
&.clay-btn-secondary {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
color: $text-main;
}
&.clay-btn-sm {
padding: 12rpx 32rpx;
font-size: $font-sm;
border-radius: 40rpx;
}
&.clay-btn-md {
padding: 20rpx 48rpx;
font-size: $font-md;
border-radius: 50rpx;
}
&.clay-btn-lg {
padding: 28rpx 64rpx;
font-size: $font-lg;
border-radius: 60rpx;
}
&:active {
transform: scale(0.96);
box-shadow:
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.5),
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.08),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.3);
}
}
/* 3. 通用功能按钮 */ /* 3. 通用功能按钮 */
.btn-primary { .btn-primary {
background: $gradient-brand; background: $gradient-brand;