1

In famo.us, there are some easy ways to perform animations/interactions using modifiers on a surface. For instance, dragging and animating surfaces have pretty straight forward examples in the famo.us guides.

...assume require('') statements above here...

var mainContext = Engine.createContext();
var draggable = new Draggable({...});
var surface = new Surface({...});
var transitionableTransform = new TransitionableTransform();
var modifier = new Modifier({
  origin: [.5, .5],
  transform: transitionableTransform
});
surface.pipe(draggable);
surface.on('click', function () {
  transitionableTransform.setScale([3, 3, 1], {duration: 300});
});
mainContext.add(draggable).add(surface);

However, in more complicated scenarios you might want to coordinate multiple animations, starting/stopping/reversing as needed depending on the interaction. In those cases, things as simple as adding transforms with a duration might work at first, but aren't guaranteed to not fall out of sync the more the user interacts with them.

The #render method appears to be a common place to put some types of coordinated animation. My limited understanding of it is it identifies the 'specs' of nodes that are being rendered, and is called on each frame of the render loop. So you might be able to identify each step of a particular animation, then depending on how it's interacted with be able to stop mid animation and change as needed. For instance, Flipper seems to work this way

(src/views/Flipper.js):

Flipper.prototype.render = function render() {
  var pos = this.state.get(); //state is a transitionable
  var axis = this.options.direction;
  var frontRotation = [0, 0, 0];
  var backRotation = [0, 0, 0];
  frontRotation[axis] = Math.PI * pos;
  backRotation[axis] = Math.PI * (pos + 1);

  if (this.options.cull && !this.state.isActive()) {
      if (pos) return this.backNode.render();
      else return this.frontNode.render();
  }
  else {
      return [
          {
              transform: Transform.rotate.apply(null, frontRotation),
              target: this.frontNode.render()
          },
          {
              transform: Transform.rotate.apply(null, backRotation),
              target: this.backNode.render()
          }
      ];
  }
};

Is there any documentation on the role #render should play when animating? Or how exactly the render method is supposed to work (for instance, the correct way to construct the specs that get returned). Is render supposed to be more low-level, and if so should a different construct be used?

jpcamara
  • 672
  • 6
  • 18

2 Answers2

1

The only way I've seen the render method used so far is to return specs from pre-existing elements. Personally, I've used it only when creating my own "Views", where I add a RenderNode to my class and create a pass-through render method that simply calls the RenderNode's render method. That's enough to pass custom objects into .add functions and have them work. I learned of that here: How to remove nodes from the Render Tree?

As for understanding the construction of RenderSpecs themselves, I'm not aware of any docs. The best way to get a sense of it would be to read through the _parseSpec function of SpecParser: https://github.com/Famous/core/blob/master/SpecParser.js#L92

From that it appears that any of the following can be used as a RenderSpec:

  1. An Entity id (assigned to every Surface upon creation)
  2. An object containing any of:
    • opacity
    • transform
    • origin
    • size
  3. An array of RenderSpecs
Community
  • 1
  • 1
aelr
  • 375
  • 1
  • 7
  • 1
    Thanks for the response. I had looked through the \_parseSpec and came to the same conclusion, just a duck typed object that describes how the rendering of a particular surface needs to go. So far i've mostly just extended view so haven't needed to _explicitly_ make a render method (thus not running into the same need you had since View has a render method which does what you said - ```return this._node.render();``` The meat of my question though, is whether it's an appropriate mechanism for handling coordinated animations so i'm still looking for more than this. – jpcamara May 09 '14 at 12:45
0

If you want to take control of rendered nodes, write a custom View with a render function. The Flipper class is a simple example (and the RenderController is a complex example of this pattern)

How Famo.us renders:

  1. Every RenderNode has a render function which creates a renderSpec.
  2. The renderSpec contains information about aModifier or Surface.
    2.1 The Modifier specs are used to calculatethe final CSS properties.
    2.2 The Surface spec are coupled to DOMelements.

  3. Every tick of the Engine, the renderSpec is rendered using the RenderNode.commit function.

  4. The commit function uses the ElementAllocator (from the Context) to allocate/deallocate DOM elements. (Which actually recycles DOM nodes to conserve memory). Therefore: Just return the correct renderSpec in your custom View, and famo.us will manage memory and performance for you.

Notes:

You don’t need to use the View class – an object with a render function will suffice. The View class simply adds events and options which is a nice way to create encapsulated, reusable components.

When a Surface element is allocated in the DOM, the deploy event is fired.

When a Surface element is deallocated, the recall event is fired.

As copied from http://famousco.de/2014/07/look-inside-rendering/

sabithpocker
  • 15,274
  • 1
  • 42
  • 75