Port sky-scrollable to Dart

This CL ports sky-scrollable to the new sky-element.

R=esprehn@chromium.org

Review URL: https://codereview.chromium.org/950603002
This commit is contained in:
Adam Barth 2015-02-21 09:56:22 -08:00
parent 085b988e6a
commit fe31fd0e4c
12 changed files with 179 additions and 164 deletions

View File

@ -29,7 +29,7 @@ typedef (CanvasRenderingContext2D or WebGLRenderingContext) RenderingContext;
[CustomElementCallbacks, RaisesException] Element createElement(DOMString tagName);
DocumentFragment createDocumentFragment();
[CustomElementCallbacks, RaisesException, TypeChecking=Interface] Node importNode(Node node, optional boolean deep = false);
[CustomElementCallbacks, RaisesException, TypeChecking=Interface] Node importNode(Node node, [Named] optional boolean deep = false);
Element getElementById(DOMString elementId);

View File

@ -394,11 +394,12 @@ bool HTMLDocumentParser::isWaitingForScripts() const
void HTMLDocumentParser::resumeAfterWaitingForImports()
{
RefPtr<HTMLDocumentParser> protect(this);
ASSERT(!isWaitingForScripts());
if (isWaitingForScripts())
return;
if (m_pendingChunks.isEmpty())
return;
ASSERT(m_haveBackgroundParser);
RefPtr<HTMLDocumentParser> protect(this);
pumpPendingChunks();
}

View File

@ -4,7 +4,7 @@
// found in the LICENSE file.
-->
<script>
module.exports = [
final List<Map> kData = [
{"name":"New York","state":"New York","population":8363710},
{"name":"Los Angeles","state":"California","population":3833995},
{"name":"Chicago","state":"Illinois","population":2853114},

View File

@ -1,43 +0,0 @@
#!mojo mojo:sky_viewer
<!--
// 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.
-->
<sky>
<import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" />
<import src="/sky/framework/sky-scrollable.sky" />
<import src="data/cities.sky" as="cities" />
<sky-element name="example-scrollable">
<template>
<style>
sky-scrollable {
margin: 20px;
height: 400px;
border: 2px solid blue;
}
b {
display: inline;
font-weight: bold;
}
</style>
<sky-scrollable>
<template repeat="{{ cities }}">
<template>
<div>{{ name }}</div>
</template>
</template>
</sky-scrollable>
</template>
<script>
module.exports = class extends SkyElement {
created() {
this.cities = cities.slice(0, 300);
}
}.register();
</script>
</sky-element>
<example-scrollable />
</sky>

View File

@ -0,0 +1,38 @@
<!--
// 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" />
<import src="/sky/framework/sky-scrollable.sky" />
<import src="../data/cities.sky" as="cities" />
<sky-element name="example-scrollable">
<template>
<style>
sky-scrollable {
margin: 20px;
height: 400px;
border: 2px solid blue;
}
</style>
<sky-scrollable />
</template>
<script>
import "dart:sky";
@Tagname('example-scrollable')
class ExampleScrollable extends SkyElement {
void shadowRootReady() {
Element parent = shadowRoot.querySelector('sky-scrollable');
for (Map city in cities.kData.getRange(0, 300)) {
Element div = document.createElement('div');
div.setChild(new Text(city['name']));
parent.appendChild(div);
}
}
}
_init(script) => register(script, ExampleScrollable);
</script>
</sky-element>

View File

@ -0,0 +1,10 @@
#!mojo mojo:sky_viewer
<!--
// 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.
-->
<sky>
<import src="example-scrollable.sky" />
<example-scrollable />
</sky>

View File

@ -0,0 +1,51 @@
// 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 "dart:math" as math;
const double _kDefaultAlpha = -5707.62;
const double _kDefaultBeta = 172.0;
const double _kDefaultGamma = 3.7;
double _positionAtTime(double t) {
return kDefaultAlpha * math.exp(-kDefaultGamma * t) - kDefaultBeta * t - kDefaultAlpha;
}
double _velocityAtTime(double t) {
return -kDefaultAlpha * kDefaultGamma * math.exp(-kDefaultGamma * t) - kDefaultBeta;
}
double _timeAtVelocity(double v) {
return -math.log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) / kDefaultGamma;
}
final double _kMaxVelocity = _velocityAtTime(0.0);
final double _kCurveDuration = _timeAtVelocity(0.0);
class FlingCurve {
double _timeOffset;
double _positionOffset;
double _startTime;
double _previousPosition;
double _direction;
FlingCurve(double velocity, double startTime) {
double startingVelocity = math.min(_kMaxVelocity, velocity.abs());
_timeOffset = _velocityAtTime(startingVelocity);
_positionOffset = _positionAtTime(_timeOffset);
_startTime = startTime / 1000.0;
_previousPosition = 0.0;
_direction = velocity.sign;
}
double update(double timeStamp) {
double t = timeStamp / 1000.0 - _startTime + _timeOffset;
if (t >= _kCurveDuration)
return 0.0;
double position = _positionAtTime(t) - _positionOffset;
double positionDelta = position - _previousPosition;
_previousPosition = position;
return _direction * math.max(0.0, positionDelta);
}
}

View File

@ -1,47 +0,0 @@
<!--
// 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.
-->
<script>
const kDefaultAlpha = -5707.62;
const kDefaultBeta = 172;
const kDefaultGamma = 3.7;
function positionAtTime(t) {
return kDefaultAlpha * Math.exp(-kDefaultGamma * t) - kDefaultBeta * t - kDefaultAlpha;
}
function velocityAtTime(t) {
return -kDefaultAlpha * kDefaultGamma * Math.exp(-kDefaultGamma * t) - kDefaultBeta;
}
function timeAtVelocity(v) {
return -Math.log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) / kDefaultGamma;
}
var kMaxVelocity = velocityAtTime(0);
var kCurveDuration = timeAtVelocity(0);
module.exports = class FlingCurve {
constructor(velocity, startTime) {
var startingVelocity = Math.min(kMaxVelocity, Math.abs(velocity));
this.timeOffset_ = timeAtVelocity(startingVelocity);
this.positionOffset_ = positionAtTime(this.timeOffset_);
this.startTime_ = startTime / 1000;
this.previousPosition_ = 0;
this.direction_ = Math.sign(velocity);
Object.preventExtensions(this);
}
update(timeStamp) {
var t = timeStamp / 1000 - this.startTime_ + this.timeOffset_;
if (t >= kCurveDuration)
return 0;
var position = positionAtTime(t) - this.positionOffset_;
var positionDelta = position - this.previousPosition_;
this.previousPosition_ = position;
return this.direction_ * Math.max(0, positionDelta);
}
};
</script>

