0

EDIT: Solved - see new code excerpts

I have a Famo.us scroll view nested inside a ContainerSurface. The ContainerSurface rotates on mutli-touch rotate gestures, but once rotated the scrollview's scroll direction doesn't stay relative to the modifier that rotates the surface and its children.

Eric T
  • 944
  • 1
  • 11
  • 26

2 Answers2

1

Given the current implementation this will not be possible, the problem is the TouchSync.

To accomplish something like this you will need to write a custom RotatedTouchSync that is "aware" of the rotation. This way when the ScrollView recieves the delta from the move event, the custom TouchSync has already translated it into a coordinate system the ScrollView can understand.

Joseph Carroll
  • 465
  • 6
  • 15
  • Also it seems like there's no way to defined "direction" on scroll view aside from 0 or 1 (vertical or horizontal). – Eric T Jan 30 '15 at 15:14
  • 1
    Unless that is something you have developed, the TouchSync is not aware. Even if it was you would need to translate the coordinates into a normalized coordinate system (rotate the x/y axis). It is correct that the only way to define direction is either x or y (0/1). – Joseph Carroll Jan 31 '15 at 19:27
  • Thanks @Joseph - I've created a rough draft of a custom RoatableTouchSync and custom scroll view. Like you said, all I needed to do was convert the delta in the TouchSync. – Eric T Feb 05 '15 at 01:15
0

I've set up a codepen with the basic example where you can see how ScrollView won't work unless you create a custom TouchSync that feeds the ScrollView a converted delta (as @Joseph Carroll stated below).

I accomplished this fix by creating a RotatableTouchSync with a modified _handleMove method:

function _handleMove(data) {
        var history = data.history;

        var currHistory = history[history.length - 1];
        var prevHistory = history[history.length - 2];

        var distantHistory = history[history.length - this.options.velocitySampleLength] ?
          history[history.length - this.options.velocitySampleLength] :
          history[history.length - 2];

        var distantTime = distantHistory.timestamp;
        var currTime = currHistory.timestamp;

        // Begin custom code -----------------------------------------------------------------------------
        var diffX, diffY, velDiffX, velDiffY;

        if (this.options.rotateAngle) {

            // Convert currHistory, prevHistory, distantHistory x,y points to quadrants because our formula only works when origin is 0,0
            var absCenter = [window.innerWidth/2, window.innerHeight/2];

            var currPrime = _calcCoordsByAngle([currHistory.x - absCenter[0], currHistory.y - absCenter[1]], this.options.rotateAngle);

            var prevPrime = _calcCoordsByAngle([prevHistory.x - absCenter[0], prevHistory.y - absCenter[1]], this.options.rotateAngle);

            var distPrime = _calcCoordsByAngle([distantHistory.x - absCenter[0], distantHistory.y - absCenter[1]], this.options.rotateAngle);

            // Converted coordinates back to normal points (clientX, clientY points)
            var convertedCurrX = currPrime.x + absCenter[0];
            var convertedCurrY = currPrime.y + absCenter[1];

            var convertedPrevX = prevPrime.x + absCenter[0];
            var convertedPrevY = prevPrime.y + absCenter[1];

            var convertedDistX = distPrime.x + absCenter[0];
            var convertedDistY = distPrime.y + absCenter[1];


            // Calculate diff like normal
            diffX = convertedCurrX - convertedPrevX;
            diffY = convertedCurrY - convertedPrevY;


            velDiffX = convertedCurrX - convertedDistX;
            velDiffY = convertedCurrY - convertedDistY;

        }
       // If the custom option for rotate angle isn't set, calculate as normal
        else {
            diffX = currHistory.x - prevHistory.x;
            diffY = currHistory.y - prevHistory.y;

            velDiffX = currHistory.x - distantHistory.x;
            velDiffY = currHistory.y - distantHistory.y;
        }

The modified _handleMove calls a custom method called _calcCoordsByAngle:

/**
 *  Used to calculate a coordinate set
 *  at a given rotation
 *  See: http://math.stackexchange.com/questions/384186/calculate-new-positon-of-rectangle-corners-based-on-angle
 *  @method _calcCoordsByAngle
 *  @param {Array} oldCoords An [x,y] set of coordinates
 *  @param {number} phi The angle to move by
 *  @return {Array} The newly converted coordinates
 */
function _calcCoordsByAngle(oldCoords, phi) {
    var newCoords = {};
    newCoords.x = (oldCoords[0] * Math.cos(-phi)) - (oldCoords[1] * Math.sin(-phi));
    newCoords.y = (oldCoords[1] * Math.cos(-phi)) + (oldCoords[0] * Math.sin(-phi));

    return newCoords;
}

From there, it's possible to use this custom RotatableTouchSync as a replacement for TouchSync on a custom ScrollView - just make sure to pass in/set the option for 'rotateAngle' on the RotatableTouchSync any time a Z axis rotation changes.

Eric T
  • 944
  • 1
  • 11
  • 26