uniapp
使用 blur 毛玻璃; blur 详细教程
<template>
<view class="container" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
<scroll-view
scroll-y
style="height: 100vh;"
@scroll="onScroll"
>
<view v-for="i in 40" :key="i" class="item">内容 {{ i }}</view>
</scroll-view>
<!-- 底部菜单 -->
<view
class="bottom-nav"
:style="{
transform: `translateY(${translateY}px) scale(${scale})`,
transition: isTouching ? 'none' : 'all 0.3s ease'
}"
>
<view class="nav-item" v-for="(item, i) in navList" :key="i">
<image :src="item.icon" class="nav-icon" />
<text class="nav-text">{{ item.text }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const translateY = ref(0)
const scale = ref(1)
const startY = ref(0)
const isTouching = ref(false)
const navList = [
{ icon: '/static/home.png', text: '首页' },
{ icon: '/static/shop.png', text: '商城' },
{ icon: '/static/user.png', text: '我的' }
]
const touchStart = (e) => {
startY.value = e.touches[0].clientY
isTouching.value = true
}
const touchMove = (e) => {
const diff = e.touches[0].clientY - startY.value
// 向上滑动时上移并放大
translateY.value = Math.min(0, diff)
scale.value = 1 + Math.min(0.15, Math.abs(diff) / 200)
}
const touchEnd = () => {
isTouching.value = false
translateY.value = 0
scale.value = 1
}
const onScroll = (e) => {
// 可扩展:滚动时渐隐或移动菜单
}
</script>
<style scoped>
.container {
position: relative;
overflow: hidden;
}
.item {
height: 100rpx;
line-height: 100rpx;
text-align: center;
background: #f8f8f8;
margin-bottom: 10rpx;
}
.bottom-nav {
position: fixed;
bottom: 20rpx;
left: 50%;
transform: translateX(-50%);
width: 90%;
height: 120rpx;
border-radius: 40rpx;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(20rpx);
display: flex;
justify-content: space-around;
align-items: center;
transition: all 0.3s ease;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
}
.nav-icon {
width: 60rpx;
height: 60rpx;
}
.nav-text {
font-size: 24rpx;
color: #fff;
}
</style>
- 毛玻璃使用 backdrop-filter;
拖动时放大,松开恢复;
兼容 App、H5、小程序(部分小程序 blur 支持有限)。
flutter实现
- 用到的组件
-
BackdropFilter:毛玻璃;
-
GestureDetector:监听手势;
-
AnimatedContainer 或 Transform.scale:平滑放大;
-
SingleChildScrollView:滑动内容。
import 'dart:ui';
import 'package:flutter/material.dart';class FrostedBottomBar extends StatefulWidget {
@override
_FrostedBottomBarState createState() => _FrostedBottomBarState();
}class _FrostedBottomBarState extends State<FrostedBottomBar> {
double _translateY = 0;
double _scale = 1.0;
bool _isDragging = false;@override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( onVerticalDragUpdate: (details) { setState(() { _isDragging = true; _translateY = details.primaryDelta!.clamp(-60.0, 0.0); _scale = 1 + (_translateY.abs() / 300); }); }, onVerticalDragEnd: (_) { setState(() { _isDragging = false; _translateY = 0; _scale = 1.0; }); }, child: Stack( children: [ ListView.builder( itemCount: 40, itemBuilder: (context, i) => ListTile(title: Text("Item $i")), ), Positioned( bottom: 20, left: 0, right: 0, child: AnimatedContainer( duration: Duration(milliseconds: _isDragging ? 0 : 300), transform: Matrix4.identity() ..translate(0.0, _translateY) ..scale(_scale), alignment: Alignment.bottomCenter, child: ClipRRect( borderRadius: BorderRadius.circular(40), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20), child: Container( height: 80, margin: EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(40), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: const [ Icon(Icons.home, color: Colors.white), Icon(Icons.shopping_cart, color: Colors.white), Icon(Icons.person, color: Colors.white), ], ), ), ), ), ), ), ], ), ), ); }}
-
毛玻璃完全原生(BackdropFilter);
-
拖动平滑放大,松手回弹;
-
iOS 样式极佳,流畅度接近原生 Dock。
| 框架 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| UniApp | 轻量,适配多端 | 毛玻璃在部分小程序不支持 | 商业小程序、兼容App |
| Flutter | 动画丝滑、质感最接近iOS原生 | 打包体积大 | 高端App、iOS视觉追求 |