Вольный перевод статьи из блога 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
Заметьте, что, в режиме горизонтального письма справа-налево, вертикальный скроллбар (если он существует) будет находиться слева. Горизонтальный скроллбар (если он существует), все время находится снизу.
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 и обозначает точку в координатной плоскости, определенную переполняемым блоком.
Визуализация этих концепций:
Если блок в потоке справа-налево и имеет скроллбар ортогонального направления (например, вертикальный скроллбар в блоке direction:rtl), размер скроллбара должен быть добавлен к вычислению начала прокрутки. Ниже представлены два примера. Заметьте, что не имеет значения, находится вертикальных скроллбар по правую или левую сторону блока (вертикальный скроллбар обозначен с помощью |/|
):
блок
содержимого
|<-------->|
начало
прокрутки
|----------->|
_______________________
| |/| |
| |/| |
| |/| |
direction:rtl | |/| блок |
| |/| |
| |/| |
|__________|/|__________|
переполняемый блок
|<--------------------->|
блок
содержимого
|<-------->|
начало
прокрутки
|----------->|
_________________________
| | |/|
| | |/|
| | |/|
writing-mode: | | блок |/|
vertical-rl | | |/|
| | |/|
|____________|__________|/|
переполняемый блок
|<--------------------->|
Координатные пространства
Layout и Paint работают с четырьмя координатными пространствами и часто обращаются к ним (на самом деле, к двум):
- Физические координаты: Соответствует физическому направлению вывода на физический дисплей (экран, распечатанная страница). Обычно используется для рисования, потому логика компоновки, на основе которой происходит рисование, может отдавать значения в этом пространстве. CSS свойства
top
,right
,bottom
иleft
также находятся в этом пространстве. - Логические координаты: Используются для того, чтобы обеспечить обобщенное позиционирование, которое соответствует любым значениями CSS свойств
writing-mode
иdirection
. Свойстваbefore
,after
,start
и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
'начальная' / 'конечная' сторона
Другом пример с элементом, имеющим относительное (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>
Их финальное расположение внутри фрейма с разрешением 800x600:
container: (492, 8 300x200)
relpos: (662, 58 70x80)
Внешний отступ (margin) в 8px является дефолтным для HTML элемента body. согласно https://html.spec.whatwg.org/multipage/rendering.html#the-page.
Диаграмма ниже детально описывает размеры задействованных элементов:
На зарисовке ниже показан каждый шаг рекурсивного прохода к верхней границе документа:
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
Поскольку относительно позиционированные элементы позиционируются с помощью физических координат, и переворот на каждом шаге отражает позицию, основанную на ширине блкоа контейнера, мы можем высчитать только финальные физические пикселы в пространстве экрана для относительно позиционированных элементов, если пройдем по всему 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-start
, block-end
, inline-start
и inline-end
, или на start
и end
.
Заметьте, что большинство кодовой базы Blink предшествует спецификации логических свойств и, таким образом, еще не последовательно ссылается на логическое направление в заявленном порядке, хотя мы хотели бы двигаться в этом направлении. Также взгляните на спецификацию physical, flow-relative и line-relative асбтрактной блочной терминологии.
-
writing-mode
: горизонтальный или вертикаьлный, где вертикальный может находиться в потоке слева-направо или справа-налево. Для вертикального геометрия была смещена. Смотритеtransposed{Rect,Point,Size}()
. -
direction
/dir
: “внутристрочное базовое направление” блока. Либоltr
, либоrtl
. СмотритеisLeftToRightDirection()
. -
text-orientation
: ориентация текста в строке. Относится только к вертикальным режимам. - ортогональный поток: когда блок имеет перпендикулярный своему контейнеру режим записи. Может привести к сложным случаям. Смотрите спецификацию.
Top comments (0)