190 lines
4.6 KiB
Vue
190 lines
4.6 KiB
Vue
<template>
|
|
<view class="vsm" :class="[`vsm--${size}`, { 'vsm--active': active, 'vsm--on-dark': onDark }]">
|
|
<view class="vsm-body">
|
|
<view class="vsm-leaf" aria-hidden="true" />
|
|
<view class="vsm-face">
|
|
<view class="vsm-eye vsm-eye--l" />
|
|
<view class="vsm-eye vsm-eye--r" />
|
|
<view class="vsm-blush vsm-blush--l" />
|
|
<view class="vsm-blush vsm-blush--r" />
|
|
<view class="vsm-mouth" />
|
|
</view>
|
|
<view v-if="active" class="vsm-waves" aria-hidden="true">
|
|
<view class="vsm-wave vsm-wave--1" />
|
|
<view class="vsm-wave vsm-wave--2" />
|
|
<view class="vsm-wave vsm-wave--3" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
defineProps({
|
|
/** 正在按住说话 / 录音中 */
|
|
active: { type: Boolean, default: false },
|
|
/** 深色条背景上使用浅色卡通 */
|
|
onDark: { type: Boolean, default: false },
|
|
size: { type: String, default: 'md' }
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.vsm {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.vsm--md { width: 72rpx; height: 72rpx; }
|
|
.vsm--sm { width: 64rpx; height: 64rpx; }
|
|
|
|
.vsm-body {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.vsm-leaf {
|
|
position: absolute;
|
|
top: 2rpx;
|
|
left: 50%;
|
|
width: 16rpx;
|
|
height: 16rpx;
|
|
margin-left: -8rpx;
|
|
border-radius: 0 100% 0 100%;
|
|
background: #34d399;
|
|
transform: rotate(-18deg);
|
|
z-index: 2;
|
|
}
|
|
.vsm--on-dark .vsm-leaf {
|
|
background: #a7f3d0;
|
|
}
|
|
|
|
.vsm-face {
|
|
position: relative;
|
|
width: 52rpx;
|
|
height: 52rpx;
|
|
border-radius: 50%;
|
|
background: #fef9c3;
|
|
border: 3rpx solid #047857;
|
|
box-shadow: 0 4rpx 10rpx rgba(4, 120, 87, 0.18);
|
|
z-index: 1;
|
|
}
|
|
.vsm--sm .vsm-face {
|
|
width: 46rpx;
|
|
height: 46rpx;
|
|
}
|
|
.vsm--on-dark .vsm-face {
|
|
background: #fffbeb;
|
|
border-color: rgba(255, 255, 255, 0.85);
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
.vsm-eye {
|
|
position: absolute;
|
|
top: 16rpx;
|
|
width: 6rpx;
|
|
height: 8rpx;
|
|
border-radius: 50%;
|
|
background: #064e3b;
|
|
}
|
|
.vsm--sm .vsm-eye { top: 14rpx; width: 5rpx; height: 7rpx; }
|
|
.vsm--on-dark .vsm-eye { background: #134e4a; }
|
|
.vsm-eye--l { left: 12rpx; }
|
|
.vsm-eye--r { right: 12rpx; }
|
|
.vsm--sm .vsm-eye--l { left: 10rpx; }
|
|
.vsm--sm .vsm-eye--r { right: 10rpx; }
|
|
|
|
.vsm-blush {
|
|
position: absolute;
|
|
top: 24rpx;
|
|
width: 10rpx;
|
|
height: 6rpx;
|
|
border-radius: 50%;
|
|
background: rgba(251, 113, 133, 0.45);
|
|
opacity: 0.85;
|
|
}
|
|
.vsm--sm .vsm-blush { top: 21rpx; width: 8rpx; height: 5rpx; }
|
|
.vsm-blush--l { left: 6rpx; }
|
|
.vsm-blush--r { right: 6rpx; }
|
|
.vsm--on-dark .vsm-blush { background: rgba(255, 255, 255, 0.35); }
|
|
|
|
.vsm-mouth {
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: 10rpx;
|
|
width: 18rpx;
|
|
height: 8rpx;
|
|
margin-left: -9rpx;
|
|
border-radius: 0 0 18rpx 18rpx;
|
|
background: #047857;
|
|
transform-origin: center top;
|
|
}
|
|
.vsm--sm .vsm-mouth {
|
|
bottom: 9rpx;
|
|
width: 16rpx;
|
|
height: 7rpx;
|
|
margin-left: -8rpx;
|
|
}
|
|
.vsm--on-dark .vsm-mouth { background: #ecfdf5; }
|
|
|
|
.vsm-waves {
|
|
position: absolute;
|
|
right: -2rpx;
|
|
top: 50%;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 4rpx;
|
|
height: 28rpx;
|
|
margin-top: -14rpx;
|
|
z-index: 0;
|
|
}
|
|
.vsm-wave {
|
|
width: 5rpx;
|
|
border-radius: 4rpx;
|
|
background: #047857;
|
|
animation: vsm-wave-jump 0.72s ease-in-out infinite;
|
|
}
|
|
.vsm--on-dark .vsm-wave { background: rgba(255, 255, 255, 0.9); }
|
|
.vsm-wave--1 { height: 10rpx; animation-delay: 0s; }
|
|
.vsm-wave--2 { height: 18rpx; animation-delay: 0.12s; }
|
|
.vsm-wave--3 { height: 12rpx; animation-delay: 0.24s; }
|
|
|
|
.vsm--active .vsm-body {
|
|
animation: vsm-bob 0.9s ease-in-out infinite;
|
|
}
|
|
.vsm--active .vsm-mouth {
|
|
animation: vsm-talk 0.32s ease-in-out infinite alternate;
|
|
}
|
|
.vsm--active .vsm-eye {
|
|
animation: vsm-blink 2.4s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes vsm-bob {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-4rpx); }
|
|
}
|
|
@keyframes vsm-talk {
|
|
0% {
|
|
transform: scaleY(0.45);
|
|
border-radius: 0 0 18rpx 18rpx;
|
|
}
|
|
100% {
|
|
transform: scaleY(1.15);
|
|
height: 12rpx;
|
|
border-radius: 50%;
|
|
}
|
|
}
|
|
@keyframes vsm-blink {
|
|
0%, 42%, 46%, 100% { transform: scaleY(1); }
|
|
44% { transform: scaleY(0.15); }
|
|
}
|
|
@keyframes vsm-wave-jump {
|
|
0%, 100% { transform: scaleY(0.45); opacity: 0.55; }
|
|
50% { transform: scaleY(1); opacity: 1; }
|
|
}
|
|
</style>
|