在famo.us中构建Sticky Scrollview

时间:2014-04-21 22:14:25

标签: famo.us

我正在尝试在famo.us中构建一个Sticky scrollview,我在两个地方陷入困境。

1)身体在标题顶部滚动。当我将一个Transform.translate(0,0,1)添加到标题中,然后将其添加到_mainScrollview之前,它什么也没做。

2)当_mainScrollview卡住时,如何保持_bodyScrollview上的速度?

/***
 * A view that scrolls, sticks the header at stickAt,
 * and then allows the body to scroll.
 */
function Sticky(options) {
    View.apply(this, arguments);

    options = options || {};

    this.options = Object.create(Sticky.DEFAULT_OPTIONS);
    for (var i in Sticky.DEFAULT_OPTIONS) {
        if (options[i] !== undefined) this.options[i] = options[i];
    }

    // setup the scrollviews

    this._bodyScrollview = new Scrollview({});
    this._bodyScrollview.sequenceFrom([this.options.body]);

    this._mainScrollview = new Scrollview({});
    this._mainScrollview.sequenceFrom([this.options.header, this._bodyScrollview]);
    this._eventInput.pipe(this._mainScrollview);

    // track the velocities

    this._mainScrollview.sync.on('update', function (data) {
        this._mainVelocity = data.velocity;
    }.bind(this));

    this._bodyScrollview.sync.on('update', function (data) {
        this._bodyVelocity = data.velocity;
    }.bind(this));

    this.add(this._mainScrollview);
}

Sticky.prototype.render = function () {
    // If the main scrollview is scrolled up (velocity < 0)
    // past the stickAt point, stick it and unstick the body scrollview.
    if (this._mainVelocity < 0) {
        if (this._mainScrollview.getPosition() >= this.options.stickAt) {
            this._eventInput.unpipe(this._mainScrollview);
            this._eventInput.pipe(this._bodyScrollview);

            Tools.forcePosition(this._mainScrollview, this.options.stickAt, true);

            this._mainVelocity = 0;
        }
    }

    // If the main scrollview is scrolled down (velocity > 0)
    // past 0, stick it and unstick the main scrollview.
    if (this._bodyVelocity > 0) {
        console.log(this._bodyScrollview.getPosition());
        if (this._bodyScrollview.getPosition() <= 0) {
            this._eventInput.unpipe(this._bodyScrollview);
            this._eventInput.pipe(this._mainScrollview);

            Tools.forcePosition(this._bodyScrollview, 0, true);

            this._bodyVelocity = 0;
        }
    }

    return View.prototype.render.call(this);
};

/**
 * Force a scrollview to a position
 */
Tools.forcePosition = function (scrollview, position, noSpring) {
    if (noSpring) {
        scrollview._springState = 0;
        scrollview._physicsEngine.detachAll();
    }

    scrollview.setVelocity(0);
    scrollview.setPosition(position);    
};

3 个答案:

答案 0 :(得分:1)

您可以使用着名的Flex FlexScrollView执行此操作,请参阅此演示:

https://github.com/IjzerenHein/famous-flex-chat

教程:https://github.com/IjzerenHein/famous-flex/blob/master/tutorials/FlexScrollView.md

答案 1 :(得分:0)

1)我怀疑你的标题大小是错误的并且与主要滚动内容重叠。检查使用检查器并根据需要修改大小。 (参见Famo.us Common pitfalls: Why am I not getting this click?

2)订阅滚动条的edgeHit事件并手动设置速度(就像停止滚动条时一样)。

顺便说一句,而不是这个

ScrollView
    HeaderSurface
    ScrollView
        Content

我会这样布局:

 HeaderSurface
 ScrollView
     HeaderSurface
     Content1
     Content2
     Content3

因此,在RenderTree中引用了两个HeaderSurface。因此我将克隆HeaderSurface,如果滚动经过某个点,则显示第一个(粘性)HeaderSurface。这与第二个(scrollviewed)HeaderSurface重叠。

答案 2 :(得分:0)

1)这对Chrome来说是一个问题,它适用于Safari。解决方法是在正文后创建标题。

2)绕过速度传递被证明是困难和复杂的 - 当我坚持标题时,身体会反弹,因为它在同一个滚动视图内。

我重建了视图以使用一个带有表示标题的填充节点的scrollview。然后我根据scrollview的位置手动转换标题。

HeaderSurface
BodyScrollview
    HeaderFiller
    BodySurface

以下是代码:

/***
 * A view that scrolls sticks the header at stickHeaderAt
 * and allows the body to scroll underneath the header.
 */
function Sticky(options) {
    View.apply(this, arguments);

    options = options || {};

    this.options = Object.create(Sticky.DEFAULT_OPTIONS);
    for (var i in Sticky.DEFAULT_OPTIONS) {
        if (options[i] !== undefined) this.options[i] = options[i];
    }

    // Setup the body scrollview.

    // Add a filler node to represent the header.
    // In render we will manually transform the header depending on where the scrollview is.
    var headerFiller = new RenderNode(new Modifier({
        size: this.options.header.getSize()
    }));
    headerFiller.add(new Surface);

    this._bodyScrollview = new Scrollview({});
    this._bodyScrollview.sequenceFrom([headerFiller, this.options.body]);

    this.add(this._bodyScrollview);

    this._eventInput.pipe(this._bodyScrollview);

    // Create a wrapping container surface for the header after
    // the body as a workaround of a bug where chrome does not
    // respect our transform on the header.

    var headerContainer = new ContainerSurface({
        size: this.options.header.getSize(),
        transform: Transform.translate(0, 0, 1)
    });
    headerContainer.add(this.options.header);

    // Setup the header node.

    // Transform the header on top of the body (broken in chrome).
    this._headerModifier = new StateModifier({
        transform: Transform.translate(0, 0, 1)
    });
    var headerNode = new RenderNode(this._headerModifier);
    headerNode.add(headerContainer);

    this.add(headerNode);
}

Sticky.prototype = Object.create(View.prototype);
Sticky.constructor = Sticky;

Sticky.DEFAULT_OPTIONS = {
    header: null,
    body: null,
    stickHeaderAt: 0
};

Sticky.prototype.render = function () {
    // If the body scrollview is on the first page handle sticking / unsticking the header.
    // Otherwise the body is scrolled up enought that the header is already stuck, so ignore it.
    if (this._bodyScrollview._node.getNext()) {
        var scrollviewPosition = this._bodyScrollview.getPosition();

        // Stop the header when the scrollview hits stickHeaderAt.
        if (scrollviewPosition > this.options.stickHeaderAt) {
            this._headerModifier.setTransform(Transform.translate(0, -1 * this.options.stickHeaderAt, 1));
        }
        // When the scrollview is below the stickHeaderAt point
        // move the header with the scrollview.
        else {
            this._headerModifier.setTransform(Transform.translate(0, -1 * scrollviewPosition, 1));
        }
    }

    return View.prototype.render.call(this);
};

/**
 * Reset the header and body to the top.
 */
Sticky.prototype.reset = function () {
    this._headerModifier.setTransform(Transform.translate(0, 0, 1));

    this._bodyScrollview.goToPreviousPage();
    Tools.forcePosition(this._bodyScrollview, 0, true);
};