Flutter 仿英雄联盟客户端详解

最近在用Flutter 模仿英雄联盟客户端,

制作顶部菜单的时候,发现了之前没有注意的细节效果,写个文章分享和记录一下。


分析:鼠标移动到菜单上,会产生一个光晕,从底部向上扇形扩散,光晕的中心点就是鼠标的X轴坐标。

现在尝试来复现这个效果。

我们需要用到的第一个组件是:CustomPaint

它是一个自己处理绘制的组件。

一定要用RepaintBoundary包住,不然它会被其他组件的notifyListeners()影响导致CustomPaint重绘,如果没有用RepaintBoundary包裹,它自身的notifyListeners()行为也会影响其他组件导致重绘。

less 复制代码
RepaintBoundary(
   //底层光圈绘制层
   child: CustomPaint(
       painter: _painter,
       // 上层元素
       child: widget.child,
   ),
)
scala 复制代码
class TopMenuShadowPainter extends ChangeNotifier implements CustomPainter {
  TopMenuShadowPainter();

  @override
  void paint(Canvas canvas, Size size) {
    // 圆的尺寸
    double radius = 80;

    // 绘制一个 圆形
    canvas.drawOval(
      Rect.fromLTWH(0, 0, radius, radius),
      paint,
    );

    // 或者绘制一个 扇形  pi是系统定义的 3.1415926535897932 , pi + pi 就是从左侧水平  0°到 右侧 180°
    canvas.drawArc(
      Rect.fromLTWH(0, 0, radius, radius),
      pi,
      pi,
      true,
      paint,
    );
  }

绘制出大小80直径的圆

绘制出80直径半圆


接下来就是让这个圆在鼠标的位置中心点绘制,所以需要把鼠标传进去,一开始我使用的是AnimatedBuilder+CustomPaint, 这种方案每次绘制都会重新创建新的painter对象,不是特别的好。

我参考了Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡这篇文章,里面有讲到CustomPaint的几种刷新机制,当前这个Menu 比较适合采用 ChangeNotifier的方式进行刷新,让当前TopMenuShadowPainter继承ChangeNotifier,并且实现CustomPainter,这样它就带有通知刷新和绘制能力。

我们需要使用MouseRegion捕获鼠标的位置和移出移入事件。

csharp 复制代码
/// 绘制器
late TopMenuShadowPainter _painter;

MouseRegion(
      /// 显示为手指的效果
      cursor: SystemMouseCursors.click,
      onEnter: (event) {
        // 鼠标进入
        _painter.updateEnter(true);
      },
      onExit: (event) {
        // 鼠标离开
        _painter.updateEnter(false);
      },
      onHover: (event) {
        // 鼠标坐标移动
        _painter.updatePoint(event.localPosition.dx);
      },
      child: RepaintBoundary(
        //底层光圈绘制层
        child: CustomPaint(
          painter: _painter,
          child: widget.child,
        ),
      ),
);

内部更新鼠标的坐标和进出状态。

ini 复制代码
TopMenuShadowPainter extends ChangeNotifier implements CustomPainter

// 使用方法刷新鼠标的进入离开状态 以及 鼠标的 坐标 =-=
// notifyListeners 触发的时候,没有用RepaintBoundary,会导致其他部分组件重绘。

/// 鼠标进入
  bool mouseEnter = false;

  // 鼠标的坐标点
  double mouseX = 0;

  /// 更新
  void updateEnter(bool newMouseEnter) {
    mouseEnter = newMouseEnter;
    //debugPrint("鼠标进入退出:$mouseEnter");
    notifyListeners();
  }

  /// 更新
  void updatePoint(double newMouseX) {
    mouseX = newMouseX;
    notifyListeners();
  }

重新计算绘制的圆位置。

arduino 复制代码
// 计算鼠标的坐标 产生圆的位置
// 圆起点的X坐标 计算方式 = 鼠标的X轴坐标(鼠标是居中位置) 减去 圆的一半
double x =  mouseX - radius / 2,
// 圆起点的Y坐标 计算方式 = 绘图Canvs的高度的一半 
// 向下偏移 10px(使光晕更贴底,这个值也可以不加或者更大)
double y =  size.height / 2 + 10,
// 绘制圆
canvas.drawOval(
    Rect.fromLTWH(
        mouseX - radius  / 2,
        size.height / 2 + 10,
        radius,
        radius,
    ),
    paint,
);

好的,我们现在来看看效果。

ops?咋回事,溢出了。。

这时候我们设置一下裁剪区域,

arduino 复制代码
// 从0,0坐标 扩展到画板尺寸size的Rect区域
canvas.clipRect(Offset.zero  & size);

Y轴溢出问题解决

Y轴不溢出了,但是X轴上面的被裁剪掉了

调整一下裁剪区域

arduino 复制代码
// 裁剪区域 向左移动 圆的大小 向右也移动圆的的大小,radius
Rect clipRect = Rect.fromLTRB(- radius, 0, size.width + radius, size.height);
canvas.clipRect(clipRect);

接下来,再给光晕增加一个高斯模糊的效果。

ini 复制代码
var paint = Paint()
      // 背景渐变刷子
      ..shader = gradient
      // 模糊效果
      ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 20);

// 绘制底部的金线
canvas.drawLine(
      Offset(mouseX - 50, size.height),
      Offset(mouseX + 50, size.height),
      Paint()
        ..strokeCap = StrokeCap.round
        // 从 中间向两边 渐变的背景刷
        ..shader = lineColor
        ..strokeWidth = 1.5,
    );

PS:最开始我是给每个菜单包一个这个渲染,因为我是模仿英雄联盟做的,后面封装了一个完整的容器,这样多个菜单共享一个背景刷,效果会好很多。

另外一个 PLAY按钮的具体做法,目前有点LOW,大概讲一下思路,看看后面有没有更好的方案

默认效果

随机圆点

开启模糊

鼠标移入的时候,开启一个循环动画。绘制随机的光斑(模糊),每一帧向左侧偏移0~1px,最终效果就还能接受,等优化了。

最后附上源码地址,可以下载编译 或者 下载 编译好的包 体验。 github.com/944095635/l...

相关推荐
神经智研社43 分钟前
ROS2-5章:节点参数parameter详细讲解
windows·microsoft·机器人环境搭建·win11 ros2 开发环境
诚信定制8394 小时前
PrivaZer逆向解析:深度清理背后的三大隐患
windows
love530love8 小时前
WorkBuddy + 本地 ComfyUI Wan2.1 文生视频实战:从连续报错到成功出片的完整踩坑记录
人工智能·windows·python·音视频·devops·comfyui·mcp
TrisighT8 小时前
Electron 跑鸿蒙 PC 上,这 4 个 API 的行为跟 Windows 完全不一样——但文档一行都没写
windows·electron·harmonyos
SoaringHeart1 天前
Flutter进阶:基于 EasyRefresh 的下拉刷新封装 n_easy_refresh_mixin.dart
前端·flutter
月光下的丝瓜2 天前
Flutter 国内安装指南
前端·flutter
恋猫de小郭4 天前
Amper 正式转正 Kotlin Toolchain ,Gradle 未来何去何从
android·前端·flutter
张风捷特烈4 天前
Flutter 类库大揭秘#02 | path_provider 各平台实现
前端·flutter
TT_Close5 天前
别劝退了!5秒搞定 Flutter 鸿蒙 FVM 起跑线
flutter·harmonyos·visual studio code
你听得到115 天前
用户说 App 卡,但说不清在哪?我把 Flutter 监控 SDK 升级成了链路观测工作台
前端·flutter·性能优化