【问题标题】:Cut Out Text Effect In Flutter在 Flutter 中剪切文本效果
【发布时间】:2019-02-08 15:28:20
【问题描述】:

我怎样才能将文本剪切到一个盒子容器中,让它下面的内容可见?

这正是我的意思:

如您所见,图片显示在文字下方。我怎么能这样做?

【问题讨论】:

标签: android ios flutter


【解决方案1】:

你必须使用CustomPainterTextPainterBlendModesaveLayer

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Playground',
      home: TestPage(),
    );
  }
}

class TestPage extends StatelessWidget {
  const TestPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        image: DecorationImage(
            image: AssetImage('assets/3g.jpg'), fit: BoxFit.cover),
      ),
      child: Center(
        child: CustomPaint(
          painter: CutOutTextPainter(text: 'YOUR NAME'),
        ),
      ),
    );
  }
}

class CutOutTextPainter extends CustomPainter {
  CutOutTextPainter({required this.text}) {
    _textPainter = TextPainter(
      text: TextSpan(
        text: text,
        style: const TextStyle(
          fontSize: 40.0,
          fontWeight: FontWeight.w600,
        ),
      ),
      textDirection: TextDirection.ltr,
    );
    _textPainter.layout();
  }

  final String text;
  late final TextPainter _textPainter;

  @override
  void paint(Canvas canvas, Size size) {
    // Draw the text in the middle of the canvas
    final textOffset =
        size.center(Offset.zero) - _textPainter.size.center(Offset.zero);
    final textRect = textOffset & _textPainter.size;

    // The box surrounding the text should be 10 pixels larger, with 4 pixels corner radius
    final boxRect = RRect.fromRectAndRadius(
        textRect.inflate(10.0), const Radius.circular(4.0));
    final boxPaint = Paint()
      ..color = Colors.white
      ..blendMode = BlendMode.srcOut;

    canvas.saveLayer(boxRect.outerRect, Paint());

    _textPainter.paint(canvas, textOffset);
    canvas.drawRRect(boxRect, boxPaint);

    canvas.restore();
  }

  @override
  bool shouldRepaint(CutOutTextPainter oldDelegate) {
    return text != oldDelegate.text;
  }
}

【讨论】:

  • 我认为更通用的渲染可以通过将 2 个 RepaintBoundary 混合为一个来实现。虽然不确定性能影响
  • 也许吧,但我认为这足以作为一个答案。很容易理解
【解决方案2】:

您可以为此使用ShaderMask,它允许您将着色器应用于小部件,同时考虑Blend Mode。混合模式是我们感兴趣的,所以着色器将是一种简单的颜色:

class Cutout extends StatelessWidget {
  const Cutout({
    Key key,
    @required this.color,
    @required this.child,
  }) : super(key: key);

  final Color color;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      blendMode: BlendMode.srcOut,
      shaderCallback: (bounds) => LinearGradient(colors: [color], stops: [0.0]).createShader(bounds),
      child: child,
    );
  }
}

这是一个示例渲染:

对于您的确切示例图像,孩子应该是 Text 小部件,您还应该将其包含在 ClipRRect 中以用于圆角(或者如果性能影响,您可以使用 BoxDecoration 找出更优化的解决方案ClipRRect 是个问题)

此解决方案的优势在于它可以与任何小部件一起作为子小部件使用,并且它是一个可组合的小部件,您可以在布局中弹出。

【讨论】:

  • 我还应该提到,渲染的黑色部分可以是GradientImageShader,在我的示例中,我使用渐变来模拟简单的颜色。据我所知,这 2 个shaders 是 Fl​​utter 中唯一默认可用的。两者都可以用于一些有趣的结果。
猜你喜欢
  • 1970-01-01
  • 2020-03-25
  • 2021-08-05
  • 2021-04-19
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多