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);
}