【剪裁 widget】Flutter ClipRect

简介: 【剪裁 widget】Flutter ClipRect

image.png

用一个矩形去剪裁 child,矩形以外的部分不显示。通过和一些没有剪裁功能的 widget 合用,剪裁这些 widget 溢出的部分,还能高效的实现动画。

和布局 widget 不同,剪裁 widget 功能实现是在绘制阶段。所以 剪裁 widget 的 size 是不会变的,无论怎样剪裁。

因为实现是在绘制阶段,所以具体实现是在 paint 方法中调用 PaintingContext 类的 pushClipRect 方法进行剪裁。

@override
  void paint(PaintingContext context, Offset offset) {
   ...
        layer = context.pushClipRRect(
          needsCompositing,
          offset,
          _clip!.outerRect,
          _clip!,
          super.paint,
          clipBehavior: clipBehavior,
          oldLayer: layer as ClipRRectLayer?,
        );
      }
   ...
  }
复制代码

相比于 ClipPath,ClipRect 是比较高效的,所以如果是要实现矩形剪裁,优先选用 ClipRect。

默认情况下,ClipRect 的剪裁路径是正好包含整个 child,只有溢出 child 的部分才会剪裁。我们可以指定 clipper 参数进行自定义裁剪。

举个例子,我们想裁剪出花朵的部分。图片大小为 100 x 100。


image.png

class MyClipper extends CustomClipper<Rect> {
  @override
  Rect getClip(Size size) {
     return const Rect.fromLTWH(30, 40, 50, 50);
  }
  @override
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
    return false;
  }
}
复制代码
Center(
    child: ClipRect(
      clipper: MyClipper(),
      child: Image.network(
        'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e993af7d8ac142d5a5818e6a12249b2c~tplv-k3u1fbpfcp-watermark.image?',
        width: 100,
        height: 100,
        fit: BoxFit.fill,
    ),
 ))
复制代码

想要有裁剪效果, 自定义 clipper 是必须的,还好并不复杂。

ClipRect 的应用场景

我们知道有的 widget 是带 clip 参数的,有剪裁功能,但有的 wiget 没有,如果想剪裁溢出的部分怎么办呢。这就需要用到 ClipRect了。

裁剪 Align 溢出部分


image.png


Center(
    child: Column(
      children: [
        Align(
          alignment: Alignment.topCenter,
          heightFactor: 0.5,
          child: Container(
            width: 100,
            height: 100,
            color: Colors.blue[300],
          ),
        ),
        const Text('IAM17 天天更新'),
      ],
  ))
复制代码

我们前面已经讲解过 Align Widget 了,正好复习一下。本例中 Align Widget 有 heightFactor 参数,所以它的高为 100 * 0.5 = 50。虽然高为 50,但在绘制的时候,还会把 child 完整绘制出来,我们看到的 child 还是高 100。下面的文本 IAM17 天天更新 会接着在 50 高的位置显示出来。效果上就是文本覆盖了蓝色 box。


加上 ClipRect 就可以把多出来的部分 clip 掉。先看下效果


image.png

Center(
    child: Column(
      children: [
        ClipRect(
          child: Align(
            alignment: Alignment.topCenter,
            heightFactor: 0.5,
            child: Container(
              width: 100,
              height: 100,
              color: Colors.blue[300],
            ),
          ),
        ),
        //省略字号等信息
        const Text('IAM17 天天更新'),
      ],
 ))
复制代码

除了可以为 Align Widget 剪裁,还可以给 OverFlowBoxSizedOverFlowBox 等其它无法自己剪裁的 widget 进行剪裁。

clip 动画

在 css 中 clip 能做动画 ,flutter 中也是一样的,大同小异。

剪裁 widget 做动画的关键在于 clipper 参数,clipper 是 CustomClipper 类型 ,CustomClipper 的参数 reclip 可以用来做动画。

/// The clipper will update its clip whenever [reclip] notifies its listeners.
  const CustomClipper({Listenable? reclip}) : _reclip = reclip;
复制代码

还是举个例子吧,这样比较直观些。我就不用图片了,用矩形代替,直接就能画,重意而非重形。


