Two-finger, animated power scrolling in your WebOS app

Thanks to a recent beta of Carbon (an upcoming Twitter client for WebOS), several WebOS developers have been implementing a feature called power scrolling: in a scrollable area, you swipe with two fingers instead of one, and it jumps you immediately to the top or bottom.

This is an incredibly handy feature, and Jay Canuck and Doug Reeder both posted code snippets to help you integrate it into your own app. However, their code has some downsides: you have to implement it for every scene, it doesn’t animate, and extending prototypes is ugly.

Here, then, is a reasonably simple way for you to enable animated two-finger power scrolling in all scenes of your WebOS app.

To do so, we’ll use Prototype.js class inheritance to create a generic base class with two-finger scrolling support that can then be added to any of your scene assistant classes to toggle on power-scrolling.

The first step is to create a new file (location doesn’t matter, although mine is stored in /app/assistants) and add it to your sources.json file. In my instance, I called it power-scroll-base.js so the new line in sources.json looks like this:

{"source": "app/assistants/power-scroll-base.js"},

(Trailing comma is there because this is not the last item; if yours is last in your file, you won’t want that comma.)

After you have the file created and added to sources.json, add these contents to it:

/*
PowerScrollBase: a Prototype base class to quickly add power scrolling
to your existing scenes

Created by Ian Beck <https://beckism.com>
Released in the public domain

Many thanks to Jay Canuck and Doug Reeder for the basic idea and code:
- http://pastebin.com/6JqcQT4a
- https://gist.github.com/786358
*/
var PowerScrollBase = Class.create({
    // === $SUPER CALLS ===
    
    initialize: function() {
        this.twoFingerStartBound = this.twoFingerStart.bind(this);
        this.twoFingerEndBound = this.twoFingerEnd.bind(this);
    },
    
    activate: function() {
        // Add listeners for two-finger gesture events
        Mojo.Event.listen(this.controller.document, "gesturestart", this.twoFingerStartBound);
        Mojo.Event.listen(this.controller.document, "gestureend", this.twoFingerEndBound);
    },
    
    deactivate: function() {
        // Stop listening to two-finger gesture events
        Mojo.Event.stopListening(this.controller.document, "gesturestart", this.twoFingerStartBound);
        Mojo.Event.stopListening(this.controller.document, "gestureend", this.twoFingerEndBound);
    },
    
    // === EVENT METHODS ===
    
    twoFingerStart: function(event) {
        this.gestureStartY = event.centerY;
    },
    
    twoFingerEnd: function(event) {
        var gestureDistanceY = event.centerY - this.gestureStartY;
        var scroller = this.controller.getSceneScroller();
        var pos;
        if (gestureDistanceY > 0) {
            scroller.mojo.revealTop();
            pos = scroller.mojo.getScrollPosition();
            scroller.mojo.scrollTo(0, pos.top - 50, true);
        } else if (gestureDistanceY < 0) {
            scroller.mojo.revealBottom();
            pos = scroller.mojo.getScrollPosition();
            scroller.mojo.scrollTo(0, pos.top + 50, true);
        }
    }
});

This works by listening to the WebOS two-finger gesture events. When the gesture starts, it notes the Y position. Then when the two-finger gesture ends, if the Y position has changed it uses Mojo.Widget.Scroller.revealTop() or Mojo.Widget.Scroller.revealBottom() to immediately jump to the top or bottom of the scene scroller (Mojo.Widget.Scroller documentation here). However, this instantaneous jump is not very user-friendly because there’s no good visual indication what just happened, so the code then grabs the scroller position and bumps it 50 pixels beyond its bounds with Mojo.Widget.Scroller.scrollTo() (with animation enabled). This triggers the “you’ve reached the end of the scrollable area” bounce, which effectively communicates to the user that they have jumped to the top or bottom of the scene.

To use this code, you need to make a few changes to your existing scenes (assuming that they are already laid out as Prototype classes—if not you will need to first convert them). For instance, if you have an ExampleAssistant, you would modify it to look like this:

var ExampleAssistant = Class.create(PowerScrollBase, {
    initialize: function($super) {
        $super();
        // Do other initialization
    },
    
    activate: function($super, event) {
        $super(event);
        // Do other activation tasks
    },
    
    deactivate: function($super, event) {
        $super(event);
        // Do other deactivation tasks
    }
    // Presumably you will have other logic in this class, as well
});

This works by using Prototype.js’s class inheritance and special $super keyword. When the first argument in a class method in Prototype is $super, Prototype will internally modify the method so that external calls ignore the $super argument, but within the function you can call $super() to invoke the overridden copy of the method on the base class. By using PowerScrollBase for your base class and $super in your initialize, activate, and deactivate methods you can enable power scrolling very quickly in any scene within your app.

Here’s hoping that power scrolling catches on more broadly within the WebOS community; it’s one of those gestures that you will rarely discover on your own, but once you know it can make your life a whole heck of a lot easier.

One response to “Two-finger, animated power scrolling in your WebOS app”

Leave a response

  1. pcworld says:

    Thank you! :)

Leave a response