With the emergence of Flex layout, mobile terminal has greatly met the daily layout requirements, has become one of the front-end standard.

preface

After the table layout and div layout, Flex has gradually perfected and become the standard. At this time the front end typesetting ushered in a liberation, many difficult to write layout writing method was greatly simplified. Today, Flex is a foundation, both in terms of functionality and skills. It covers most scenarios in daily development.

This article will skip over flex syntax, functionality, and abbreviations and go straight to how Flex is implemented, the principles of the algorithm. For the application does not understand please consult first.

To prepare

Unlike other normal document flows (such as blocks), Flex’s child nodes are in a special order, ordered by CSS declaration order, and relegated to node order if none or equal. So the first step is to sort the document flow children of the container first, with the result denoted as orderChildren.

The fundamental reason we call Flex an elastic layout is that each child node is elastic and grows/shrinks, so that there is always a basis on which scaling can be determined. So how do we determine this baseline? www.w3.org/TR/css-flex…

According to the specification, we can know (simplified to some extent, such as not considering min/ Max limit, vertical typesetting, English word typesetting, etc.) :

  1. If the node is definedflex-basis, then use it.
  2. If the node is a special node with a fixed aspect ratio (such as IMG), andflex-basisIn the end iscontent, and the cross axis is known, then the spindle size calculated according to the width to height ratio is.
  3. autoUse spindle size if defined, degrade to if notcontent;
  4. contentIs the size of the content after adaptation.

The above adaptation is the most difficult because the content is fluid and the layout of the content itself changes. Therefore, adaptation requires a process of calculating the size of the content (in fact, even if it is not adaptive, it also needs to calculate Max /min below).

Before you can understand this process, there are two basic concepts to know:

  1. Maximum size (Max) : refers to the ideal size of the node under the current environment;
  2. Minimum size (min) : refers to the size of the node compressed to the minimum state under the current environment;

The text description is a bit abstract. Here’s an example:

On the left is point 1 Max, which assumes that the available space is infinite, so text (or inline nodes, etc.) can be all the way down a line, even if it goes beyond the Flex parent (black edge);

The second point min on the right assumes that we are completely out of space, but to ensure that the content is rendered with at least the width of a single character (or English word, etc.), regardless of whether it exceeds the Flex parent node.

Going back to the process of calculating adaptation, I break it down into two main parts:

  1. Flex direct child node size calculation;
  2. Its subsequent recursive child node size calculation.

Let’s look at section 1. Flex’s child node is an anonymous block container and can be considered block level (block/flex, ignoring inline-flex).

When the child node is a block, we need to combine flex-direction (row/column) with the type of recursive child node (DOM /text). Max2 /min2, max2/min2, max2/min2, max2/min2, max2/min2, max2/min2, max2/min2 The inline and text try to go together in ideal conditions, so Max /min can be reached (all Max vs all min vs maximum). Basis is ultimately the value of Max.

Similarly in the case of column, Max /min is cumulative when all block grandkids line up from top to bottom, while inline and text take the highest height in the row.

When the child node is Flex, the situation is similar to the block above, but more simplified, because the Flex child node enforces block level, so the grandchild node without inline is easier to calculate.

Let’s look at most 2. The difference is that the node can be inline, the rest is the same, and the overall calculation is the same.

At this point, we have the Max /min value for each Flex child node.

branch

Next, assume the principal size according to the situation of each child node. This step is relatively simple and can be expressed according to the relationship between basis/ Max /min, which can be expressed by the formula:

hypothetical = clamp(min_main_size, flex_base_size, max_main_size)
Copy the code

The pseudo-code form is:

hypothetical_main_size:
if flex_base_size > max_main_size:
    return max_main_size
if flex_base_size < min_main_size:
    return min_main_size
return flex_base_size
Copy the code

According to the flex-wrap declaration, we need to determine whether the container layout is nowRAP or wrap. Wrap-reverse is the same as multiple lines, but in reverse order. So how do you tell? Arrange the orderChildren in order with the assumed principal size obtained above, and if one row does not fit another row. Of course if the declaration is NowRAP you don’t need a line break (that is, only one line), but either way you need to count the sum of the assumed main dimensions of each line.

layout

