mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This patch allows for an initial delay before an animation begins and also adds an Animation class which encapsulates a value which is long-lived, can be explicitly set and also animated from its current value to another value. BUG= R=abarth@chromium.org Review URL: https://codereview.chromium.org/994143002
160 lines
3.8 KiB
Dart
160 lines
3.8 KiB
Dart
part of widgets;
|
|
|
|
const double _kWidth = 256.0;
|
|
const double _kMinFlingVelocity = 0.4;
|
|
const double _kBaseSettleDurationMS = 246.0;
|
|
const double _kMaxSettleDurationMS = 600.0;
|
|
const Cubic _kAnimationCurve = easeOut;
|
|
|
|
class DrawerAnimation extends Animation {
|
|
|
|
Stream<double> get onPositionChanged => onValueChanged;
|
|
|
|
bool get _isMostlyClosed => value <= -_kWidth / 2;
|
|
|
|
DrawerAnimation() {
|
|
value = -_kWidth;
|
|
}
|
|
|
|
void toggle(_) => _isMostlyClosed ? _open() : _close();
|
|
|
|
void handleMaskTap(_) => _close();
|
|
|
|
void handlePointerDown(_) => stop();
|
|
|
|
void handlePointerMove(sky.PointerEvent event) {
|
|
assert(!isAnimating);
|
|
value = math.min(0.0, math.max(value + event.dx, -_kWidth));
|
|
}
|
|
|
|
void handlePointerUp(_) {
|
|
if (!isAnimating)
|
|
_settle();
|
|
}
|
|
|
|
void handlePointerCancel(_) {
|
|
if (!isAnimating)
|
|
_settle();
|
|
}
|
|
|
|
void _open() => _animateToPosition(0.0);
|
|
|
|
void _close() => _animateToPosition(-_kWidth);
|
|
|
|
void _settle() => _isMostlyClosed ? _close() : _open();
|
|
|
|
void _animateToPosition(double targetPosition) {
|
|
double distance = (targetPosition - value).abs();
|
|
if (distance != 0) {
|
|
double targetDuration = distance / _kWidth * _kBaseSettleDurationMS;
|
|
double duration = math.min(targetDuration, _kMaxSettleDurationMS);
|
|
animateTo(targetPosition, duration, curve: _kAnimationCurve);
|
|
}
|
|
}
|
|
|
|
void handleFlingStart(event) {
|
|
double direction = event.velocityX.sign;
|
|
double velocityX = event.velocityX.abs() / 1000;
|
|
if (velocityX < _kMinFlingVelocity)
|
|
return;
|
|
|
|
double targetPosition = direction < 0.0 ? -_kWidth : 0.0;
|
|
double distance = (targetPosition - value).abs();
|
|
double duration = distance / velocityX;
|
|
|
|
animateTo(targetPosition, duration, curve: linear);
|
|
}
|
|
}
|
|
|
|
class Drawer extends Component {
|
|
|
|
static Style _style = new Style('''
|
|
position: absolute;
|
|
z-index: 2;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
right: 0;
|
|
box-shadpw: ${Shadow[3]};'''
|
|
);
|
|
|
|
static Style _maskStyle = new Style('''
|
|
background-color: black;
|
|
will-change: opacity;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
right: 0;'''
|
|
);
|
|
|
|
static Style _contentStyle = new Style('''
|
|
background-color: ${Grey[50]};
|
|
will-change: transform;
|
|
position: absolute;
|
|
z-index: 3;
|
|
width: 256px;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;'''
|
|
);
|
|
|
|
DrawerAnimation animation;
|
|
List<Node> children;
|
|
|
|
Drawer({
|
|
Object key,
|
|
this.animation,
|
|
this.children
|
|
}) : super(key: key);
|
|
|
|
double _position = -_kWidth;
|
|
|
|
bool _listening = false;
|
|
|
|
void _ensureListening() {
|
|
if (_listening)
|
|
return;
|
|
|
|
_listening = true;
|
|
animation.onPositionChanged.listen((position) {
|
|
setState(() {
|
|
_position = position;
|
|
});
|
|
});
|
|
}
|
|
|
|
Node build() {
|
|
_ensureListening();
|
|
|
|
bool isClosed = _position <= -_kWidth;
|
|
String inlineStyle = 'display: ${isClosed ? 'none' : ''}';
|
|
String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}';
|
|
String contentInlineStyle = 'transform: translateX(${_position}px)';
|
|
|
|
Container mask = new Container(
|
|
key: 'Mask',
|
|
style: _maskStyle,
|
|
inlineStyle: maskInlineStyle
|
|
)..events.listen('gesturetap', animation.handleMaskTap)
|
|
..events.listen('gestureflingstart', animation.handleFlingStart);
|
|
|
|
Container content = new Container(
|
|
key: 'Content',
|
|
style: _contentStyle,
|
|
inlineStyle: contentInlineStyle,
|
|
children: children
|
|
);
|
|
|
|
return new Container(
|
|
style: _style,
|
|
inlineStyle: inlineStyle,
|
|
children: [ mask, content ]
|
|
)..events.listen('pointerdown', animation.handlePointerDown)
|
|
..events.listen('pointermove', animation.handlePointerMove)
|
|
..events.listen('pointerup', animation.handlePointerUp)
|
|
..events.listen('pointercancel', animation.handlePointerCancel);
|
|
|
|
}
|
|
}
|