Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS Layout API #26

Open
anjia opened this issue Oct 15, 2018 · 1 comment
Open

CSS Layout API #26

anjia opened this issue Oct 15, 2018 · 1 comment
Labels
CSS cascading style sheets css-houdini

Comments

@anjia
Copy link
Owner

anjia commented Oct 15, 2018

简介

CSS Layout API 能让网页开发人员写自己的布局算法。它是 CSS Houdini #23 的一部分

这样,我们在 CSS 里写display属性值的时候,就不局限于浏览器已经支持的那几种布局了,诸如display: blockdisplay: flex等,我们可以写display: layout(myLayout)

Layout API 里的所有内容都在 Logical Coordinate System(逻辑坐标系统)里计算。这样的话,网站的书写模式就可以自动影响到你写的布局了。

对于熟悉文本从左到右书写的开发人员来说,Logical Coordinate System 到我们日常用到的名词的对应关系如下:

Logical 在CSS里写的
inlineSize width
inlineStart left
inlineEnd right
blockSize height
blockStart top
blockEnd bottom

知道这个名词对应关系有什么意义呢?等会在编写 Layout Worklet 的时候,你会看到代码里大量用到了第一列的属性值。

参考 https://github.com/w3c/css-houdini-drafts/blob/master/css-layout-api/EXPLAINER.md

@anjia
Copy link
Owner Author

anjia commented Oct 15, 2018

接下来,我们用一个例子来理解下 Layout API。

例子

它最终的运行效果是:

图1. 宽度自适应(若无图,请戳链接


图2.1 接受参数,eg.间距(若无图,请戳链接


图2.2 接受参数,eg.列数(若无图,请戳链接

完整代码见 src/css-layout-api/demo1.masonry

关键代码如下,建议配合着代码注释看,方便理解。

在 index.html 里

<style>
  ul {
    --padding: 5;
    --columns: 3;
    display: layout(masonry);
  }
</style>

<!-- 内含 10 个高度不等的 li -->
<ul>...</ul>

<script>
  if ('layoutWorklet' in CSS) {
    // 把 module script 添加到 Layout Worklet 里
    CSS.layoutWorklet.addModule('my_layout_masonry.js');
  }
</script>

在 my_layout_masonry.js 里

registerLayout('masonry', class {
  static get inputProperties() {
    return [ '--padding', '--columns' ]; 
  }
  
  *intrinsicSizes() { /* TODO */ }

  /**
   * 渲染引擎,在浏览器的layou 阶段时的回调
   * @param children 要执行layout元素的子元素列表
   * @param edges 在 logical coordinate system 里的 borders, scrollbar 和 padding 的大小
   * @param constraints 生成的片段应该满足的条件,该对象里提前计算了当前layout的一些属性。
   *                    eg. inline-size (width), block-size (height)
   * @param styleMap 当前layout的只读style
   */
  *layout(children, edges, constraints, styleMap) {
    // 1. 确定当前layout的内部大小, width
    const inlineSize = constraints.fixedInlineSize;

    const padding = parseInt(styleMap.get('--padding').toString());
    const columnValue = styleMap.get('--columns').toString();

    let columns = parseInt(columnValue);
    if (columnValue == 'auto' || !columns) {
      columns = Math.ceil(inlineSize / 350); // 默认每个宽350px
    }

    const childInlineSize = (inlineSize - ((columns + 1) * padding)) / columns;
    const childFragments = yield children.map((child) => {
      // 2. 对子节点进行布局,根据 columns
      return child.layoutNextFragment({fixedInlineSize: childInlineSize});
    });

    // 3. 算出'auto'块的大小。就能知道子元素的最大 height
    let autoBlockSize = 0;
    const columnOffsets = Array(columns).fill(0);
    for (let childFragment of childFragments) {
      const min = columnOffsets.reduce((acc, val, idx) => {
        if (!acc || val < acc.val) {
          return {idx, val};
        }

        return acc;
      }, {val: +Infinity, idx: -1});

      // 设置相对于父元素的 offset(除这两之外其它的属性都是只读的)
      childFragment.inlineOffset = padding + (childInlineSize + padding) * min.idx;
      childFragment.blockOffset = padding + min.val;

      columnOffsets[min.idx] = childFragment.blockOffset + childFragment.blockSize;
      autoBlockSize = Math.max(autoBlockSize, columnOffsets[min.idx] + padding);
    }

    // 4. 返回 fragment
    return {autoBlockSize, childFragments};
  }
});

参考 https://github.com/GoogleChromeLabs/houdini-samples

@anjia anjia added CSS cascading style sheets css-houdini labels Oct 15, 2018
@anjia anjia mentioned this issue Oct 15, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CSS cascading style sheets css-houdini
Projects
None yet
Development

No branches or pull requests

1 participant