提交 dd1df9f5 编写于 作者: C Collin Jackson

Animate checkboxes. I based it on the dialer app. Could probably use more...

Animate checkboxes. I based it on the dialer app. Could probably use more improvement, but I think this is an improvement on what we are doing currently.

R=abarth@chromium.org, abarth

Review URL: https://codereview.chromium.org/1182743002.
上级 518ecd24
......@@ -6,22 +6,54 @@ import 'dart:sky' as sky;
import 'package:sky/framework/theme2/colors.dart' as colors;
import '../animation/animated_value.dart';
import '../animation/curves.dart';
import '../rendering/box.dart';
import 'button_base.dart';
import 'animated_component.dart';
import 'wrappers.dart';
typedef void ValueChanged(value);
class Checkbox extends ButtonBase {
const double _kMidpoint = 0.5;
const double _kCheckDuration = 200.0;
const sky.Color _kUncheckedColor = const sky.Color(0x8A000000);
// TODO(jackson): This should change colors with the theme
sky.Color _kCheckedColor = colors.Purple[500];
const double _kEdgeSize = 20.0;
const double _kEdgeRadius = 1.0;
Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
class Checkbox extends AnimatedComponent {
Checkbox({
Object key,
this.checked,
this.onChanged
}) : super(key: key) {
_checkedAnimation = new AnimatedValue(checked ? 1.0 : 0.0);
}
bool checked;
AnimatedValue _checkedAnimation;
ValueChanged onChanged;
void syncFields(Checkbox source) {
checked = source.checked;
onChanged = source.onChanged;
if (checked != source.checked) {
checked = source.checked;
double targetValue = checked ? 1.0 : 0.0;
double difference = (_checkedAnimation.value - targetValue).abs();
if (difference > 0) {
double duration = difference * _kCheckDuration;
_checkedAnimation.stop();
Curve curve;
if (targetValue > _checkedAnimation.value) {
curve = easeIn;
} else {
curve = easeOut;
}
_checkedAnimation.animateTo(targetValue, duration, curve: curve);
}
}
super.syncFields(source);
}
......@@ -29,37 +61,68 @@ class Checkbox extends ButtonBase {
onChanged(!checked);
}
UINode buildContent() {
// TODO(jackson): This should change colors with the theme
sky.Color color = highlight ? colors.Purple[500] : const sky.Color(0x8A000000);
const double kEdgeSize = 20.0;
const double kEdgeRadius = 1.0;
UINode build() {
return new EventListenerNode(
new Container(
margin: const EdgeDims.symmetric(horizontal: 5.0),
width: kEdgeSize + 2.0,
height: kEdgeSize + 2.0,
width: _kEdgeSize + 2.0,
height: _kEdgeSize + 2.0,
child: new CustomPaint(
token: _checkedAnimation.value,
callback: (sky.Canvas canvas, Size size) {
// Choose a color between grey and the theme color
sky.Paint paint = new sky.Paint()..strokeWidth = 2.0
..color = _kUncheckedColor;
sky.Paint paint = new sky.Paint()..color = color
..strokeWidth = 2.0;
// The rrect contracts slightly during the animation
double inset = 2.0 - (_checkedAnimation.value - _kMidpoint).abs() * 2.0;
sky.Rect rect = new sky.Rect.fromLTRB(inset, inset, _kEdgeSize - inset, _kEdgeSize - inset);
sky.RRect rrect = new sky.RRect()..setRectXY(rect, _kEdgeRadius, _kEdgeRadius);
// Draw the outer rrect
paint.setStyle(checked ? sky.PaintingStyle.strokeAndFill : sky.PaintingStyle.stroke);
sky.Rect rect = new sky.Rect.fromLTRB(0.0, 0.0, kEdgeSize, kEdgeSize);
sky.RRect rrect = new sky.RRect()..setRectXY(rect, kEdgeRadius, kEdgeRadius);
// Outline of the empty rrect
paint.setStyle(sky.PaintingStyle.stroke);
canvas.drawRRect(rrect, paint);
// Draw the inner check
if (checked) {
// TODO(jackson): Use the theme color
// Radial gradient that changes size
if (_checkedAnimation.value > 0) {
paint.setStyle(sky.PaintingStyle.fill);
paint.setShader(
new sky.Gradient.radial(
new Point(_kEdgeSize / 2.0, _kEdgeSize / 2.0),
_kEdgeSize * (_kMidpoint - _checkedAnimation.value) * 8.0,
[const sky.Color(0x00000000), _kUncheckedColor],
[0.0, 1.0]
)
);
canvas.drawRRect(rrect, paint);
}
if (_checkedAnimation.value > _kMidpoint) {
double t = (_checkedAnimation.value - _kMidpoint) / (1.0 - _kMidpoint);
// Solid filled rrect
paint.setStyle(sky.PaintingStyle.strokeAndFill);
paint.color = new Color.fromARGB((t * 255).floor(),
_kCheckedColor.red,
_kCheckedColor.green,
_kCheckedColor.blue);
canvas.drawRRect(rrect, paint);
// White inner check
paint.color = const sky.Color(0xFFFFFFFF);
paint.setStyle(sky.PaintingStyle.stroke);
sky.Path path = new sky.Path();
path.moveTo(kEdgeSize * 0.2, kEdgeSize * 0.5);
path.lineTo(kEdgeSize * 0.4, kEdgeSize * 0.7);
path.lineTo(kEdgeSize * 0.8, kEdgeSize * 0.3);
sky.Point start = new sky.Point(_kEdgeSize * 0.2, _kEdgeSize * 0.5);
sky.Point mid = new sky.Point(_kEdgeSize * 0.4, _kEdgeSize * 0.7);
sky.Point end = new sky.Point(_kEdgeSize * 0.8, _kEdgeSize * 0.3);
Point lerp(Point p1, Point p2, double t)
=> new Point(p1.x * (1.0 - t) + p2.x * t, p1.y * (1.0 - t) + p2.y * t);
sky.Point drawStart = lerp(start, mid, 1.0 - t);
sky.Point drawEnd = lerp(mid, end, t);
path.moveTo(drawStart.x, drawStart.y);
path.lineTo(mid.x, mid.y);
path.lineTo(drawEnd.x, drawEnd.y);
canvas.drawPath(path, paint);
}
}
......
......@@ -49,19 +49,21 @@ class DecoratedBox extends OneChildRenderObjectWrapper {
}
// TODO(jackson) need a mechanism for marking the RenderCustomPaint as needing paint
class CustomPaint extends OneChildRenderObjectWrapper {
CustomPaint({ this.callback, UINode child, Object key })
CustomPaint({ this.callback, this.token, UINode child, Object key })
: super(child: child, key: key);
RenderCustomPaint get root { RenderCustomPaint result = super.root; return result; }
final CustomPaintCallback callback;
final dynamic token; // set this to be repainted automatically when the token changes
RenderCustomPaint createNode() => new RenderCustomPaint(callback: callback);
void syncRenderObject(CustomPaint old) {
super.syncRenderObject(old);
if (old != null && old.token != token)
root.markNeedsPaint();
root.callback = callback;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册