0

It seems I'm a bit confused as to how the render tree works, or more specifically how modifiers affect their descendants.

For example, if I have a render tree that looks like:

                     -------------------
                     | Global Modifier |
                     -------------------
                             |
            ---------------------------------------
            |                |                    |
    ----------------  ----------------   --------------------
    | Blue Surface |  | Top Left Mod |   | Bottom Right Mod |
    ----------------  ----------------   --------------------
                             |                    |
                      -----------------    ---------------
                      | Green Surface |    | Red Surface |
                      -----------------    --------------- 
  • The global modifier sets the overall size.
  • The blue surface fills the space.
  • And the top-left and bottom-right modifiers have smaller sizes and use origin and align to align their child surfaces to in the corners.

So far, so good. It all works well. But if I put a transform on the global modifier, rotating it, for example, what I would expect would be for the whole unit to rotate, but instead it looks like each of my three surfaces are rotating independently around their own origins.

Why is it doing that?

How do I turn this into a unit that I can manipulate as a whole?

Thanks.

Here's some code:

http://jsfiddle.net/scucLw0h/1/

var Surface = famous.core.Surface;
var View = famous.core.View;
var Modifier = famous.core.Modifier;
var StateModifier = famous.modifiers.StateModifier;
var Engine = famous.core.Engine;
var Transform = famous.core.Transform;
var Transitionable = famous.core.Transitionable;


var globalModifier = new StateModifier({
    size: [100, 100],
    origin: [.5, .5],
    align: [.5, .5]
});

var topLeftModifier = new Modifier({
    size: [50, 50],
    align: [0, 0],
    origin: [0, 0]
});

var bottomRightModifier = new Modifier({
    size: [50, 50],
    align: [1, 1],
    origin: [1, 1]
});

var blueSurface = new Surface({
    properties: {
        backgroundColor: 'blue'
    }
});

var redSurface = new Surface({
    properties: {
        backgroundColor: 'red'
    }
});

var greenSurface = new Surface({
    properties: {
        backgroundColor: 'green'
    }
});


var mainContext = Engine.createContext();

var globalContext = mainContext.add(globalModifier);

globalContext.add(blueSurface);

globalContext.add(topLeftModifier).add(greenSurface);
globalContext.add(bottomRightModifier).add(redSurface);


globalModifier.setTransform(Transform.rotate(0, 45, 0), {duration: 1000});
nicholas
  • 14,184
  • 22
  • 82
  • 138

2 Answers2

1

In famo.us origin is responsible for two different things, which makes trouble for me as well. Famo.us university explains:

Changing origin does more than just change the anchor point on the child element. Origin is also the point about which transforms get applied.

However playing with your code it seems to me the align is what determines the point about which transforms get applied. At least if I set the inner align to [0, 0] then it works as you expected, see: fiddle: http://jsfiddle.net/nzmsvans/

I don't state I understand totally -- maybe there is a bug -- but maybe it helps.

Skalár Wag
  • 2,247
  • 2
  • 18
  • 21
1

To have it transition as a whole with respect to the origin point, there needs to be a positioning modifier and a separate transitioning modifier. In your example above, the position modifier is also transitioning the render nodes based on it's own origin. You want it to transition from the root origin of the position (center of context in this case).

                     -------------------
                     | Global Modifier | <=== Modifier for position
                     -------------------
                             |
                     -------------------
                     |  Root Modifier  | <=== Modifier that will transition
                     -------------------        the root node from/to position
                             |
            ---------------------------------------
            |                |                    |
    ----------------  ----------------   --------------------
    |   Blue Mod   |  |  Green Mod   |   |     Red Mod      | <=== Positions each renderable
    ----------------  ----------------   --------------------        with respect to the root
            |                |                    |
    ----------------  -----------------    ----------------
    | Blue Surface |  | Green Surface |    | Red Surface  |
    ----------------   -----------------    --------------- 

Here is some code to show how it can be done.

Click Here for a running example

var globalModifier = new Modifier({
  size: [100, 100],
  origin: [0, 0],
  align: [0.5, 0.5],
  transform: Transform.translate(0, 0, 0.0001)
});

var rootModifier = new Modifier({
  size: [undefined, undefined],
  origin: [0, 0],
  align: [0, 0],
  transform: Transform.translate(0, 0, 0.0001)
});

var blueModifier = new Modifier({
  size: [100, 100],
  origin: [0.5, 0.5],
  align: [0, 0],
  transform: Transform.translate(0, 0, 1)
});

var greenModifier = new Modifier({
  size: [50, 50],
  origin: [0, 0],
  align: [0, 0],
  transform: Transform.translate(0, 0, 2)
});

var redModifier = new Modifier({
  size: [50, 50],
  origin: [1, 1],
  align: [0, 0],
  transform: Transform.translate(0, 0, 2)
});

var blueSurface = new Surface({
  content: 'blue',
  size: [undefined, undefined],
  classes: ['double-sided'],
  properties: {
    color: 'white',
    textAlign: 'right',
    backgroundColor: 'blue'
  }
});

var redSurface = new Surface({
  content: 'red',
  size: [undefined, undefined],
  classes: ['double-sided'],
  properties: {
    backgroundColor: 'red'
  }
});

var greenSurface = new Surface({
  content: 'green',
  size: [undefined, undefined],
  classes: ['double-sided'],
  properties: {
    backgroundColor: 'green'
  }
});

var mainContext = Engine.createContext();
mainContext.setPerspective(10000);

var globalContext = mainContext.add(globalModifier);
var rootContext = globalContext.add(rootModifier);

rootContext.add(blueModifier).add(blueSurface);
rootContext.add(greenModifier).add(greenSurface);
rootContext.add(redModifier).add(redSurface);
talves
  • 13,993
  • 5
  • 40
  • 63
  • Great answer! Thanks for taking the time to explain. – nicholas Nov 27 '14 at 02:51
  • @talves, your code also works if you remove global modifier and set align = [0.5, 0.5] in the root modifier. In this case your code quite similar to mine. I don't think the axis of the rotation does matter now. – Skalár Wag Nov 27 '14 at 12:01
  • There are more than one way to accomplish transitions and layout in famo.us. I tried to set it up the easiest way for the example, but thanks @SkalárWag for the explanation. – talves Nov 27 '14 at 18:02
  • 1
    It should be pointed out that in my example, you could add other branches to the global modifier that the developer does not want to rotate. So, there is a separation of the position origin from our transition origin. **[I changed the location of the buttons by using the global Modifier in this revision](http://jsbin.com/fobaxo/4/edit?js,output)** – talves Nov 27 '14 at 18:27
  • What about layouts, like `SequentialLayout`... are they part of the render tree and how do `Modifier` objects or `StateModifier` objects work on `Surface` objects that had been added to a `SequentialLayout`? Why is origin of `Surface` objects in a `SequentialLayout` 0,0? – zealoushacker Nov 30 '14 at 23:56
  • I've added a separate question just to capture the idea: http://stackoverflow.com/questions/27221704/do-famo-us-layouts-like-sequentiallayout-participate-in-the-render-tree – zealoushacker Dec 01 '14 at 04:29