DEV Community

собачья будка
собачья будка

Posted on • Edited on

blink layout [перевод]

Вольный перевод статьи из блога chromium.googlesource "Blink Layout"

Blink Layout

Директория Source/core/layout содержит реализацию объектов разметки. Покрывает следующие состояния жизненного цикла документа:

  • LayoutSubtreeChange (InLayoutSubtreeChange и LayoutSubtreeChangeClean)
  • PreLayout (InPreLayout)
  • PerformLayout (InPerformLayout)
  • AfterPerformLayout (AfterPerformLayout и LayoutClean)

Заметьте, что ведется разработка новой Blink layout системы. Смотрите документ LayoutNG design document.

Код поддерживается командой layout team.

Блочная модель

Блочная модель CSS основана на серии вложенных блоков, от наружней к внутренней:

  • Margin box
  • Border box: основное координатное пространство LayoutBox
  • Padding box: a.k.a. client box
  • Content box

Следуя css-overflow-3, если скроллбары ничего не перекрывают, они должны быть внедрены между внутренней границей рамки (border) и внешней границей внутреннего отступа (padding).

График ниже был модифицирован на основе графика из спецификации блочной модели css, включая скроллбары:

    |-------------------------------------------------|
    |                                                 |
    |                  margin-top                     |
    |                                                 |
    |    |---------------------------------------|    |
    |    |                                       |    |
    |    |             border-top                |    |
    |    |                                       |    |
    |    |    |--------------------------|--|    |    |
    |    |    |                          |  |    |    |
    |    |    |       padding-top        |##|    |    |
    |    |    |                          |##|    |    |
    |    |    |    |----------------|    |##|    |    |
    |    |    |    |                |    |  |    |    |
    | ML | BL | PL |  content box   | PR |SW| BR | MR |
    |    |    |    |                |    |  |    |    |
    |    |    |    |----------------|    |  |    |    |
    |    |    |                          |  |    |    |
    |    |    |      padding-bottom      |  |    |    |
    |    |    |                          |  |    |    |
    |    |    |--------------------------|--|    |    |
    |    |    |     scrollbar height ####|SC|    |    |
    |    |    |-----------------------------|    |    |
    |    |                                       |    |
    |    |           border-bottom               |    |
    |    |                                       |    |
    |    |---------------------------------------|    |
    |                                                 |
    |                margin-bottom                    |
    |                                                 |
    |-------------------------------------------------|

    BL = border-left
    BR = border-right
    ML = margin-left
    MR = margin-right
    PL = padding-left
    PR = padding-right
    SC = scroll corner
    SW = scrollbar width
Enter fullscreen mode Exit fullscreen mode

Заметьте, что, в режиме горизонтального письма справа-налево, вертикальный скроллбар (если он существует) будет находиться слева. Горизонтальный скроллбар (если он существует), все время находится снизу.

Scroll origin vs. offset vs. position

Если LayoutBox имеет прокручиваемое переполнение (scrollable overflow), он ассоциируется с PaintLayerScrollableArea. Для представления положения верхней левой точки блока содержимого (видимой его части) в координатной системе, определенной верхней левой точкой переполняемого блока, в котором он может прокручиваться полностью к началу его содержимого, PaintLayerScrollableArea использует “начало прокрутки”.

Для содержимого, которое находится в потоке слева-направо и сверху-вниз, начало прокрутки будет в точке (0, 0), т.е. верхняя левая точка блока содержимого совпадает с верхней левой точкой содержимого переполняемого блока, в котором он может прокручиваться полностью к началу его содержимого.

Для содержимого, которое находится в потоке справа-налево (включая direction:ltr, writing-mode:vertical-rl и flex-direction:row-reverse), x-координата начала прокрутки будет положительной, а для содержимого, которое находится в потоке снизу-вверх (т.е., flex-direction:column-reverse и режим вертикального письма с direction:ltr), положительной будет y-координата.

