|
@@ -53,27 +53,6 @@ class ProgressPainter extends CustomPainter { |
|
@@ -53,27 +53,6 @@ class ProgressPainter extends CustomPainter { |
|
53
|
width: wh - strokeWidth,
|
53
|
width: wh - strokeWidth,
|
|
54
|
height: wh - strokeWidth,
|
54
|
height: wh - strokeWidth,
|
|
55
|
);
|
55
|
);
|
|
56
|
- int colorRangeIndex = (sweepAngle / 360).ceil() - 1;
|
|
|
|
57
|
- if (colorRangeIndex > progressColor.length - 1) {
|
|
|
|
58
|
- colorRangeIndex = progressColor.length - 1;
|
|
|
|
59
|
- }
|
|
|
|
60
|
- if (colorRangeIndex < 0) {
|
|
|
|
61
|
- colorRangeIndex = 0;
|
|
|
|
62
|
- }
|
|
|
|
63
|
-
|
|
|
|
64
|
- var shader = SweepGradient(
|
|
|
|
65
|
- startAngle: 0.toRadians(),
|
|
|
|
66
|
- endAngle: 360.toRadians(),
|
|
|
|
67
|
- colors: reverse
|
|
|
|
68
|
- ? progressColor[colorRangeIndex].reversed.toList()
|
|
|
|
69
|
- : progressColor[colorRangeIndex],
|
|
|
|
70
|
- ).createShader(rect);
|
|
|
|
71
|
-
|
|
|
|
72
|
- var paint = Paint()
|
|
|
|
73
|
- ..strokeWidth = strokeWidth
|
|
|
|
74
|
- ..shader = shader
|
|
|
|
75
|
- ..strokeCap = StrokeCap.butt
|
|
|
|
76
|
- ..style = PaintingStyle.stroke;
|
|
|
|
77
|
|
56
|
|
|
78
|
var backgroundPaint = Paint()
|
57
|
var backgroundPaint = Paint()
|
|
79
|
..strokeWidth = strokeWidth
|
58
|
..strokeWidth = strokeWidth
|
|
@@ -89,49 +68,93 @@ class ProgressPainter extends CustomPainter { |
|
@@ -89,49 +68,93 @@ class ProgressPainter extends CustomPainter { |
|
89
|
backgroundPaint,
|
68
|
backgroundPaint,
|
|
90
|
);
|
69
|
);
|
|
91
|
|
70
|
|
|
92
|
- if (sweepAngle >= 0) {
|
71
|
+ if (sweepAngle > 0) {
|
|
93
|
double capAngle = 0;
|
72
|
double capAngle = 0;
|
|
94
|
if (radius > 0) {
|
73
|
if (radius > 0) {
|
|
95
|
capAngle = asin((strokeWidth / 2) / radius) * 180 / pi;
|
74
|
capAngle = asin((strokeWidth / 2) / radius) * 180 / pi;
|
|
96
|
}
|
75
|
}
|
|
97
|
|
76
|
|
|
98
|
- double actualCapAngle = capAngle;
|
|
|
|
99
|
- if (sweepAngle < 360) {
|
|
|
|
100
|
- if (sweepAngle < 2 * capAngle) {
|
|
|
|
101
|
- actualCapAngle = sweepAngle / 2;
|
77
|
+ int fullLaps = (sweepAngle / 360).floor();
|
|
|
|
78
|
+ double remainingAngle = sweepAngle % 360;
|
|
|
|
79
|
+
|
|
|
|
80
|
+ // Draw full laps first
|
|
|
|
81
|
+ for (int i = 0; i < fullLaps; i++) {
|
|
|
|
82
|
+ int colorIdx = i;
|
|
|
|
83
|
+ if (colorIdx > progressColor.length - 1) {
|
|
|
|
84
|
+ colorIdx = progressColor.length - 1;
|
|
102
|
}
|
85
|
}
|
|
103
|
- } else {
|
|
|
|
104
|
- actualCapAngle = 0;
|
86
|
+ var shader = SweepGradient(
|
|
|
|
87
|
+ startAngle: 0.toRadians(),
|
|
|
|
88
|
+ endAngle: 360.toRadians(),
|
|
|
|
89
|
+ colors: reverse
|
|
|
|
90
|
+ ? progressColor[colorIdx].reversed.toList()
|
|
|
|
91
|
+ : progressColor[colorIdx],
|
|
|
|
92
|
+ ).createShader(rect);
|
|
|
|
93
|
+
|
|
|
|
94
|
+ var lapPaint = Paint()
|
|
|
|
95
|
+ ..strokeWidth = strokeWidth
|
|
|
|
96
|
+ ..shader = shader
|
|
|
|
97
|
+ ..strokeCap = StrokeCap.butt
|
|
|
|
98
|
+ ..style = PaintingStyle.stroke;
|
|
|
|
99
|
+
|
|
|
|
100
|
+ canvas.save();
|
|
|
|
101
|
+ canvas.translate(centerX, centerY);
|
|
|
|
102
|
+ canvas.rotate((-90).toRadians());
|
|
|
|
103
|
+ canvas.translate(-centerX, -centerY);
|
|
|
|
104
|
+
|
|
|
|
105
|
+ canvas.drawArc(
|
|
|
|
106
|
+ rect,
|
|
|
|
107
|
+ 0.toRadians(),
|
|
|
|
108
|
+ reverse ? -360.toRadians() : 360.toRadians(),
|
|
|
|
109
|
+ false,
|
|
|
|
110
|
+ lapPaint,
|
|
|
|
111
|
+ );
|
|
|
|
112
|
+
|
|
|
|
113
|
+ canvas.restore();
|
|
105
|
}
|
114
|
}
|
|
106
|
|
115
|
|
|
107
|
- double startAngle = actualCapAngle;
|
|
|
|
108
|
- double drawnSweepAngle = sweepAngle;
|
|
|
|
109
|
- if (sweepAngle < 360) {
|
|
|
|
110
|
- drawnSweepAngle = sweepAngle - 2 * actualCapAngle;
|
116
|
+ // Draw the remaining fractional lap
|
|
|
|
117
|
+ if (remainingAngle > 0.0) {
|
|
|
|
118
|
+ int colorIdx = fullLaps;
|
|
|
|
119
|
+ if (colorIdx > progressColor.length - 1) {
|
|
|
|
120
|
+ colorIdx = progressColor.length - 1;
|
|
|
|
121
|
+ }
|
|
|
|
122
|
+
|
|
|
|
123
|
+ var shader = SweepGradient(
|
|
|
|
124
|
+ startAngle: 0.toRadians(),
|
|
|
|
125
|
+ endAngle: 360.toRadians(),
|
|
|
|
126
|
+ colors: reverse
|
|
|
|
127
|
+ ? progressColor[colorIdx].reversed.toList()
|
|
|
|
128
|
+ : progressColor[colorIdx],
|
|
|
|
129
|
+ ).createShader(rect);
|
|
|
|
130
|
+
|
|
|
|
131
|
+ var lapPaint = Paint()
|
|
|
|
132
|
+ ..strokeWidth = strokeWidth
|
|
|
|
133
|
+ ..shader = shader
|
|
|
|
134
|
+ ..strokeCap = StrokeCap.butt
|
|
|
|
135
|
+ ..style = PaintingStyle.stroke;
|
|
|
|
136
|
+
|
|
|
|
137
|
+ double actualCapAngle = capAngle;
|
|
|
|
138
|
+ if (remainingAngle < 2 * capAngle) {
|
|
|
|
139
|
+ actualCapAngle = remainingAngle / 2;
|
|
|
|
140
|
+ }
|
|
|
|
141
|
+
|
|
|
|
142
|
+ double startAngle = actualCapAngle;
|
|
|
|
143
|
+ double drawnSweepAngle = remainingAngle - 2 * actualCapAngle;
|
|
111
|
if (drawnSweepAngle < 0) {
|
144
|
if (drawnSweepAngle < 0) {
|
|
112
|
drawnSweepAngle = 0;
|
145
|
drawnSweepAngle = 0;
|
|
113
|
}
|
146
|
}
|
|
114
|
- }
|
|
|
|
115
|
|
147
|
|
|
116
|
- canvas.save();
|
|
|
|
117
|
- // Move the canvas origin to the center
|
|
|
|
118
|
- canvas.translate(centerX, centerY);
|
|
|
|
119
|
- double rotationAngle = 0;
|
148
|
+ canvas.save();
|
|
|
|
149
|
+ canvas.translate(centerX, centerY);
|
|
|
|
150
|
+ canvas.rotate((-90).toRadians());
|
|
|
|
151
|
+ canvas.translate(-centerX, -centerY);
|
|
120
|
|
152
|
|
|
121
|
- if (sweepAngle > 360) {
|
|
|
|
122
|
- rotationAngle = 360 - sweepAngle;
|
|
|
|
123
|
- }
|
|
|
|
124
|
- double rotationRadians =
|
|
|
|
125
|
- reverse ? -rotationAngle.toRadians() : rotationAngle.toRadians();
|
|
|
|
126
|
- // Rotate the canvas
|
|
|
|
127
|
- canvas.rotate((-90).toRadians() - rotationRadians);
|
|
|
|
128
|
- // Move the canvas origin back
|
|
|
|
129
|
- canvas.translate(-centerX, -centerY);
|
|
|
|
130
|
-
|
|
|
|
131
|
- if (sweepAngle < 360) {
|
153
|
+ // Draw start cap for this fractional lap
|
|
132
|
canvas.save();
|
154
|
canvas.save();
|
|
133
|
canvas.translate(centerX, centerY);
|
155
|
canvas.translate(centerX, centerY);
|
|
134
|
- canvas.rotate(reverse ? -startAngle.toRadians() : startAngle.toRadians());
|
156
|
+ canvas
|
|
|
|
157
|
+ .rotate(reverse ? -startAngle.toRadians() : startAngle.toRadians());
|
|
135
|
canvas.drawArc(
|
158
|
canvas.drawArc(
|
|
136
|
Rect.fromCenter(
|
159
|
Rect.fromCenter(
|
|
137
|
center: Offset(radius, 0),
|
160
|
center: Offset(radius, 0),
|
|
@@ -141,7 +164,10 @@ class ProgressPainter extends CustomPainter { |
|
@@ -141,7 +164,10 @@ class ProgressPainter extends CustomPainter { |
|
141
|
0,
|
164
|
0,
|
|
142
|
reverse ? 180.toRadians() : -180.toRadians(),
|
165
|
reverse ? 180.toRadians() : -180.toRadians(),
|
|
143
|
true,
|
166
|
true,
|
|
144
|
- Paint()..color = progressColor[0].first,
|
167
|
+ Paint()
|
|
|
|
168
|
+ ..color = reverse
|
|
|
|
169
|
+ ? progressColor[colorIdx].last
|
|
|
|
170
|
+ : progressColor[colorIdx].first,
|
|
145
|
);
|
171
|
);
|
|
146
|
canvas.drawRect(
|
172
|
canvas.drawRect(
|
|
147
|
Rect.fromCenter(
|
173
|
Rect.fromCenter(
|
|
@@ -149,31 +175,37 @@ class ProgressPainter extends CustomPainter { |
|
@@ -149,31 +175,37 @@ class ProgressPainter extends CustomPainter { |
|
149
|
width: strokeWidth,
|
175
|
width: strokeWidth,
|
|
150
|
height: 1,
|
176
|
height: 1,
|
|
151
|
),
|
177
|
),
|
|
152
|
- Paint()..color = progressColor[0].first,
|
178
|
+ Paint()
|
|
|
|
179
|
+ ..color = reverse
|
|
|
|
180
|
+ ? progressColor[colorIdx].last
|
|
|
|
181
|
+ : progressColor[colorIdx].first,
|
|
153
|
);
|
182
|
);
|
|
154
|
canvas.restore();
|
183
|
canvas.restore();
|
|
155
|
- }
|
|
|
|
156
|
|
184
|
|
|
157
|
- canvas.drawArc(
|
|
|
|
158
|
- rect,
|
|
|
|
159
|
- reverse ? -startAngle.toRadians() : startAngle.toRadians(),
|
|
|
|
160
|
- reverse
|
|
|
|
161
|
- ? -drawnSweepAngle.toRadians()
|
|
|
|
162
|
- : drawnSweepAngle.toRadians(),
|
|
|
|
163
|
- false,
|
|
|
|
164
|
- paint,
|
|
|
|
165
|
- );
|
|
|
|
166
|
-
|
|
|
|
167
|
- if (sweepAngle != 360) {
|
185
|
+ // Draw the fractional arc
|
|
|
|
186
|
+ canvas.drawArc(
|
|
|
|
187
|
+ rect,
|
|
|
|
188
|
+ reverse ? -startAngle.toRadians() : startAngle.toRadians(),
|
|
|
|
189
|
+ reverse ? -drawnSweepAngle.toRadians() : drawnSweepAngle.toRadians(),
|
|
|
|
190
|
+ false,
|
|
|
|
191
|
+ lapPaint,
|
|
|
|
192
|
+ );
|
|
|
|
193
|
+
|
|
|
|
194
|
+ // Draw end cap for this fractional lap
|
|
168
|
final endAngle = startAngle + drawnSweepAngle;
|
195
|
final endAngle = startAngle + drawnSweepAngle;
|
|
169
|
canvas.save();
|
196
|
canvas.save();
|
|
170
|
canvas.translate(centerX, centerY);
|
197
|
canvas.translate(centerX, centerY);
|
|
171
|
canvas.rotate(reverse ? -endAngle.toRadians() : endAngle.toRadians());
|
198
|
canvas.rotate(reverse ? -endAngle.toRadians() : endAngle.toRadians());
|
|
172
|
|
199
|
|
|
173
|
- final endColor =
|
|
|
|
174
|
- getColorAtAngle(sweepAngle, progressColor[colorRangeIndex]);
|
|
|
|
175
|
-
|
|
|
|
176
|
- final bool shouldDrawShadow = sweepAngle > 360 || (sweepAngle > 355 && sweepAngle < 360);
|
200
|
+ final endColor = getColorAtAngleOf360(
|
|
|
|
201
|
+ remainingAngle,
|
|
|
|
202
|
+ reverse
|
|
|
|
203
|
+ ? progressColor[colorIdx].reversed.toList()
|
|
|
|
204
|
+ : progressColor[colorIdx],
|
|
|
|
205
|
+ );
|
|
|
|
206
|
+
|
|
|
|
207
|
+ final bool shouldDrawShadow =
|
|
|
|
208
|
+ sweepAngle > 360 || (remainingAngle > 355 && remainingAngle < 360);
|
|
177
|
if (shouldDrawShadow) {
|
209
|
if (shouldDrawShadow) {
|
|
178
|
//Draw arc shadow
|
210
|
//Draw arc shadow
|
|
179
|
var shadowPosition = Offset(
|
211
|
var shadowPosition = Offset(
|
|
@@ -208,10 +240,9 @@ class ProgressPainter extends CustomPainter { |
|
@@ -208,10 +240,9 @@ class ProgressPainter extends CustomPainter { |
|
208
|
Paint()..color = endColor,
|
240
|
Paint()..color = endColor,
|
|
209
|
);
|
241
|
);
|
|
210
|
canvas.restore();
|
242
|
canvas.restore();
|
|
211
|
- }
|
|
|
|
212
|
|
243
|
|
|
213
|
- // // Restore the canvas to the saved state
|
|
|
|
214
|
- canvas.restore();
|
244
|
+ canvas.restore();
|
|
|
|
245
|
+ }
|
|
215
|
}
|
246
|
}
|
|
216
|
}
|
247
|
}
|
|
217
|
|
248
|
|
|
@@ -220,16 +251,19 @@ class ProgressPainter extends CustomPainter { |
|
@@ -220,16 +251,19 @@ class ProgressPainter extends CustomPainter { |
|
220
|
return sweepAngle != oldDelegate.sweepAngle;
|
251
|
return sweepAngle != oldDelegate.sweepAngle;
|
|
221
|
}
|
252
|
}
|
|
222
|
|
253
|
|
|
223
|
- /// get color by and angle
|
|
|
|
224
|
- Color getColorAtAngle(double angle, List<Color> colors) {
|
|
|
|
225
|
- double totalAngle = sweepAngle > 360 ? sweepAngle : 360;
|
|
|
|
226
|
- double fraction = angle / totalAngle;
|
254
|
+ /// get color by angle of a single 360 degree span
|
|
|
|
255
|
+ Color getColorAtAngleOf360(double angle, List<Color> colors) {
|
|
|
|
256
|
+ double fraction = angle / 360.0;
|
|
|
|
257
|
+ if (fraction > 1.0) fraction = 1.0;
|
|
|
|
258
|
+ if (fraction < 0.0) fraction = 0.0;
|
|
227
|
int segments = colors.length - 1;
|
259
|
int segments = colors.length - 1;
|
|
|
|
260
|
+ if (segments <= 0) return colors.first;
|
|
228
|
double segmentFraction = fraction * segments;
|
261
|
double segmentFraction = fraction * segments;
|
|
229
|
int segmentIndex = segmentFraction.floor();
|
262
|
int segmentIndex = segmentFraction.floor();
|
|
|
|
263
|
+ if (segmentIndex >= segments) segmentIndex = segments - 1;
|
|
230
|
double segmentInnerFraction = segmentFraction - segmentIndex;
|
264
|
double segmentInnerFraction = segmentFraction - segmentIndex;
|
|
231
|
Color startColor = colors[segmentIndex];
|
265
|
Color startColor = colors[segmentIndex];
|
|
232
|
- Color endColor = colors[(segmentIndex + 1) % colors.length];
|
266
|
+ Color endColor = colors[segmentIndex + 1];
|
|
233
|
return Color.lerp(startColor, endColor, segmentInnerFraction)!;
|
267
|
return Color.lerp(startColor, endColor, segmentInnerFraction)!;
|
|
234
|
}
|
268
|
}
|
|
235
|
} |
269
|
} |