image.png

贴出全部代码,方便大家自己试试。

import 'package:flutter/material.dart';
void main() => runApp(const ClipRectApp());
class ClipRectApp extends StatefulWidget {
  const ClipRectApp({super.key});
  @override
  State<ClipRectApp> createState() => _ClipRectAppState();
}
class _ClipRectAppState extends State<ClipRectApp>
    with TickerProviderStateMixin {
  late AnimationController _sizeController;
  late Animation<double> _animation;
  @override
  void initState() {
    _sizeController =
        AnimationController(vsync: this, duration: const Duration(seconds: 2))
          ..repeat();
    _animation = _sizeController.drive(Tween<double>(begin: 0.0, end: 100.0));
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ClipRect(
        clipper: MyClipper(
          reclip: _animation,
        ),
          child: Container(
        width: 100,
        height: 100,
        color: Colors.blue[300],
      )),
    );
  }
}
class MyClipper extends CustomClipper<Rect> {
  MyClipper({required Animation<double> reclip})
      :_reclip=reclip, super(reclip: reclip);
  Animation<double> _reclip;
  @override
  Rect getClip(Size size) {
    return Rect.fromCenter(
        center: const Offset(50, 50), width: _reclip.value, height: _reclip.value);
  }
  @override
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
    return false;
  }
}
复制代码


clip 动画是很高效的,因为它省略了 build 的开销。

虽然 ClipRect 是 剪裁中最简单的一个,但它的应用却是最广泛的。自己能剪裁的 widget 实现的大都是矩形剪裁。比如 Stack

从源头上来说,其实大家都用的都是 PaintingContext 类的 pushClipRect 实现的矩形剪裁功能。


最后要注意的


如果你发现 ClipRect 没有效果从两个方向找原因

  1. clipper 参数是否正确
  2. 可能是 ClipRect 比 child 大,剪在了 child 之外。
IAM17
+关注
目录
打赏
0
0
0
0
1
分享
相关文章
vue-swiper-配置不生效
今天安装 swiper的时候 直接安装了 轮播效果也有,无限循环滚动也有 但是自动滚动,切换特效等效果不生效 简单记录如下
742 0
vue-swiper-配置不生效
MySQL多表查询之子查询详解
在数据库查询中,多表查询是一项非常常见且重要的任务。它允许我们从多个相关联的表中检索和组合数据,以满足各种复杂的查询需求。在多表查询中,子查询是一种强大的工具,用于在查询中嵌套另一个查询。本文将深入探讨MySQL中的子查询,包括什么是子查询、如何编写子查询以及使用子查询解决的常见查询问题。
520 1
(linux-x86-arm64)麒麟V10安装openjdk+Grafana-7.2.0+Prometheus-2.16.0
记(linux-x86-arm64)麒麟V10安装openjdk+Grafana-7.2.0+Prometheus-2.16.0
QGS
1444 0
(linux-x86-arm64)麒麟V10安装openjdk+Grafana-7.2.0+Prometheus-2.16.0
word-break:break-all和word-wrap:break-word的区别
word-break:break-all和word-wrap:break-word的区别
454 0
MySQL多表查询之外连接详解
在MySQL数据库中,多表查询是一种常见且强大的功能,允许您在多个表之间执行联接操作,从而检索、过滤和组合数据。在本篇博客中,我们将深入探讨多表查询的一种类型,即外连接(Outer Join),并详细介绍其语法、用途和示例。无论您是刚开始学习数据库还是想深入了解MySQL的查询功能,本文都将为您提供有价值的信息。
1107 0
.NET Core 跨平台资源监控库及dotnet tool小工具
.NET Core 跨平台资源监控库及dotnet tool小工具
592 0
.NET Core 跨平台资源监控库及dotnet tool小工具
C#获取windows 10的下载文件夹路径
Windows没有为“下载”文件夹定义CSIDL,并且通过Environment.SpecialFolder枚举无法使用它。 但是,新的Vista 知名文件夹 API确实使用ID定义它FOLDERID_Downloads。
1981 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问