Во всех случаях, термин ‘scrollOffset’ (или просто ‘offset’) использует тип ScrollOffset и обозначает расстояние прокрутки вьюпорта от его местоположения до начала его содержимого. Термин ‘scrollPosition’ (или просто ‘position’) использует тип FloatPoint и обозначает точку в координатной плоскости, определенную переполняемым блоком.

Визуализация этих концепций:

Alt Text
Alt Text
Alt Text

Если блок в потоке справа-налево и имеет скроллбар ортогонального направления (например, вертикальный скроллбар в блоке direction:rtl), размер скроллбара должен быть добавлен к вычислению начала прокрутки. Ниже представлены два примера. Заметьте, что не имеет значения, находится вертикальных скроллбар по правую или левую сторону блока (вертикальный скроллбар обозначен с помощью |/|):

                                 блок
                              содержимого
                             |<-------->|
                    начало
                  прокрутки
                |----------->|
                 _______________________
                |          |/|          |
                |          |/|          |
                |          |/|          |
direction:rtl   |          |/|   блок   |
                |          |/|          |
                |          |/|          |
                |__________|/|__________|

                    переполняемый блок
                |<--------------------->|

                                 блок
                              содержимого
                             |<-------->|
                    начало
                  прокрутки
                |----------->|
                 _________________________
                |            |          |/|
                |            |          |/|
                |            |          |/|
writing-mode:   |            |    блок  |/|
  vertical-rl   |            |          |/|
                |            |          |/|
                |____________|__________|/|

                    переполняемый блок
                |<--------------------->|
Enter fullscreen mode Exit fullscreen mode

Координатные пространства

Layout и Paint работают с четырьмя координатными пространствами и часто обращаются к ним (на самом деле, к двум):

  • Физические координаты: Соответствует физическому направлению вывода на физический дисплей (экран, распечатанная страница). Обычно используется для рисования, потому логика компоновки, на основе которой происходит рисование, может отдавать значения в этом пространстве. CSS свойства toprightbottom и left также находятся в этом пространстве.
  • Логические координаты: Используются для того, чтобы обеспечить обобщенное позиционирование, которое соответствует любым значениями CSS свойств writing-mode и direction. Свойства beforeafterstart и end находится в этом пространстве. Они также известны, как ‘logical top’, ‘logical bottom’, ‘logical left’ и ‘logical right’ соответственно.
  • Физические координаты с перевернутым направлением блочного потока (flipped block-flow direction): То же, что и физические координаты, но применяется для writing-mode: vertical-rl, где блок расположен справа-налево, а его позиция перевернута слева-направо относительно оберточного блока. This is essentially a mirror reflection horizontally across the center of a block's containing block.

    Для writing-mode значений, отличных от vertical-rl, изменения физических координат нет.

    Логика компоновки и рисования ссылается на это пространство, чтобы обозначить, было ли к значениям применено "переворачивание". Финальный вывод для режима письма с перевернутым направлением блочного потока должен, по определению, включать в себя и переворот. Поиск режима письма для объекта может быть дорогой операцией. Выполнение вычислений для значений, о которых известно, что они находятся в этом пространстве, может сэкономить на накладных расходах, необходимых для отскока/перескока.

  • Логические координаты без перескоков линейного направления: являются “логическими координатами блока”, не учитывающими направление текста. Например, “LogicalLeft” и “LogicalRight”.

Пример с writing-mode: vertical-rl; direction: ltr:

             'верхняя' / 'начальная' сторона

               направление блочного потока
          <------------------------------------ |
          ------------------------------------- |
          |        c   |          s           | |
'левая'   |        o   |          o           | |   линейное     'правая'
   /      |        n   |          m           | |  направление      /
'after'   |        t   |          e           | |                'before'
сторона   |        e   |                      | |                сторона
          |        n   |                      | |
          |        t   |                      | |
          ------------------------------------- v

                'начальная' / 'конечная' сторона
Enter fullscreen mode Exit fullscreen mode

