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

相关推荐
pengyu1 小时前
系统化掌握Dart网络编程之Dio(二):责任链模式篇
android·flutter·dart
侑柚酒1 小时前
一个例子直观的告诉你flutter中key的作用
flutter
云徒川1 小时前
【设计模式】过滤器模式
windows·python·设计模式
virelin_Y.lin2 小时前
系统与网络安全------Windows系统安全(4)
windows·web安全·系统安全·账号安全
pengyu3 小时前
系统化掌握Dart网络编程之Dio(二):配置管理篇
android·flutter·dart
学也不会4 小时前
d202541
windows
蹲街式等待4 小时前
Flutter dart代码混淆与解混淆
flutter
厦门德仔5 小时前
【C#】C#字符串拼接的6种方式及其性能分析对比
服务器·windows·c#
唔667 小时前
flutter 曲线学习 使用第三方插件实现左右滑动
javascript·学习·flutter
harry235day9 小时前
Flutter getx 状态管理
flutter·前端框架