uniapp/flutter中实现苹果IOS 26 毛玻璃效果、跟随滑动放大动画

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视觉追求
相关推荐
gys98959 小时前
uniapp使用sqlite模块
数据库·sqlite·uni-app
abigale039 小时前
开发实战 - ego商城 -补充:使用uniapp扩展组件
uni-app·uni-ui
2501_9160074711 小时前
Fastlane 结合 开心上架(Appuploader)命令行实现跨平台上传发布 iOS App 的完整方案
android·ios·小程序·https·uni-app·iphone·webview
爱喝水的小周11 小时前
《UniApp 页面导航跳转全解笔记》
前端·uni-app
CV大师杨某11 小时前
如何在uni-app中禁用iOS橡皮筋效果?
ios·uni-app
More more12 小时前
uniapp实时查看在线监控,JessibucaMobile实现横屏播放
前端·javascript·uni-app·jessibucamobile
好想早点睡.12 小时前
vue2+UniApp微信小程序集成高德地图
微信小程序·小程序·uni-app
Jiaberrr13 小时前
解决uni-app通用上传与后端接口不匹配问题:原生上传文件方法封装 ✨
前端·javascript·uni-app
listhi52013 小时前
Vue.js 3的组合式API
android·vue.js·flutter