Now that you have the information for each line, use the following algorithm given by the official documentation:

  1. Determine the elasticity factor used. Sum the external assumed primary dimensions of all items, if and is less than the primary dimensions of the Flex container, then the algorithm uses the growth factor, otherwise the contraction factor. Whether flex-grow or flex-shrink is used.
  2. Inelastic dimension term. Freeze and set its target primary size to its assumed primary size. If any of the following conditions are met, it is an inelastic dimension:
  • A. The elastic factor is 0
  • B. If a growth factor is used, flex-basis calculates a value greater than its assumed primary size
  • C. If a shrink factor is used, the flex-basis computed value is less than its assumed primary size
  1. Calculate the initial available space. Sum the external dimensions of all items on the row and subtract the primary dimensions of the Flex container. For frozen items, the external size refers to the target main size; Others that are not frozen calculate values for their external Flex-basis.
  2. Cycle:
  • A. Check each item. If all elastic terms are frozen, free space is considered allocated and the loop is broken.
  • B. Calculate the remaining free space and use it as the initial free space mentioned in the previous section. If the sum of the elastic factors of the unfrozen terms is less than 1, the initial available space is multiplied by this sum. If this value is less than the free space, it is set to the new free space.
  • C. Allocate available space according to the elasticity factor.

Skip if free space is 0. If the growth factor is used to calculate the proportion of the growth factor of this item to the sum of the growth factors of all unfrozen items. Set the target primary size of the item to the Flex-basis calculated value plus the scale times the remaining free space. If you use a shrink factor for each item that is not frozen, multiply its shrink factor by the flex-basis calculated value, denoting the scale shrink factor. Find the sum of all the scaling shrinkage factors, and then find the proportion of the sum of each scaling shrinkage factor. Set the item’s target primary size to the flex-basis calculation minus the scale multiplied by the product of the remaining free space. Note that this may result in a negative primary size, which will be corrected in the next step. Otherwise skip.

  • D. Fix min/Max violations. Clamp fixed the target main size of each non-frozen item according to the minimum/maximum size it used, limiting its Content-box to 0. If the target primary size is less than the minimum, it is a maximum violation. If it is greater than the maximum value, it is the minimum violation.
  • E. Freeze transitional elastic extension items. The total violation value is the sum of each adjustment in the previous step (Clamped size – unclamped size), that is, the violation difference value. If the value is:

0 Freezes all items. A positive number freezes all minimum violations. Negative values freeze all maximum violations. 5. Set the main size of each item to its target main size.

It is also worth noting that the specification does not mention in detail the MPB of orderChildren (margin/padding/border) or the MPB of recursive grandchild nodes. The immediate item is taken into account regardless of unit; However, only the min/ Max size is taken into account and must be a fixed value. Other values, such as percentages, are ignored.

reverse

In the case of multiple rows in the reverse direction, one more step is required to sort the contents of the current multiple rows in reverse order. Note that the unit is row. Remember forward each line height list is maxCrossList, starting with the relative starting point y=0. Calculates the increasing height list crossSumList, which is the sum of all previous row heights. The loop then starts at the end, setting a count variable and increasing the value of the corresponding index maxCrossList at the end of the loop. Call source the value of the corresponding index of the crossSumList, call diff count minus the value of the source, and offset it if it is not 0.

The sample

<div style={{display:'flex',width:100}} >
  <span style={{flex:'1 1 50',background:'#F00',padding:'0 5'}} >2</span>
  <span style={{flex:'1 1 40',background:'#00F'}} >3</span>
</div>
<div style={{display:'flex',width:100}} >
  <span style={{flex:'1 1 auto',background:'#F00'}} ><strong style={{display:'block',padding:'0 5'}} >2</strong></span>
  <span style={{flex:'1 1 auto',background:'#00F'}} ><strong style={{display:'block'}} >3</strong></span>
</div>
<div style={{display:'flex',width:100}} >
  <span style={{flex:'1 1 auto',background:'#F00'}} ><strong style={{display:'block',padding:'0 5'}} % >2</strong></span>
  <span style={{flex:'1 1 auto',background:'#00F'}} ><strong style={{display:'block'}} >3</strong></span>
</div>
Copy the code

Using these three simplified examples, the final result is as follows:

The three Flex nodes, called A, B, and C in sequence, are similar except for the MPB and child nodes. The children of node A have basis 50 and 40, but since the padding is declared first, they end up with 60 and 40, Max is the same, and min is around 19 and 9 (the size of character 2 and character 3, the first padding is counted). Assume that the main size is the same as basis. Because 60 plus 40 is exactly 100, they end up being the width of 60 and 40.

The children of node B are based on auto and have no width declaration, so they are downgraded to content adaptive. The recursive children of the first node have more padding, so the final Max, min, and basis are around 19 and 9. Assuming that the main size is consistent with basis, spare 100-19-9=72, evenly divided into 2 nodes each 36, eventually they are 55 and 45 widths.

The children of C node basis is auto, and there is no width declaration, so it is degraded to content adaptive. The recursive child of the first node has more padding but the percentage is invalid, so Max, min and basis are all around 9 in the end. Assuming that the primary size is the same as basis, and that the two nodes are rated 41 each, the primary size is 100-9-9=82, and they all end up 50 wide.

Finally attached source code: github.com/karasjs/kar…