blob: 906f4f4939d33106a40712581a2f6f51c3e4bddd [file] [log] [blame]
<!--
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<import src="sky-element.sky" />
<import src="sky-scrollable.sky" />
<sky-element>
<template>
<style>
#mask {
background-color: black;
will-change: opacity;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
#content {
background-color: white;
will-change: transform;
position: absolute;
width: 256px;
top: 0;
left: 0;
bottom: 0;
}
</style>
<div id="mask" />
<div id="content">
<content/>
</div>
</template>
<script>
import "animation/controller.dart";
import "animation/curves.dart";
import "animation/timer.dart";
import "dart:math" as math;
import "dart:sky";
const double _kWidth = 256.0;
const double _kMinFlingVelocity = 0.4;
const double _kMinAnimationDurationMS = 246.0;
const double _kMaxAnimationDurationMS = 600.0;
const Cubic _kAnimationCurve = easeOut;
@Tagname('sky-drawer')
class SkyDrawer extends SkyElement implements AnimationDelegate {
Element _mask;
Element _content;
double _position = 0.0;
AnimationController _animation;
SkyDrawer() {
_animation = new AnimationController(this);
addEventListener('pointerdown', _handlePointerDown);
addEventListener('pointermove', _handlePointerMove);
addEventListener('pointerup', _handlePointerUp);
addEventListener('pointercancel', _handlePointerCancel);
}
void shadowRootReady() {
_mask = shadowRoot.getElementById('mask');
_mask.addEventListener('gesturetap', _handleMaskTap);
_content = shadowRoot.getElementById('content');
_content.addEventListener('gestureflingstart', _handleFlingStart);
position = -_kWidth;
}
void toggle() {
if (isMostlyClosed)
open();
else
close();
}
void open() {
_animateToPosition(0.0);
}
void close() {
_animateToPosition(-_kWidth);
}
bool get isClosed => _position <= -_kWidth;
bool get isMostlyClosed => _position <= -_kWidth / 2;
double get position => _position;
set position(double value) {
double newPosition = math.min(0.0, math.max(value, -_kWidth));
_position = newPosition;
_content.style['transform'] = 'translateX(${newPosition}px)';
_mask.style['opacity'] = '${(newPosition / _kWidth + 1) * 0.25}';
style['display'] = isClosed ? 'none' : '';
}
void _settle() {
if (isMostlyClosed)
close();
else
open();
}
void _animateToPosition(double targetPosition) {
double currentPosition = _position;
double distance = (targetPosition - currentPosition).abs();
double duration = _kMaxAnimationDurationMS * distance / _kWidth;
_animation.start(
begin: currentPosition,
end: targetPosition,
duration: math.max(_kMinAnimationDurationMS, duration),
curve: _kAnimationCurve);
}
void updateAnimation(double p) {
position = p;
}
void _handleMaskTap(_) {
close();
}
void _handlePointerDown(_) {
_animation.stop();
}
void _handlePointerMove(PointerEvent event) {
position += event.dx;
}
void _handlePointerUp(_) {
if (!_animation.isAnimating)
_settle();
}
void _handlePointerCancel(_) {
if (!_animation.isAnimating)
_settle();
}
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 currentPosition = _position;
double distance = (targetPosition - currentPosition).abs();
double duration = distance / velocityX;
_animation.start(
begin: currentPosition,
end: targetPosition,
duration: duration,
curve: linear);
}
}
_init(script) => register(script, SkyDrawer);
</script>
</sky-element>