Flutter 84: 图解自定义 ACEWave 波浪 Widget (二)

简介: 0 基础学习 Flutter,第八十四步:尝试优化自定义波浪效果 (二)!

      小菜继续完善前两天自定义 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 案例源码


      至此,小菜自定义的波浪效果基本完成,其中对于性能和异常的处理还为完善;如有错误,请多多指导!

来源: 阿策小和尚

目录
相关文章
|
23天前
|
前端开发 搜索推荐
Flutter中自定义气泡框效果的实现
Flutter中自定义气泡框效果的实现
30 3
|
2月前
|
前端开发
Flutter快速实现自定义折线图,支持数据改变过渡动画
Flutter快速实现自定义折线图,支持数据改变过渡动画
46 4
Flutter快速实现自定义折线图,支持数据改变过渡动画
|
2月前
|
开发者 监控 开发工具
如何将JSF应用送上云端?揭秘在Google Cloud Platform上部署JSF应用的神秘步骤
【8月更文挑战第31天】本文详细介绍如何在Google Cloud Platform (GCP) 上部署JavaServer Faces (JSF) 应用。首先,确保已准备好JSF应用并通过Maven构建WAR包。接着,使用Google Cloud SDK登录并配置GCP环境。然后,创建`app.yaml`文件以配置Google App Engine,并使用`gcloud app deploy`命令完成部署。最后,通过`gcloud app browse`访问应用,并利用GCP的监控和日志服务进行管理和故障排查。整个过程简单高效,帮助开发者轻松部署和管理JSF应用。
41 0
|
2月前
|
开发者 容器 Java
Azure云之旅:JSF应用的神秘部署指南,揭开云原生的新篇章!
【8月更文挑战第31天】本文探讨了如何在Azure上部署JavaServer Faces (JSF) 应用,充分发挥其界面构建能力和云平台优势,实现高效安全的Web应用。Azure提供的多种服务如App Service、Kubernetes Service (AKS) 和DevOps简化了部署流程,并支持应用全生命周期管理。文章详细介绍了使用Azure Spring Cloud和App Service部署JSF应用的具体步骤,帮助开发者更好地利用Azure的强大功能。无论是在微服务架构下还是传统环境中,Azure都能为JSF应用提供全面支持,助力开发者拓展技术视野与实践机会。
13 0
|
2月前
|
开发框架 API 开发者
Flutter表单控件深度解析:从基本构建到高级自定义,全方位打造既美观又实用的移动端数据输入体验,让应用交互更上一层楼
【8月更文挑战第31天】在构建美观且功能强大的移动应用时,表单是不可或缺的部分。Flutter 作为热门的跨平台开发框架,提供了丰富的表单控件和 API,使开发者能轻松创建高质量表单。本文通过问题解答形式,深入解读 Flutter 表单控件,并通过具体示例代码展示如何构建优秀的移动应用表单。涵盖创建基本表单、处理表单提交、自定义控件样式、焦点管理和异步验证等内容,适合各水平开发者学习和参考。
31 0
|
3月前
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
37 1
|
3月前
|
Dart Android开发
Flutter-自定义短信验证码
Flutter-自定义短信验证码
31 1
|
3月前
Flutter-自定义画板
Flutter-自定义画板
27 0
Flutter-自定义画板
|
3月前
|
移动开发 UED 容器
Flutter-自定义可展开文本控件
Flutter-自定义可展开文本控件
70 0
|
3月前
Flutter-自定义折叠流布局实现
Flutter-自定义折叠流布局实现
50 0
下一篇
无影云桌面