View File

@ -47,8 +47,8 @@ abstract class SkyElement extends Element {
var registration = _registery[tagName];
if (registration.template != null) {
ShadowRoot shadow = ensureShadowRoot();
var tree = registration.template.content.cloneNode(deep:true);
shadow.appendChild(tree);
Node content = registration.template.content;
shadow.appendChild(document.importNode(content, deep: true));
shadowRootReady();
}
}

View File

@ -3,17 +3,9 @@
// 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-element.sky" as="SkyElement" />
<import src="fling-curve.sky" as="FlingCurve" />
<import src="sky-element.sky" />
<sky-element
name="sky-scrollable"
on-gesturescrollstart="handleScrollStart_"
on-gesturescrollend="handleScrollEnd_"
on-gesturescrollupdate="handleScrollUpdate_"
on-gestureflingstart="handleFlingStart_"
on-gestureflingcancel="handleFlingCancel_"
on-wheel="handleWheel_">
<sky-element>
<template>
<style>
:host {
@ -42,90 +34,103 @@
<div id="vbar" />
</template>
<script>
module.exports = class extends SkyElement {
created() {
this.scrollable_ = null;
this.vbar_ = null;
this.scrollOffset_ = 0;
this.flingCurve_ = null;
this.flingAnimationId_ = null;
import "dart:math" as math;
import "dart:sky";
import "fling-curve.dart";
@Tagname('sky-scrollable')
class SkyScrollable extends SkyElement {
Element _scrollable;
Element _vbar;
double _scrollOffset = 0.0;
FlingCurve _flingCurve;
int _flingAnimationId;
SkyScrollable() {
addEventListener('gesturescrollstart', _handleScrollStart);
addEventListener('gesturescrollend', _handleScrollEnd);
addEventListener('gesturescrollupdate', _handleScrollUpdate);
addEventListener('gestureflingstart', _handleFlingStart);
addEventListener('gestureflingcancel', _handleFlingCancel);
addEventListener('wheel', _handleWheel);
}
shadowRootReady() {
this.scrollable_ = this.shadowRoot.getElementById('scrollable');
this.vbar_ = this.shadowRoot.getElementById('vbar');
void shadowRootReady() {
_scrollable = shadowRoot.getElementById('scrollable');
_vbar = shadowRoot.getElementById('vbar');
}
get scrollOffset() {
return this.scrollOffset_;
}
double get scrollOffset => _scrollOffset;
set scrollOffset(value) {
set scrollOffset(double value) {
// TODO(abarth): Can we get these values without forcing a synchronous layout?
var outerHeight = this.clientHeight;
var innerHeight = this.scrollable_.clientHeight;
var scrollRange = innerHeight - outerHeight;
var newScrollOffset = Math.max(0, Math.min(scrollRange, value));
if (newScrollOffset == this.scrollOffset_)
double outerHeight = clientHeight.toDouble();
double innerHeight = _scrollable.clientHeight.toDouble();
double scrollRange = innerHeight - outerHeight;
double newScrollOffset = math.max(0.0, math.min(scrollRange, value));
if (newScrollOffset == _scrollOffset)
return;
this.scrollOffset_ = newScrollOffset;
var transform = 'translateY(' + -this.scrollOffset_.toFixed(2) + 'px)';
this.scrollable_.style.transform = transform;
_scrollOffset = newScrollOffset;
String transform = 'translateY(${(-_scrollOffset).toStringAsFixed(2)}px)';
_scrollable.style['transform'] = transform;
var topPercent = newScrollOffset / innerHeight * 100;
var heightPercent = outerHeight / innerHeight * 100;
this.vbar_.style.top = topPercent + '%';
this.vbar_.style.height = heightPercent + '%';
double topPercent = newScrollOffset / innerHeight * 100.0;
double heightPercent = outerHeight / innerHeight * 100.0;
_vbar.style['top'] = '${topPercent}%';
_vbar.style['height'] = '${heightPercent}%';
}
scrollBy(scrollDelta) {
var oldScrollOffset = this.scrollOffset_;
this.scrollOffset += scrollDelta;
return this.scrollOffset_ != oldScrollOffset;
bool scrollBy(double scrollDelta) {
double oldScrollOffset = _scrollOffset;
scrollOffset += scrollDelta;
return _scrollOffset != oldScrollOffset;
}
scheduleFlingUpdate_() {
this.flingAnimationId_ = requestAnimationFrame(this.updateFling_.bind(this));
void _scheduleFlingUpdate() {
_flingAnimationId = window.requestAnimationFrame(_updateFling);
}
stopFling_() {
cancelAnimationFrame(this.flingAnimationId_);
this.flingCurve_ = null;
this.flingAnimationId_ = null;
this.vbar_.style.opacity = 0;
void _stopFling() {
window.cancelAnimationFrame(_flingAnimationId);
_flingCurve = null;
_flingAnimationId = null;
_vbar.style['opacity'] = '0';
}
updateFling_(timeStamp) {
var scrollDelta = this.flingCurve_.update(timeStamp);
if (!scrollDelta || !this.scrollBy(scrollDelta))
return this.stopFling_();
this.scheduleFlingUpdate_();
void _updateFling(double timeStamp) {
double scrollDelta = _flingCurve.update(timeStamp);
if (scrollDelta == 0.0 || !scrollBy(scrollDelta))
_stopFling();
else
_scheduleFlingUpdate();
}
handleScrollStart_(event) {
this.vbar_.style.opacity = 1;
void _handleScrollStart(_) {
_vbar.style['opacity'] = '1';
}
handleScrollEnd_(event) {
this.vbar_.style.opacity = 0;
void _handleScrollEnd(_) {
_vbar.style['opacity'] = '0';
}
handleScrollUpdate_(event) {
this.scrollBy(-event.dy);
void _handleScrollUpdate(GestureEvent event) {
scrollBy(-event.dy);
}
handleFlingStart_(event) {
this.flingCurve_ = new FlingCurve(-event.velocityY, event.timeStamp);
this.scheduleFlingUpdate_();
void _handleFlingStart(GestureEvent event) {
_flingCurve = new FlingCurve(-event.velocityY, event.timeStamp);
_scheduleFlingUpdate();
}
handleFlingCancel_(event) {
this.stopFling_();
void _handleFlingCancel(_) {
_stopFling();
}
handleWheel_(event) {
this.scrollBy(-event.offsetY);
void _handleWheel(WheelEvent event) {
scrollBy(-event.offsetY);
}
}.register();
}
_init(script) => register(script, SkyScrollable);
</script>
</sky-element>

View File

@ -6,7 +6,7 @@ import "../resources/unit.dart";
import "dart:sky";
class FooElement extends Element {
FooElement() : super("foo");
final String tagName = "foo";
attachedCallback() {
++attachedCallbackCount;

View File

@ -3,7 +3,7 @@
import "dart:sky";
class FooElement extends Element {
FooElement() : super("foo");
final String tagName = "foo";
attachedCallback() {
print("PASS: FooElement attached.");