mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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:
parent
085b988e6a
commit
fe31fd0e4c
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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>
|
||||
38
examples/example-scrollable/example-scrollable.sky
Normal file
38
examples/example-scrollable/example-scrollable.sky
Normal 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>
|
||||
10
examples/example-scrollable/index.sky
Normal file
10
examples/example-scrollable/index.sky
Normal 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>
|
||||
51
framework/fling-curve.dart
Normal file
51
framework/fling-curve.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -6,7 +6,7 @@ import "../resources/unit.dart";
|
||||
import "dart:sky";
|
||||
|
||||
class FooElement extends Element {
|
||||
FooElement() : super("foo");
|
||||
final String tagName = "foo";
|
||||
|
||||
attachedCallback() {
|
||||
++attachedCallbackCount;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import "dart:sky";
|
||||
|
||||
class FooElement extends Element {
|
||||
FooElement() : super("foo");
|
||||
final String tagName = "foo";
|
||||
|
||||
attachedCallback() {
|
||||
print("PASS: FooElement attached.");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user