Другом пример с элементом, имеющим относительное (relative) позиционирование:

<style>
html {
    writing-mode: vertical-rl;
}
</style>
<div id="container" style="background-color: lightBlue; width: 300px; height: 200px;">
  <div id="relpos" style="position: relative; top: 50px; left: -60px; width: 70px; height: 80px; background-color: red;"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

Их финальное расположение внутри фрейма с разрешением 800x600:

container: (492, 8  300x200)
relpos:    (662, 58 70x80)
Enter fullscreen mode Exit fullscreen mode

Внешний отступ (margin) в 8px является дефолтным для HTML элемента body. согласно https://html.spec.whatwg.org/multipage/rendering.html#the-page.

Диаграмма ниже детально описывает размеры задействованных элементов:

Alt Text

На зарисовке ниже показан каждый шаг рекурсивного прохода к верхней границе документа:

LayoutBlockFlow (позиционирован относительно) DIV id='relpos'   0,0    70x80

    Применить относительное позиционирование к 'relpos', пока происходит переворот
    внутри контейнера.

    170 = 300 (ширина контейнера) - 70 (ширина relpos) - 60 (relpos left)
    50 = relpos top

LayoutBlockFlow DIV id='container'                      170,50 70x80

    Поскольку body одной ширины с контейнером, переворот
    не влияет на блок на этом шаге.

LayoutBlockFlow BODY                                    170,50 70x80

    Перевернуть внутри html блока, который симметрично больше body на 8px
    из-за дефолтного внешнего отступа.

LayoutBlockFlow HTML                                    178,58 70x80

    Перевернуть блок в области просмотра.

    662 = 800 (ширина области просмотра) - 316 (ширина html) 
      + 178 (текущий left блока)

LayoutView #document                                    662,58 70x80
Enter fullscreen mode Exit fullscreen mode

Поскольку относительно позиционированные элементы позиционируются с помощью физических координат, и переворот на каждом шаге отражает позицию, основанную на ширине блкоа контейнера, мы можем высчитать только финальные физические пикселы в пространстве экрана для относительно позиционированных элементов, если пройдем по всему layout дереву от начального объекта до самой верхней области видимости, как описано выше.

Дополнительные примеры режима письма и комбинаций направлений можно посмотреть здесь.

Перевернутые координаты блочного потока

Суть “переворачивания” значения в зеркальном его отражении внутри блока контейнера таким образом, что переворачивание его дважды приведет к изначальному положению. Таким образом, при работе с задействованной логиком может быть легко случайно перевернуть ненужное, поскольку переворот слишком много раз можно «исправить» еще одним переворотом. Очевидно, это может привести к запутанному и менее производительному коду, поэтому следует позаботиться о том, чтобы понять и задокументировать любые изменения в логике переворачивания.

Покрытие тестами Blink для возможностей режимов вертикальной записи и vertical-rl в частности может быть не настолько широким, как для режима горизонтальной записи. Имейте это ввиду, окгда пишете новую функциональность или тесты и обязательно включайте покрытие для всех режимов записи, когда это необходимо.

Значения обычно преобразуются в координаты перевернутого блочного потока с помощью набора методов на задействованных объектах разметки. Смотрите на FlipForWritingMode()FlipForWritingModeForChild().

InlineBox::FlipForWritingMode() переворачивает входное значение встроенного блока внутри контейнера.

LayoutBox::FlipForWritingMode() переворачивает входное значение указанного блока.

LayoutBox::FlipForWritingModeForChild() переворачивает входное значение указанного блока, компенсируя ширину и текущую x-позицию указанного дочернего блока. Это может быть полезным для частого шаблона, в котором мы строим точечное местоположение, начиная с текущего местоположения дочернего блока.

