颤动:倒置ClipOval

问题描述 投票:3回答:1

我是Flutter的新手,我正在尝试编写一个库,允许用户平移/缩放他们的个人资料图片。

为了使它具有视觉效果,我想用“倒置”的ClipOval堆叠它们的图像,以显示边界。

到目前为止,这是我获得的结果:

enter image description here

这显示了边界,但这不是用户友好的,我想“反转”ClipOval,使剪辑的中心“清晰”,外部变灰(类似于蒙版)。

有没有办法实现这个目标?

这是我到目前为止的代码(其中一部分来自flutter_zoomable_image):

import 'dart:ui' as ui;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class ImagePanner extends StatefulWidget {
  ImagePanner(this.image, {Key key}) : super(key: key);

  /// The image to be panned
  final ImageProvider image;

  @override
  _ImagePannerState createState() => new _ImagePannerState();
}

class _ImagePannerState extends State<ImagePanner> {
  ImageStream _imageStream;
  ui.Image _image;
  double _zoom = 1.0;
  Offset _offset = Offset.zero;
  double _scale = 16.0;

  @override
  void didChangeDependencies() {
    _resolveImage();
    super.didChangeDependencies();
  }

  @override
  void reassemble() {
    _resolveImage();
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    if (_image == null) {
      return new Container();
    }
    return new Container(
      width: double.INFINITY,
      color: Colors.amber,
      child: new Padding(
          padding: new EdgeInsets.all(50.0),
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new AspectRatio(
                aspectRatio: 1.0,
                child: new Stack(
                  children: [
                    _child(),
                    new Opacity(
                      opacity: 0.5,
                      child: new ClipOval(
                        child: new Container(
                          color: Colors.black,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          )),
    );
  }


  Widget _child() {
    Widget bloated = new CustomPaint(
      child: new Container(),
      painter: new _ImagePainter(
        image: _image,
        offset: _offset,
        zoom: _zoom / _scale,
      ),
    );

    bloated = new Stack(
      children: [
        new Container(
        ),
        bloated
      ],
    );

    return new Transform(
        transform: new Matrix4.diagonal3Values(_scale, _scale, _scale),
        child: bloated);
  }

  void _resolveImage() {
    _imageStream = widget.image.resolve(createLocalImageConfiguration(context));
    _imageStream.addListener(_handleImageLoaded);
  }

  void _handleImageLoaded(ImageInfo info, bool synchronousCall) {
    print("image loaded: $info $synchronousCall");
    setState(() {
      _image = info.image;
    });
  }
}

class _ImagePainter extends CustomPainter {
  const _ImagePainter({this.image, this.offset, this.zoom});

  final ui.Image image;
  final Offset offset;
  final double zoom;

  @override
  void paint(Canvas canvas, Size size) {
    paintImage(canvas: canvas, rect: offset & (size * zoom), image: image);
  }

  @override
  bool shouldRepaint(_ImagePainter old) {
    return old.image != image || old.offset != offset || old.zoom != zoom;
  }
}

我想获得的结果是以下内容,以便用户可以直接看到边界,并且能够在椭圆形中心,平移,缩放其轮廓图像。

enter image description here

(我是通过Photoshop制作的,因为我不知道如何用Flutter实现这一点)

非常感谢您的帮助。

flutter
1个回答
9
投票

还有其他几种方法可以做到这一点 - 您可以使用具有圆形和矩形的路径在CustomCanvas中绘制叠加层,因为您真正需要的是一个带有孔的矩形半透明矩形。但是你也可以使用CustomClipper,它可以在将来提供更大的灵活性而无需手动绘制内容。

void main() {
  int i = 0;
  runApp(new MaterialApp(
      home: new SafeArea(
        child: new Stack(
          children: <Widget>[
            new GestureDetector(
              onTap: () {
                print("Tapped! ${i++}");
              },
              child: new Container(
                color: Colors.white,
                child: new Center(
                  child: new Container(
                    width: 400.0,
                    height: 300.0,
                    color: Colors.red.shade100,
                  ),
                ),
              ),
            ),
            new IgnorePointer(
              child: new ClipPath(
                clipper: new InvertedCircleClipper(),
                child: new Container(
                  color: new Color.fromRGBO(0, 0, 0, 0.5),
                ),
              ),
            )
          ],
        ),
      ),
    ));
}

class InvertedCircleClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    return new Path()
      ..addOval(new Rect.fromCircle(
          center: new Offset(size.width / 2, size.height / 2),
          radius: size.width * 0.45))
      ..addRect(new Rect.fromLTWH(0.0, 0.0, size.width, size.height))
      ..fillType = PathFillType.evenOdd;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

需要IgnorePointer,否则事件不会通过半透明部分传播(假设您需要触摸事件)。

这是如何工作的,clipPath使用的Path是一个中间的圆圈(你需要手动调整大小),矩形占据整个大小。 fillType = PathFillType.evenOdd很重要,因为它告诉路径填充应该在圆和矩形之间。

如果你想使用customPainter,路径将是相同的,你只需要绘制它。

这一切都导致了:enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.