专栏首页阿策小和尚【Flutter 专题】84 图解自定义 ACEWave 波浪 Widget (二)

【Flutter 专题】84 图解自定义 ACEWave 波浪 Widget (二)

和尚继续完善前两天自定义 ACEWave 波浪组件,和尚预期的效果是多条波浪,渐变颜色,波浪宽高自定义等;

1. 区分波浪宽度动画

和尚上一节测试时波浪宽度小于屏幕宽度,当放大波浪宽度时,循环过程中动画会跳动一下,不顺畅;其原因是 Animation 动画设置有问题;

和尚调整了平移动画的 Offset 位置,并设置波浪起始位置偏移量与小波浪时相反;

return Transform.translate(
    offset: Offset(waveWidth * _curvedAnimation.value, 0.0),
    child: Container(); // 波浪

2. 填充波浪颜色

再此之前和尚尝试的均为线条波浪,和尚理想的效果的是有填充色的,于是设置三屏波浪最末点与三屏波浪的最初点,通过 lineTo 连接起来,并设置 Paint 画笔为填充效果;

Path _wavePath(path, size, plusWidth) {
  for (int i = 0; i < _count; i++) {
    path.moveTo(waveWidth * i - size.width - startOffset, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth + waveWidth * i - size.width - startOffset, startOffsetY - waveHeight,
        _quaterWidth * 2 + waveWidth * i - size.width - startOffset, startOffsetY);
    path.moveTo(_quaterWidth * 2 + waveWidth * i - size.width - startOffset, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth * 3 + waveWidth * i - size.width - startOffset, startOffsetY + waveHeight,
        _quaterWidth * 4 + waveWidth * i - size.width - startOffset, startOffsetY);
    path.moveTo(_quaterWidth * 4 + waveWidth * i - size.width - startOffset, startOffsetY);
  }
  path.lineTo(_quaterWidth * 4 + waveWidth * (_count - 1) - size.width - startOffset, 600.0);
  path.lineTo(-size.width - startOffset, 600.0);
  path.lineTo(-size.width - startOffset, startOffsetY);

  for (int i = 0; i < _count; i++) {
    path.moveTo(waveWidth * i - startOffset + plusWidth, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth + waveWidth * i - startOffset + plusWidth, startOffsetY - waveHeight,
        _quaterWidth * 2 + waveWidth * i - startOffset + plusWidth, startOffsetY);
    path.moveTo(_quaterWidth * 2 + waveWidth * i - startOffset + plusWidth, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth * 3 + waveWidth * i - startOffset + plusWidth, startOffsetY + waveHeight,
        _quaterWidth * 4 + waveWidth * i - startOffset + plusWidth, startOffsetY);
    path.moveTo(_quaterWidth * 4 + waveWidth * i - startOffset + plusWidth, startOffsetY);
  }
  path.lineTo(_quaterWidth * 4 + waveWidth * (_count - 1) - startOffset + plusWidth, 600.0);
  path.lineTo(-startOffset + plusWidth, 600.0);
  path.lineTo(-startOffset + plusWidth, startOffsetY);

  for (int i = 0; i < _count; i++) {
    path.moveTo(waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth + waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY - waveHeight,
        _quaterWidth * 2 + waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY);
    path.moveTo(
        _quaterWidth * 2 + waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY);
    path.quadraticBezierTo(
        _quaterWidth * 3 + waveWidth * i + size.width - startOffset + plusWidth * 2,
        startOffsetY + waveHeight, _quaterWidth * 4 + waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY);
    path.moveTo(_quaterWidth * 4 + waveWidth * i + size.width - startOffset + plusWidth * 2, startOffsetY);
  }
  path.lineTo(
      _quaterWidth * 4 + waveWidth * (_count - 1) + size.width - startOffset + plusWidth * 2, 600.0);
  path.lineTo(size.width - startOffset + plusWidth * 2, 600.0);
  path.lineTo(size.width - startOffset + plusWidth * 2, startOffsetY);
  return path;
}

3. 波浪渐变色

和尚填充完波浪颜色之后,想进一步实现波浪渐变色,可以通过 Paint 画笔来设置 shader 渐变效果;其中线性渐变的起始点从波峰开始,至最底部为止;

Paint paint = Paint()
    ..color = Colors.blue ..strokeCap = StrokeCap.round ..strokeWidth = 6
    ..style = PaintingStyle.fill;
var rect = Offset(0.0, startOffsetY) & Size(size.width, size.height)
paint.shader = LinearGradient(
    begin: Alignment.bottomCenter,
    end: Alignment.topCenter,
    colors: [Colors.blue.withOpacity(0.4), Colors.white.withOpacity(0.4) ]).createShader(rect);

4. 裁剪波浪

和尚设置的波浪高度默认是填充满父控件的,但若父控件高度小于波浪的波峰到波谷高度时,波谷依然绘制出来,此时和尚通过裁剪方式,只展示设置的最高高度即可;此时注意优先设置裁剪范围,之后再进行波浪的绘制;

canvas.save();

canvas.clipPath(_clipPath(size, _plusWidth));
canvas.drawPath(_wavePath(size, _plusWidth), paint);

canvas.restore();

5. 设置多条波浪

和尚想一次性展示多条波浪,于是将各个自定义参数类型及动画 Animation 放在 List 中,只需在初始化时传递多条数据即可;其中包括波浪宽高,一个波浪动画时长,初始横纵偏移量以及渐变色波浪颜色等;

// 波浪整体高度(裁剪后)
final double allHeight;
// 一个周期波浪宽度 List
final List<double> waveWidthList;
// 波峰到中心点高度 List
final List<double> waveHeightList;
// 水平偏移量 List
final List<double> startOffsetXList;
// 波峰距顶点偏移量 List
final List<double> startOffsetYList;
// 时间 List
final List<Duration> durationList;
// 渐变色 List
final List<List<Color>> waveColorList;

ACEWave(this.waveWidthList, this.waveHeightList, this.allHeight,
    {this.durationList,
    this.startOffsetXList,
    this.startOffsetYList,
    this.waveColorList});
List<double> waveWidth = [600, 800, 300];
List<double> waveHeight = [60, 80, 70];
List<double> startOffsetX = [30, 150, 100];
List<double> startOffsetY = [100, 120, 100];
List<Duration> duration = [
  Duration(milliseconds: 6000),
  Duration(milliseconds: 4000),
  Duration(milliseconds: 5000)
];
List<List<Color>> colorList = [
  [Colors.green.withOpacity(0.2), Colors.white.withOpacity(0.4)],
  [Colors.blue.withOpacity(0.2), Colors.white.withOpacity(0.4)],
  [Colors.blue.withOpacity(0.2), Colors.white.withOpacity(0.4)]
];
return Scaffold(
    appBar: AppBar(title: Text('ACEWave Page')),
    body: Container(
        color: Colors.grey,
        height: (MediaQuery.of(context).size.height),
        child: Container(
            child: ACEWave(
          waveWidth,
          waveHeight,
          300.0,
          startOffsetXList: startOffsetX,
          startOffsetYList: startOffsetY,
          durationList: duration,
          waveColorList: colorList,
        ))));

ACEWave 案例源码

本文分享自微信公众号 - 阿策小和尚(gh_8297e718c166),作者:阿策小和尚

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Flutter 专题】76 图解基本 TabBar 标签导航栏 (二)

    和尚刚刚学习了 TabBar 标签导航栏的使用,其中对于标签指示器 indicator 的使用较少;和尚今天尝试一下自定义标签指示器;

    阿策小和尚
  • 【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)

    和尚今天尝试一下绘制波浪的效果,虽然 pub 仓库中已经有成熟的插件,但和尚还是准备用之前学习的 Canvas 和 Animation 尝试自定义一个 ACEW...

    阿策小和尚
  • 【Flutter 专题】29 易忽略的【小而巧】的技术点汇总 (五)

    和尚在做 Android 时经常会双击快速点击返回键弹出退出对话框,之后在进行操作,而 Flutter 也提供了监听返回导航的 WillPopSco...

    阿策小和尚
  • 奇怪,Spring Security 登录成功后总是获取不到登录用户信息?

    一开始我觉得这可能是一个小概率 BUG,但是当问的人多了,我觉得这个问题对于新手来说还有一定的普遍性,有必要来写篇文章跟大家仔细聊一聊这个问题,防止小伙伴们掉坑...

    江南一点雨
  • AI时代,如何缓解CMO的决策焦虑?

    2018 是人工智能落地之年,在 AI 应用方面,目前中国已呈现出爆发的趋势,主要集中在安防、金融、医疗、教育、零售、机器人以及智能驾驶等领域。

    AI科技大本营
  • C语言 | 每日基础(78)

    读者:怎样才能进行反向操作, 把 struct tm 或一个字符串转换成 time_t?

    C语言入门到精通
  • MySQL(十)DQL之分页查询

    leeqico
  • matplotlib基础绘图命令之violinplot

    vert参数的默认值为True,表示竖直方向的小提琴图,当取值WieFalse时,绘制水平方向的小提琴图,用法如下

    生信修炼手册
  • 用matplotlib实现画中画

    当我们想要在一个坐标系中包含另外一个完整的图像时,就需要用到子图相关的技术,在matplotlib中,提供了以下两种实现方式

    生信修炼手册
  • 学界 | 人工智能的圣杯:关于可解释AI(XAI)的一切

    这期间,在企业客户却也始终存在一种怀疑态度:AI系统做出的产品部署是否真的值得被信赖呢?

    大数据文摘

扫码关注云+社区

领取腾讯云代金券