You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
7.3 KiB

import 'dart:math' as Math;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:huixiang/utils/pixel_utils.dart';
class ExplosionWidget extends StatefulWidget {
final Widget child;
final Rect bound;
final String tag;
const ExplosionWidget({Key key, this.child, this.bound, this.tag})
: super(key: key);
@override
State<StatefulWidget> createState() {
return _ExplosionWidget();
}
}
class _ExplosionWidget extends State<ExplosionWidget>
with SingleTickerProviderStateMixin {
ByteData _byteData;
Size _imageSize;
AnimationController _animationController;
GlobalObjectKey globalKey;
@override
void initState() {
super.initState();
globalKey = GlobalObjectKey(widget.tag);
_animationController =
AnimationController(duration: Duration(milliseconds: 600), vsync: this);
}
@override
void dispose() {
super.dispose();
_animationController.dispose();
}
void onTap() {
if (_byteData == null || _imageSize == null) {
RenderRepaintBoundary boundary =
globalKey.currentContext.findRenderObject();
boundary.toImage().then((image) {
_imageSize = Size(image.width.toDouble(), image.height.toDouble());
image.toByteData().then((byteData) {
_byteData = byteData;
_animationController.value = 0;
_animationController.forward();
setState(() {});
});
});
} else {
_animationController.value = 0;
_animationController.forward();
setState(() {});
}
}
@override
void didUpdateWidget(ExplosionWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.tag != oldWidget.tag) {
_byteData = null;
_imageSize = null;
globalKey = GlobalObjectKey(widget.tag);
}
}
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: GestureDetector(
onTap: onTap,
child: AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return ExplosionRenderObjectWidget(
key: globalKey,
child: widget.child,
byteData: _byteData,
imageSize: _imageSize,
progress: _animationController.value,
);
},
),
),
);
}
}
class ExplosionRenderObjectWidget extends RepaintBoundary {
final ByteData byteData;
final Size imageSize;
final double progress;
final Rect bound;
const ExplosionRenderObjectWidget(
{Key key,
Widget child,
this.byteData,
this.imageSize,
this.progress,
this.bound})
: super(key: key, child: child);
@override
_ExplosionRenderObject createRenderObject(BuildContext context) =>
_ExplosionRenderObject(
byteData: byteData, imageSize: imageSize, bound: bound);
@override
void updateRenderObject(
BuildContext context, _ExplosionRenderObject renderObject) {
renderObject.update(byteData, imageSize, progress);
}
}
class _ExplosionRenderObject extends RenderRepaintBoundary {
ByteData byteData;
Size imageSize;
double progress;
List<_Particle> particles;
Rect bound;
_ExplosionRenderObject(
{this.byteData, this.imageSize, this.bound, RenderBox child})
: super(child: child);
void update(ByteData byteData, Size imageSize, double progress) {
this.byteData = byteData;
this.imageSize = imageSize;
this.progress = progress;
markNeedsPaint();
}
@override
void paint(PaintingContext context, Offset offset) {
if (byteData != null &&
imageSize != null &&
progress != 0 &&
progress != 1) {
if (particles == null) {
if (bound == null) {
bound = Rect.fromLTWH(0, 0, size.width, size.height * 2);
}
particles = initParticleList(bound, byteData, imageSize);
}
draw(context.canvas, particles, progress);
} else {
if (child != null) {
context.paintChild(child, offset);
}
}
}
}
const double END_VALUE = 1.4;
const double V = 2;
const double X = 5;
const double Y = 20;
const double W = 1;
List<_Particle> initParticleList(
Rect bound, ByteData byteData, Size imageSize) {
int partLen = 15;
List<_Particle> particles = List(partLen * partLen);
Math.Random random = new Math.Random(DateTime.now().millisecondsSinceEpoch);
int w = imageSize.width ~/ (partLen + 2);
int h = imageSize.height ~/ (partLen + 2);
for (int i = 0; i < partLen; i++) {
for (int j = 0; j < partLen; j++) {
particles[(i * partLen) + j] = generateParticle(
getColorByPixel(byteData, imageSize,
Offset((j + 1) * w.toDouble(), (i + 1) * h.toDouble())),
random,
bound);
}
}
return particles;
}
bool draw(Canvas canvas, List<_Particle> particles, double progress) {
Paint paint = Paint();
for (int i = 0; i < particles.length; i++) {
_Particle particle = particles[i];
particle.advance(progress);
if (particle.alpha > 0) {
paint.color = particle.color
.withAlpha((particle.color.alpha * particle.alpha).toInt());
canvas.drawCircle(
Offset(particle.cx, particle.cy), particle.radius, paint);
}
}
return true;
}
_Particle generateParticle(Color color, Math.Random random, Rect bound) {
_Particle particle = _Particle();
particle.color = color;
particle.radius = V;
if (random.nextDouble() < 0.2) {
particle.baseRadius = V + ((X - V) * random.nextDouble());
} else {
particle.baseRadius = W + ((V - W) * random.nextDouble());
}
double nextDouble = random.nextDouble();
particle.top = bound.height * ((0.18 * random.nextDouble()) + 0.2);
particle.top = nextDouble < 0.2
? particle.top
: particle.top + ((particle.top * 0.2) * random.nextDouble());
particle.bottom = (bound.height * (random.nextDouble() - 0.5)) * 1.8;
double f = nextDouble < 0.2
? particle.bottom
: nextDouble < 0.8
? particle.bottom * 0.6
: particle.bottom * 0.3;
particle.bottom = f;
particle.mag = 4.0 * particle.top / particle.bottom;
particle.neg = (-particle.mag) / particle.bottom;
f = bound.center.dx + (Y * (random.nextDouble() - 0.5));
particle.baseCx = f;
particle.cx = f;
f = bound.center.dy + (Y * (random.nextDouble() - 0.5));
particle.baseCy = f;
particle.cy = f;
particle.life = END_VALUE / 10 * random.nextDouble();
particle.overflow = 0.4 * random.nextDouble();
particle.alpha = 1;
return particle;
}
class _Particle {
double alpha;
Color color;
double cx;
double cy;
double radius;
double baseCx;
double baseCy;
double baseRadius;
double top;
double bottom;
double mag;
double neg;
double life;
double overflow;
void advance(double factor) {
double f = 0;
double normalization = factor / END_VALUE;
if (normalization < life || normalization > 1 - overflow) {
alpha = 0;
return;
}
normalization = (normalization - life) / (1 - life - overflow);
double f2 = normalization * END_VALUE;
if (normalization >= 0.7) {
f = (normalization - 0.7) / 0.3;
}
alpha = 1 - f;
f = bottom * f2;
cx = baseCx + f;
cy = (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
radius = V + (baseRadius - V) * f2;
}
}