提交 1b76bf43 编写于 作者: H Hixie

[Effen] Reduce splashes when scrolling.

Pipe the remaining time for an animation all the way out to the AnimatedValue.
Make splashes abortable, which causes them to continue to fade but no longer grow, by having them fade in 100ms or however long the animation was still going to go for, whichever is quickest.
Make Scrollables support objects registering with them to be told when scrolling happens.
Make UINode support subclasses being informed when _remove() was called.
Hook all that together to make splashes go away when scrolling by having them register with any ancestor Scrollables such that when those scroll, all the splashes get aborted. Unregister when removed.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/1092423003
上级 f646c8ac
......@@ -67,4 +67,11 @@ class AnimatedValue {
_completer = new Completer();
return _completer.future;
}
double get remainingTime {
if (_animation == null)
return 0.0;
return _animation.remainingTime;
}
}
......@@ -68,6 +68,7 @@ class AnimationGenerator extends Generator {
FrameGenerator _generator;
Stream<double> _stream;
bool _done = false;
double _lastTime;
AnimationGenerator({
this.initialDelay: 0.0,
......@@ -87,13 +88,20 @@ class AnimationGenerator extends Generator {
startTime = timeStamp;
double t = (timeStamp - (startTime + initialDelay)) / duration;
return math.max(0.0, math.min(t, 1.0));
_lastTime = math.max(0.0, math.min(t, 1.0));
return _lastTime;
})
.takeWhile(_checkForCompletion)
.where((t) => t >= 0.0)
.map(_transform);
}
double get remainingTime {
if (_lastTime == null)
return duration;
return duration - _lastTime;
}
void cancel() {
_generator.cancel();
}
......
......@@ -13,6 +13,8 @@ import 'dart:sky' as sky;
const double _kSplashConfirmedDuration = 350.0;
const double _kSplashUnconfirmedDuration = config.kDefaultLongPressTimeout;
const double _kSplashAbortDuration = 100.0;
const double _kSplashInitialDelay = 0.0; // we could delay initially in case the user scrolls
double _getSplashTargetSize(sky.ClientRect rect, double x, double y) {
return 2.0 * math.max(math.max(x - rect.left, rect.right - x),
......@@ -26,11 +28,13 @@ class SplashController {
final AnimatedValue _size = new AnimatedValue(0.0);
double _offsetX;
double _offsetY;
double _lastSize = 0.0;
bool _growing = true;
double _targetSize;
Stream<String> _styleStream;
void start() {
_size.animateTo(_targetSize, _kSplashUnconfirmedDuration, curve: easeOut);
_size.animateTo(_targetSize, _kSplashUnconfirmedDuration, curve: easeOut, initialDelay: _kSplashInitialDelay);
}
void confirm() {
......@@ -41,6 +45,14 @@ class SplashController {
_size.animateTo(_targetSize, duration, curve: easeOut);
}
void abort() {
_growing = false;
double durationRemaining = _size.remainingTime;
if (durationRemaining <= _kSplashAbortDuration)
return;
_size.animateTo(_targetSize, _kSplashAbortDuration, curve: easeOut);
}
void cancel() {
_size.stop();
}
......@@ -55,12 +67,19 @@ class SplashController {
if (p == _targetSize) {
onDone();
}
double size;
if (_growing) {
sise = p;
_lastSize = p;
} else {
size = _lastSize;
}
return '''
top: ${_offsetY - p/2}px;
left: ${_offsetX - p/2}px;
width: ${p}px;
height: ${p}px;
border-radius: ${p}px;
top: ${_offsetY - size/2}px;
left: ${_offsetX - size/2}px;
width: ${size}px;
height: ${size}px;
border-radius: ${size}px;
opacity: ${1.0 - (p / _targetSize)};''';
});
......
......@@ -6,8 +6,9 @@ import '../fn.dart';
import 'dart:collection';
import 'dart:sky' as sky;
import 'ink_splash.dart';
import 'scrollable.dart';
class InkWell extends Component {
class InkWell extends Component implements ScrollClient {
static final Style _containmentStyleHack = new Style('''
transform: translateX(0);''');
......@@ -55,9 +56,30 @@ class InkWell extends Component {
pointer: event.primaryPointer,
onDone: () { _splashDone(splash); });
_splashes.add(splash);
UINode node = parent;
while (node != null) {
if (node is Scrollable)
node.registerScrollClient(this);
node = node.parent;
}
});
}
bool ancestorScrolled(Scrollable ancestor) {
_abortSplashes();
return false;
}
void handleRemoved() {
UINode node = parent;
while (node != null) {
if (node is Scrollable)
node.unregisterScrollClient(this);
node = node.parent;
}
super.handleRemoved();
}
void _confirmSplash(sky.GestureEvent event) {
if (_splashes == null)
return;
......@@ -65,6 +87,14 @@ class InkWell extends Component {
.forEach((splash) { splash.confirm(); });
}
void _abortSplashes() {
if (_splashes == null)
return;
setState(() {
_splashes.forEach((s) { s.abort(); });
});
}
void _cancelSplashes(sky.Event event) {
if (_splashes == null)
return;
......
......@@ -17,6 +17,10 @@ double _velocityForFlingGesture(sky.GestureEvent event) {
-event.velocityY)) / _kMillisecondsPerSecond;
}
abstract class ScrollClient {
bool ancestorScrolled(Scrollable ancestor);
}
abstract class Scrollable extends Component {
ScrollBehavior scrollBehavior;
double get scrollOffset => _scrollOffset;
......@@ -43,12 +47,43 @@ abstract class Scrollable extends Component {
);
}
List<ScrollClient> _registeredScrollClients;
void registerScrollClient(ScrollClient notifiee) {
if (_registeredScrollClients == null)
_registeredScrollClients = new List<ScrollClient>();
setState(() {
_registeredScrollClients.add(notifiee);
});
}
void unregisterScrollClient(ScrollClient notifiee) {
if (_registeredScrollClients == null)
return;
setState(() {
_registeredScrollClients.remove(notifiee);
});
}
bool scrollTo(double newScrollOffset) {
if (newScrollOffset == _scrollOffset)
return false;
setState(() {
_scrollOffset = newScrollOffset;
});
if (_registeredScrollClients != null) {
var newList = null;
_registeredScrollClients.forEach((target) {
if (target.ancestorScrolled(this)) {
if (newList == null)
newList = new List<ScrollClient>();
newList.add(target);
}
});
setState(() {
_registeredScrollClients = newList;
});
}
return true;
}
......
......@@ -62,6 +62,7 @@ enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL }
abstract class UINode {
String _key;
UINode _parent;
UINode get parent => _parent;
sky.Node _root;
bool _defunct = false;
......@@ -78,7 +79,9 @@ abstract class UINode {
void _remove() {
_defunct = true;
_root = null;
handleRemoved();
}
void handleRemoved() { }
int _nodeDepth;
void _ensureDepth() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册