Skip to content

Instantly share code, notes, and snippets.

@chenglou
Last active October 24, 2015 20:05
Show Gist options
  • Save chenglou/caff72e06e0c3d72c9b4 to your computer and use it in GitHub Desktop.
Save chenglou/caff72e06e0c3d72c9b4 to your computer and use it in GitHub Desktop.
Thoughts on Layout

While I was experimenting with animation, I had to make a few basic prototypes for a layout system and see how they intertwine. Here's the API in conjunction with React:

var MyComp = React.createClass({
  registerLayoutValues: function() {
    return {
      self: {
        width: 30,
        left: (get('parent', 'width') - get('self', 'width')) / 2 // Center.
      }
    };
  },

  render: function() {
    return <div />;
  }
});

var App = React.createClass({
  registerLayoutValues: function() {
    return {
      self: {
        height: get('self', 'width') * 2,
        width: get('MyComp', 'width') + 10, // Padding/margin.
        anyValueCanBeHere: 20
      },
      MyComp: {
        height: 50
      }
    };
  },

  render: function() {
    return (
      <div>
        <MyComp ref="MyComp" />
      </div>
    );
  }
});

It's rather simple: this constructs a DAG whose nodes are the values in registerLayoutValues, and whose edges are toward the dependencies they need. For example, MyCompleft depends AppWidth and MyCompWidth. So you start from the sink nodes and resolve the graph, like promises. How it translates to actual style is unimportant.

Text layout can be handled natively. If a width is specified to depend on height but height doesn't exist, the system touches the DOM first and retrieves the value, hoping that it makes sense, and then proceed to resolve dependents as usual.

There might or might not have benefits in baking this props/state JS promise into React. If there is: the initial render isn't called until the values are resolved. This is a micro-view/alternative of the pre-render pass we were talking about. It's better because this seems more general and natural, and worse because I have no idea if this actually makes sense =).

Absolute layout should be the building block for other layout systems. Relative layout can be sugar for absolute layout (e.g. centerAt()). The idea is that, due to whatever current constraints, we need to be able to get to the lower, imperative level (absolute positioning) for things like animation. A list of three items, whose middle item animates out onto an arbitrary position, might require the last item to stay at its position for the duration of the transition. The mid-transition layout corresponds to none of today's layout systems: flexbox, autolayout, grid-based layout, and even physics (explanations). In this sense, absolute positioning is a nice stop-gap solution until someone comes up with a layout system that makes sense of animation's craziness. A similar stopgap is jQuery Magic Move, which records the current positions of items, places them absolutely, animates stuff and then re-places them according to the old layout arrangement. Aka, it interpolates between two renders. The problem is concurrent animation: if, during transitioning out, another item inserts itself, how do you get the new starting point for your interpolation? The starting point itself is an incoherent layout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment