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...

相关推荐
coder_pig32 分钟前
📝小记:Ubuntu 部署 Jenkins 打包 Flutter APK
flutter·ubuntu·jenkins
唐宋元明清21881 小时前
.NET 阻止系统睡眠/息屏
windows·电源
捡芝麻丢西瓜3 小时前
flutter自学笔记5- dart 编码规范
flutter·dart
恋猫de小郭3 小时前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
yylの博客3 小时前
Windows通过git-bash安装zsh
windows·git·bash·zsh
进击的code3 小时前
windows 下使用WLS2 编译aosp Android14并刷机到pixle 5a
windows
染指11107 小时前
50.第二阶段x86游戏实战2-lua获取本地寻路,跨地图寻路和获取当前地图id
c++·windows·lua·游戏安全·反游戏外挂·游戏逆向·luastudio
dntktop8 小时前
Converseen:全能免费批量图像处理专家
windows
一个懒鬼9 小时前
Windows脚本清理C盘缓存
windows·缓存
蚁景网络安全10 小时前
Cobalt Strike 4.8 用户指南-第十四节 Aggressor 脚本
windows·microsoft