Flutter 组件碎片化消失效果

430 阅读1分钟

Flutter 组件碎片化消失效果

项目地址: github.com/wuweijian19…

先上效果图,可以根据触摸点作为动画消失的开始点.点击哪里就从哪里开始 消失.

没有过渡效果

有过渡效果

实现原理

1.获取组件截图
2.使用canvas.drawImageRect按rect绘制想要的效果.

获取组件截图

通过globalKey获取RenderRepaintBoundary,然后使用它的toImage就可以生成 ui.Image

FragmentsRenderObjectWidget(
  key: _fragmentsController.globalKey,
  ...
);

void generateImage(VoidCallback onEnd) {
  RenderRepaintBoundary boundary =
      _globalKey.currentContext.findRenderObject();
  boundary.toImage().then((value) {
    _image = value;
    onEnd?.call();
  });
}

canvas自定义绘制

@override
void draw({
  Canvas canvas,
  ui.Image paintImage,
  double progress,
  Offset startingOffset,
}) {
///图片大小
  Size imageSize = Size(paintImage.width.toDouble(), paintImage.height.toDouble());
  if (_startingOffset != startingOffset) {
    _startingCoordinate = initCoordinate(
      size: imageSize,
      rowLength: rowLength,
      columnLength: columnLength,
      startingOffset: startingOffset,
    );
  }
  Paint paint = Paint();
  ///触摸点到边界的最大距离
  double maxDistance = startingCoordinate.maxDistance(
    maxX: rowLength - 1,
    maxY: columnLength - 1,
  );
  for (int i = 0; i < rowLength; i++) {
    for (int j = 0; j < columnLength; j++) {
    ///将图片切成10 * 10一百个方块,用来后面使用
    ///计算当前这个方块的进度值,这个可以自定义计算公式
      double currentProgress = calculateFragmentsProgress(
        currentCoordinate: Coordinate(x: i, y: j),
        startingCoordinate: startingCoordinate,
        maxDistance: maxDistance,
      );
      ///只绘制当前进度值大于progress的方块
      if (currentProgress > progress) {
        Rect rect = calculateFragment(i: i, j: j, size: imageSize);
        if (disableTransition == false) {
          double opacity = 1;
          double transition = (rowLength + columnLength) / (rowLength * columnLength);
          transition = min(transition, .1);
          if (currentProgress - transition < progress) {
            opacity = .6;
          } else if (currentProgress - transition / 2 < progress) {
            opacity = .8;
          }
          paint.color = Colors.white.withOpacity(opacity);
        }
        ///这里进行绘制,第一个rect是图片的所在方块,第二个rect是canvas绘制的方块,也就是Rect
        canvas.drawImageRect(paintImage, rect, rect, paint);
      }
    }
  }
}

///计算当前方块的进度值
double calculateFragmentsProgress({
  Coordinate currentCoordinate,
  Coordinate startingCoordinate,
  double maxDistance,
}) {
  double distance = currentCoordinate.distance(startingCoordinate);
  return (distance / maxDistance).clamp(.0, 1.0);
}

///根据每行每列的数量和图片的大小计算所有方块的大小和位置
Rect calculateFragment({int i, int j, Size size}) {
  if(_fragments == null) {
    _fragments = List(rowLength);
  }
  if(_fragments[i] == null) {
    _fragments[i] = List(columnLength);
  }
  if(_fragments[i][j] == null) {
    double fragmentsWidth = size.width / rowLength;
    double fragmentsHeight = size.height / columnLength;
    _fragments[i][j] = Rect.fromLTWH(fragmentsWidth * i, fragmentsHeight * j,
        fragmentsWidth, fragmentsHeight);
  }
  return _fragments[i][j];
}

///计算触摸点在当前坐标系中的位置.
Coordinate initCoordinate({
  Size size,
  int rowLength,
  int columnLength,
  Offset startingOffset = Offset.zero,
}) {
  double fragmentsWidth = size.width / rowLength;
  int x = ((startingOffset.dx ~/ fragmentsWidth)).clamp(0, rowLength);
  double fragmentsHeight = size.height / columnLength;
  int y = ((startingOffset.dy ~/ fragmentsHeight)).clamp(0, columnLength);
  return Coordinate(x: x, y: y);
}