Index: sky/framework/sky-scrollable.sky |
diff --git a/sky/framework/sky-scrollable.sky b/sky/framework/sky-scrollable.sky |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f950c8b470aafc548b8751a405597c2186d4bb7e |
--- /dev/null |
+++ b/sky/framework/sky-scrollable.sky |
@@ -0,0 +1,101 @@ |
+<!-- |
+// 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/framework/sky-element/sky-element.sky" as="SkyElement" /> |
+ |
+<sky-element |
+ name="sky-scrollable" |
+ on-gesturescrollstart="handleScrollStart_" |
+ on-gesturescrollend="handleScrollEnd_" |
+ on-gesturescrollupdate="handleScrollUpdate_" |
+ on-gestureflingstart="handleFlingStart_" |
+ on-gestureflingcancel="handleFlingCancel_"> |
+<template> |
+ <style> |
+ :host { |
+ overflow: hidden; |
+ } |
+ #scrollable { |
+ transform: translateY(0); |
+ } |
+ </style> |
+ <div id="scrollable"> |
+ <content /> |
+ </div> |
+</template> |
+<script> |
+// TODO(abarth): Move the fling curve to a separate module. |
+var kFlingFriction = 0.9; |
+var kFlingVelocityMin = 1; |
+ |
+module.exports = class extends SkyElement { |
+ created() { |
+ this.scrollable_ = null; |
+ this.scrollOffset_ = 0; |
+ this.flingVelocity_ = 0; |
+ this.flingAnimationId_ = null; |
+ } |
+ |
+ shadowRootReady() { |
+ this.scrollable_ = this.shadowRoot.getElementById('scrollable'); |
+ } |
+ |
+ scrollBy(scrollDelta) { |
+ var offset = Math.max(0, Math.min(this.scrollable_.offsetHeight, this.scrollOffset_ + scrollDelta)); |
+ if (offset == this.scrollOffset_) |
+ return false; |
+ this.scrollOffset_ = offset; |
+ this.applyScrollOffset_(); |
+ return true; |
+ } |
+ |
+ applyScrollOffset_() { |
+ var transform = 'translateY(' + -this.scrollOffset_.toFixed(2) + 'px)'; |
+ this.scrollable_.style.transform = transform; |
+ } |
+ |
+ scheduleFlingTick_() { |
+ this.flingAnimationId_ = requestAnimationFrame(this.tickFling_.bind(this)); |
+ } |
+ |
+ tickFling_() { |
+ this.flingAnimationId_ = null; |
+ if (!this.scrollBy(this.flingVelocity_)) { |
+ this.flingVelocity_ = 0; |
+ return; |
+ } |
+ var velocity = this.flingVelocity_ * kFlingFriction; |
+ if (velocity < kFlingVelocityMin) |
+ velocity = 0; |
+ this.flingVelocity_ = velocity; |
+ if (velocity) |
+ this.scheduleFlingTick_(); |
+ } |
+ |
+ handleScrollStart_(event) { |
+ } |
+ |
+ handleScrollEnd_(event) { |
+ } |
+ |
+ handleScrollUpdate_(event) { |
+ this.scrollBy(-event.dy); |
+ } |
+ |
+ handleFlingStart_(event) { |
+ this.flingVelocity_ = -event.velocityY; |
+ this.scheduleFlingTick_(); |
+ } |
+ |
+ handleFlingCancel_(event) { |
+ if (!this.flingAnimationId_) |
+ return; |
+ cancelAnimationFrame(this.flingAnimationId_); |
+ this.flingVelocity_ = 0; |
+ this.flingAnimationId_ = null; |
+ } |
+}.register(); |
+</script> |
+</sky-element> |