Классы и подклассы для LayoutBox и InlineBox:

  • PhysicalLocation() возвращает физическое местоположение встроенного блока или контейнера. (0,0) это координаты верхней левой точки контейнера. Переворачивание производится на значениях по мере необходимости. В LayoutBox для поиска контейнера, если он не был передан в PhysicalLocation(), потребуется пройти по всему дереву разметки, что может быть дорогой операцией. InlineBox::PhysicalLocation() же будет дорогой операцией только если InlineBox находится в режиме перевернутого блочного потока.
  • Location() возвращает местоположение контейнера или встроенного блока в координатном пространстве "физических координат перевернутого блочного потока". (0,0) это координаты верхней левой точки контейнера для обычных направлений writing-mode (horizontal-tb и vertical-lr), и координаты верхней правой точки контейнера для writing-mode в перевернутом блочном потоке (vertical-rl).

Заметьте, что есть два основных похожих, но немного отличных метода для поиска содержащего блока для элемента:

  • LayoutObject::Container() возвращает контейнер для элемента, как опредено в CSS.
  • LayoutObject::ContainingBlock() возвращает неанонимный вложенный тег для элемента. Если контейнер сам является встроенным - вернется его вложенный неанонимный блок. Метод испольузется PhysicalLocation().

В LayoutObject есть и другие методы для специальных целей, вроде фиксированного и абсолютного позиционирования и отмены рисования. Иногда код просто ссылается на элемент контейнера, который, к сожалению, можно понимать двояко, потому важно обратить пристальное внимание, какой метод использовался для получения контейнера.

Более сложные возможности веб платформы, вроде таблиц, flexboxа или мультиколоночности обычно реализуются поверх этих примитивов вместе с проверками IsFlippedBlocksWritingMode()IsLeftToRightDirection(), и IsHorizontalWritingMode(). Смотрите LayoutTableSection::LogicalRectForWritingModeAndDirection()LayoutFlexibleBox::UpdateAutoMarginsInCrossAxis() или LayoutMultiColumnFlowThread::FlowThreadTranslationAtPoint()`.

Geometry mapping

TODO(wkorman): Разработать:

  • mapToVisualRectInAncestorSpace()
  • mapAncestorToLocal()
  • Widget и FrameView деревья. Заметьте, что с первым скоро будет покончено http://crbug.com/637460.
  • GeometryMapper (или просто указать на его секцию в README paint`а). Пока что, смотрите документ Web page geometries.

Скроллинг

На данный момент уже имеется хороший обзор на скроллинг в Blink.

Скроллингом корневого уровня называется постоянный рефакторинг архитектуры скроллинга в Blink, который делает корневой PaintLayer ответственным за скроллинг, который раньше выполнялся с помощью FrameView. Больше деталей здесь Root Layer Scrolling.

Словарь

Здесь представлен краткий обзор на ключевые термины относительно блочного потока, внутристрочного потока и ориентации текста. Больше деталей здесь CSS Writing Modes Level 3.

Спецификация CSS Logical Properties Level 1 представляет последние идеи CSSWG относительно именования пространства логических координат. Когда ось подразумевается или не имеет значения, CSSWG опирается на block-startblock-endinline-start и inline-end, или на start и end .

Заметьте, что большинство кодовой базы Blink предшествует спецификации логических свойств и, таким образом, еще не последовательно ссылается на логическое направление в заявленном порядке, хотя мы хотели бы двигаться в этом направлении. Также взгляните на спецификацию physicalflow-relative и line-relative асбтрактной блочной терминологии.

  • writing-mode: горизонтальный или вертикаьлный, где вертикальный может находиться в потоке слева-направо или справа-налево. Для вертикального геометрия была смещена. Смотрите transposed{Rect,Point,Size}().
  • direction/dir: “внутристрочное базовое направление” блока. Либо ltr, либо rtl. Смотрите isLeftToRightDirection().
  • text-orientation: ориентация текста в строке. Относится только к вертикальным режимам.
  • ортогональный поток: когда блок имеет перпендикулярный своему контейнеру режим записи. Может привести к сложным случаям. Смотрите спецификацию.

Top comments (0)