作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
免责声明 :
import 'package:flutter/material.dart';
import 'package:angles/angles.dart';
import 'dart:math';
import 'dart:core';
class AnimatedCheck extends StatefulWidget {
final double size;
final Color color;
final double strokeWidth;
final VoidCallback onComplete;
const AnimatedCheck({
this.size = 80.0,
this.onComplete,
this.strokeWidth = 2.5,
this.color = Colors.green,
});
@override
_AnimatedCheckState createState() => _AnimatedCheckState();
}
class _AnimatedCheckState extends State<AnimatedCheck> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> curve;
bool completed = false;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: Duration(seconds: 2), vsync: this);
curve = CurvedAnimation(parent: _controller, curve: Curves.decelerate);
_controller.addListener(() {
if (_controller.status == AnimationStatus.completed && widget.onComplete != null) {
widget.onComplete();
}
if (_controller.status == AnimationStatus.completed) {
completed = true;
}
setState(() {});
});
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
fit: StackFit.passthrough,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 250),
height: widget.size,
width: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(
width: widget.strokeWidth,
color: completed ? widget.color : widget.color.withOpacity(.05),
),
),
),
Container(
height: widget.size - widget.strokeWidth,
width: widget.size - widget.strokeWidth,
child: CustomPaint(
painter: CheckPainter(
value: curve.value,
color: widget.color,
strokeWidth: widget.strokeWidth,
),
),
),
],
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class CheckPainter extends CustomPainter {
Paint _paint;
double value;
Color color;
double strokeWidth;
double _length;
double _offset;
double _secondOffset;
double _startingAngle;
CheckPainter({
this.value,
this.color,
this.strokeWidth,
}) {
_paint = Paint()
..color = color
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
assert(value != null);
_length = 60;
_offset = 0;
_startingAngle = 205;
}
@override
void paint(Canvas canvas, Size size) {
// Background canvas
var rect = Offset(0, 0) & size;
_paint.color = color.withOpacity(0);
double line1x1 = size.width / 2 + size.width * cos(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1y1 = size.height / 2 + size.height * sin(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1x2 = size.width * .45;
double line1y2 = size.height * .65;
double line2x1 = size.width / 2 + size.width * cos(Angle.fromDegrees(320).radians) * .35;
double line2y1 = size.height / 2 + size.height * sin(Angle.fromDegrees(320).radians) * .35;
canvas.drawArc(rect, Angle.fromDegrees(_startingAngle).radians, Angle.fromDegrees(360).radians, false, _paint);
canvas.drawLine(Offset(line1x1, line1y1), Offset(line1x2, line1y2), _paint);
canvas.drawLine(Offset(line2x1, line2y1), Offset(line1x2, line1y2), _paint);
// animation painter
double circleValue, checkValue;
if (value < .5) {
checkValue = 0;
circleValue = value / .5;
} else {
checkValue = (value - .5) / .5;
circleValue = 1;
}
_paint.color = color;
double firstAngle = _startingAngle + 360 * circleValue;
double line1Value = 0, line2Value = 0;
canvas.drawArc(rect, Angle.fromDegrees(firstAngle).radians,
Angle.fromDegrees(getSecondAngle(firstAngle, _length, _startingAngle + 360)).radians, false, _paint);
if (circleValue >= 1) {
if (checkValue < .5) {
line2Value = 0;
line1Value = checkValue / .5;
} else {
line2Value = (checkValue - .55) / .55;
line1Value = .75;
}
}
double auxLine1x1 = (line1x2 - line1x1) * getMin(line1Value, .8);
double auxLine1y1 = (((auxLine1x1) - line1x1) / (line1x2 - line1x1)) * (line1y2 - line1y1) + line1y1;
if (_offset < 60) {
auxLine1x1 = line1x1;
auxLine1y1 = line1y1;
}
double auxLine1x2 = auxLine1x1 + _offset / 2;
double auxLine1y2 = (((auxLine1x1 + _offset / 2) - line1x1) / (line1x2 - line1x1)) * (line1y2 - line1y1) + line1y1;
if (checkIfPointHasCrossedLine(
Offset(line1x2, line1y2), Offset(line2x1, line2y1), Offset(auxLine1x2, auxLine1y2))) {
auxLine1x2 = line1x2;
auxLine1y2 = line1y2;
}
if (_offset > 0) {
canvas.drawLine(Offset(auxLine1x1, auxLine1y1), Offset(auxLine1x2, auxLine1y2), _paint);
}
// SECOND LINE
double auxLine2x1 = (line2x1 - line1x2) * line2Value;
double auxLine2y1 =
((((line2x1 - line1x2) * line2Value) - line1x2) / (line2x1 - line1x2)) * (line2y1 - line1y2) + line1y2;
if (checkIfPointHasCrossedLine(
Offset(line1x1, line1y1), Offset(line1x2, line1y2), Offset(auxLine2x1, auxLine2y1))) {
auxLine2x1 = line1x2;
auxLine2y1 = line1y2;
}
if (line2Value > 0) {
canvas.drawLine(
Offset(auxLine2x1, auxLine2y1),
Offset(
(line2x1 - line1x2) * line2Value + _offset * .75,
((((line2x1 - line1x2) * line2Value + _offset * .75) - line1x2) / (line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2),
_paint);
}
}
double getMax(double x, double y) {
return (x > y) ? x : y;
}
double getMin(double x, double y) {
return (x > y) ? y : x;
}
bool checkIfPointHasCrossedLine(Offset a, Offset b, Offset point) {
return ((b.dx - a.dx) * (point.dy - a.dy) - (b.dy - a.dy) * (point.dx - a.dx)) > 0;
}
double getSecondAngle(double angle, double plus, double max) {
if (angle + plus > max) {
_offset = angle + plus - max;
return max - angle;
} else {
_offset = 0;
return plus;
}
}
@override
bool shouldRepaint(CheckPainter old) {
return old.value != value;
}
}
最佳答案
好像这和drawArc的web实现有关,下面的代码在ios上什么也没画,而是在web上画了一个点:
canvas.drawArc(rect, 0, 0, false, _paint);
您可以尝试有条件地绘制圆弧
if (circleValue < 1)
canvas.drawArc(
rect,
Angle.fromDegrees(firstAngle).radians,
Angle.fromDegrees(
getSecondAngle(firstAngle, _length, _startingAngle + 360))
.radians,
false,
_paint);
但首先你想移动
_offset
更新自
getSecondAngle(...)
到它自己的功能,因为它是两个独立的关注点
double getUpdatedOffset(double angle, double plus, double max) =>
angle + plus > max ? angle + plus - max : 0;
并在条件 drawArc 之后调用它
_offset = getUpdatedOffset(firstAngle, _length, _startingAngle + 360);
完整更新的代码:
import 'package:flutter/material.dart';
import 'package:angles/angles.dart';
import 'dart:math';
import 'dart:core';
class AnimatedCheck extends StatefulWidget {
final double size;
final Color color;
final double strokeWidth;
final VoidCallback onComplete;
const AnimatedCheck({
this.size = 80.0,
this.onComplete,
this.strokeWidth = 2.5,
this.color = Colors.green,
});
@override
_AnimatedCheckState createState() => _AnimatedCheckState();
}
class _AnimatedCheckState extends State<AnimatedCheck>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> curve;
bool completed = false;
@override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
curve = CurvedAnimation(parent: _controller, curve: Curves.decelerate);
_controller.addListener(() {
if (_controller.status == AnimationStatus.completed &&
widget.onComplete != null) {
widget.onComplete();
}
if (_controller.status == AnimationStatus.completed) {
completed = true;
}
setState(() {});
});
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
fit: StackFit.passthrough,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 250),
height: widget.size,
width: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(
width: widget.strokeWidth,
color: completed ? widget.color : widget.color.withOpacity(.05),
),
),
),
Container(
height: widget.size - widget.strokeWidth,
width: widget.size - widget.strokeWidth,
child: CustomPaint(
painter: CheckPainter(
value: curve.value,
color: widget.color,
strokeWidth: widget.strokeWidth,
),
),
),
],
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class CheckPainter extends CustomPainter {
Paint _paint;
double value;
Color color;
double strokeWidth;
double _length;
double _offset;
double _secondOffset;
double _startingAngle;
CheckPainter({
this.value,
this.color,
this.strokeWidth,
}) {
_paint = Paint()
..color = color
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
assert(value != null);
_length = 60;
_offset = 0;
_startingAngle = 205;
}
@override
void paint(Canvas canvas, Size size) {
// Background canvas
var rect = Offset(0, 0) & size;
_paint.color = color.withOpacity(0);
double line1x1 = size.width / 2 +
size.width * cos(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(_startingAngle).radians) * .5;
double line1x2 = size.width * .45;
double line1y2 = size.height * .65;
double line2x1 =
size.width / 2 + size.width * cos(Angle.fromDegrees(320).radians) * .35;
double line2y1 = size.height / 2 +
size.height * sin(Angle.fromDegrees(320).radians) * .35;
canvas.drawArc(rect, Angle.fromDegrees(_startingAngle).radians,
Angle.fromDegrees(360).radians, false, _paint);
canvas.drawLine(Offset(line1x1, line1y1), Offset(line1x2, line1y2), _paint);
canvas.drawLine(Offset(line2x1, line2y1), Offset(line1x2, line1y2), _paint);
// animation painter
double circleValue, checkValue;
if (value < .5) {
checkValue = 0;
circleValue = value / .5;
} else {
checkValue = (value - .5) / .5;
circleValue = 1;
}
_paint.color = color;
double firstAngle = _startingAngle + 360 * circleValue;
double line1Value = 0, line2Value = 0;
if (circleValue < 1)
canvas.drawArc(
rect,
Angle.fromDegrees(firstAngle).radians,
Angle.fromDegrees(
getSecondAngle(firstAngle, _length, _startingAngle + 360))
.radians,
false,
_paint);
_offset = getUpdatedOffset(firstAngle, _length, _startingAngle + 360);
if (circleValue >= 1) {
if (checkValue < .5) {
line2Value = 0;
line1Value = checkValue / .5;
} else {
line2Value = (checkValue - .55) / .55;
line1Value = .75;
}
}
double auxLine1x1 = (line1x2 - line1x1) * getMin(line1Value, .8);
double auxLine1y1 =
(((auxLine1x1) - line1x1) / (line1x2 - line1x1)) * (line1y2 - line1y1) +
line1y1;
if (_offset < 60) {
auxLine1x1 = line1x1;
auxLine1y1 = line1y1;
}
double auxLine1x2 = auxLine1x1 + _offset / 2;
double auxLine1y2 =
(((auxLine1x1 + _offset / 2) - line1x1) / (line1x2 - line1x1)) *
(line1y2 - line1y1) +
line1y1;
if (checkIfPointHasCrossedLine(Offset(line1x2, line1y2),
Offset(line2x1, line2y1), Offset(auxLine1x2, auxLine1y2))) {
auxLine1x2 = line1x2;
auxLine1y2 = line1y2;
}
if (_offset > 0) {
canvas.drawLine(Offset(auxLine1x1, auxLine1y1),
Offset(auxLine1x2, auxLine1y2), _paint);
}
// SECOND LINE
double auxLine2x1 = (line2x1 - line1x2) * line2Value;
double auxLine2y1 =
((((line2x1 - line1x2) * line2Value) - line1x2) / (line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2;
if (checkIfPointHasCrossedLine(Offset(line1x1, line1y1),
Offset(line1x2, line1y2), Offset(auxLine2x1, auxLine2y1))) {
auxLine2x1 = line1x2;
auxLine2y1 = line1y2;
}
if (line2Value > 0) {
canvas.drawLine(
Offset(auxLine2x1, auxLine2y1),
Offset(
(line2x1 - line1x2) * line2Value + _offset * .75,
((((line2x1 - line1x2) * line2Value + _offset * .75) - line1x2) /
(line2x1 - line1x2)) *
(line2y1 - line1y2) +
line1y2),
_paint);
}
}
double getMax(double x, double y) {
return (x > y) ? x : y;
}
double getMin(double x, double y) {
return (x > y) ? y : x;
}
bool checkIfPointHasCrossedLine(Offset a, Offset b, Offset point) {
return ((b.dx - a.dx) * (point.dy - a.dy) -
(b.dy - a.dy) * (point.dx - a.dx)) >
0;
}
double getSecondAngle(double angle, double plus, double max) =>
angle + plus > max ? max - angle : plus;
double getUpdatedOffset(double angle, double plus, double max) =>
angle + plus > max ? angle + plus - max : 0;
@override
bool shouldRepaint(CheckPainter old) {
return old.value != value;
}
}
关于android - 为什么我在 Flutter AnimatedChecked 中得到了这一点? (自定义油漆),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62986605/
我需要一个在 Windows-Mobile 上运行的获取签名示例。 (油漆) 如何在 Windows-Mobile 屏幕上绘图 - 并保存图片? 我可以获得示例代码 (C#) 吗? 最佳答案 Open
我是一名优秀的程序员,十分优秀!