Mesh-градиенты
Mesh-градиент - самый структурный тип градиента в gradiente. Linear, radial, diamond и conic градиенты описывают непрерывный цветовой переход через список stops. Mesh-градиент описывает уже не линию или радиус, а цветную поверхность, собранную из vertices и patches.
У каждой vertex есть id, x/y позиция и цвет. Каждый patch соединяет четыре vertices в одну ячейку поверхности. Renderer семплит цвета внутри этих ячеек и заполняет итоговый прямоугольник.
mesh-gradient(grid 4 4 method bicubic in oklab, vertex v00 0% 0% hsl(89, 96%, 40%), vertex v10 30.04% 0% #67e8f9, vertex v20 71.53% 0.08% hsl(285, 73%, 66%), vertex v30 100% 1.84% #f472b6, vertex v01 0.62% 38.7% #0f172a, vertex v11 28.18% 35.3% hsl(120, 69%, 63%), vertex v21 66.51% 23.4% #9333ea, vertex v31 100% 37.76% #06b6d4, vertex v02 0% 72.36% #9333ea, vertex v12 30.67% 66.72% hsl(237, 62%, 41%), vertex v22 67.1% 73.74% hsl(111, 79%, 43%), vertex v32 100% 75.98% hsl(240, 95%, 47%), vertex v03 0% 100% #ec4899, vertex v13 26.77% 98.53% #06b6d4, vertex v23 62.17% 99.27% #7c3aed, vertex v33 99.93% 100% #0f172a, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p20 v20 v30 v31 v21, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12, patch p21 v21 v31 v32 v22, patch p02 v02 v12 v13 v03, patch p12 v12 v22 v23 v13, patch p22 v22 v32 v33 v23)mesh-gradient(grid 4 4 method bicubic in oklab, vertex v00 0% 0% hsl(89, 96%, 40%), vertex v10 30.04% 0% #67e8f9, vertex v20 71.53% 0.08% hsl(285, 73%, 66%), vertex v30 100% 1.84% #f472b6, vertex v01 0.62% 38.7% #0f172a, vertex v11 28.18% 35.3% hsl(120, 69%, 63%), vertex v21 66.51% 23.4% #9333ea, vertex v31 100% 37.76% #06b6d4, vertex v02 0% 72.36% #9333ea, vertex v12 30.67% 66.72% hsl(237, 62%, 41%), vertex v22 67.1% 73.74% hsl(111, 79%, 43%), vertex v32 100% 75.98% hsl(240, 95%, 47%), vertex v03 0% 100% #ec4899, vertex v13 26.77% 98.53% #06b6d4, vertex v23 62.17% 99.27% #7c3aed, vertex v33 99.93% 100% #0f172a, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p20 v20 v30 v31 v21, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12, patch p21 v21 v31 v32 v22, patch p02 v02 v12 v13 v03, patch p12 v12 v22 v23 v13, patch p22 v22 v32 v33 v23)mesh-gradient(...) не является нативной CSS gradient-функцией. gradiente превращает одну и ту же внутреннюю mesh-модель в CSS, Canvas 2D, Canvas WebGL и SVG output. CSS target - это сгенерированный SVG data URL. SVG target - это pattern payload. Canvas targets рисуют sampled mesh напрямую.
Mesh previews на этой странице ленивые. Preview не вызывает parse() и transformTo(), пока не приблизится к viewport, потому что mesh rendering значительно тяжелее сериализации обычного stop-based градиента.
Использование mesh-градиента во фреймворке
Mesh-градиенты не являются нативными CSS-функциями, поэтому transformTo('css') возвращает готовый для background SVG data URL. Примеры используют тот же сложный mesh, который показан выше: парсим один раз, трансформируем в CSS и подключаем как backgroundImage.
Из чего состоит mesh-градиент
У модели mesh-градиента есть четыре обязательные части и одна опциональная деталь patch:
Resolved config выглядит так:
type GradientMeshConfig = {
rows: number
columns: number
method: 'bilinear' | 'bicubic'
interpolation: {
colorSpace: GradientColorSpace
hue?: GradientHueInterpolation
}
}Data model - это не список stops. У mesh есть явные vertices и явные patches:
type GradientMeshVertex = {
id: string
x: GradientLengthPercentage
y: GradientLengthPercentage
color: string
}
type GradientMeshPatch = {
id: string
topLeft: string
topRight: string
bottomRight: string
bottomLeft: string
handles?: GradientMeshPatchHandles
}Что делает gradiente
Для mesh-gradient gradiente берет на себя работу, которую нельзя делегировать нативному CSS:
- Парсит mesh-строки в instance
GradientMesh. - По возможности выводит grid size из регулярных vertex ids или количества элементов.
- Валидирует vertex ids, patch ids, colors, coordinates и topology.
- Хранит vertices и patches как внутренний source of truth.
- Поддерживает
bilinearиbicubiccolor sampling. - Поддерживает color interpolation через Culori-compatible spaces.
- Сохраняет опциональные patch handles в string и JSON serialization.
- Семплит цвета через
samplePatchColor(patchId, u, v). - Трансформирует одну и ту же модель в CSS, Canvas 2D, Canvas WebGL и SVG.
- Закрывает внешнюю область render area через edge triangles, когда vertices не лежат точно на краях прямоугольника.
Анатомия
Полный синтаксис - это список mesh records, разделенных запятыми:
mesh-gradient(
grid <rows> <columns> [method bilinear|bicubic] [in color-space [hue-mode hue]],
vertex <id> <x> <y> <color>,
vertex <id> <x> <y> <color>,
...,
patch <id> <top-left> <top-right> <bottom-right> <bottom-left>,
...,
handle <patch-id> <side> <from-x> <from-y> <to-x> <to-y>
)Минимальный явный mesh имеет 2 x 2 vertex grid и один patch:
mesh-gradient(grid 2 2 method bilinear, vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% yellow, vertex v11 100% 100% green, patch p00 v00 v10 v11 v01)mesh-gradient(grid 2 2 method bilinear, vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% yellow, vertex v11 100% 100% green, patch p00 v00 v10 v11 v01)В этом примере есть:
grid 2 2: два ряда и две колонки vertices.method bilinear: каждый patch семплится через bilinear interpolation.vertex v00 0% 0% red: верхняя левая vertex красная.vertex v10 100% 0% blue: верхняя правая vertex синяя.vertex v01 0% 100% yellow: нижняя левая vertex желтая.vertex v11 100% 100% green: нижняя правая vertex зеленая.patch p00 v00 v10 v11 v01: один patch соединяет четыре vertices.
Дефолты и вывод grid
Дефолты класса:
rows: 2
columns: 2
method: "bilinear"
interpolation.colorSpace: "srgb"grid все равно сериализуется через toString(), потому что mesh topology слишком важна, чтобы прятать ее в каноническом output. Если input не содержит grid, gradiente пытается вывести его из vertex ids или количества элементов:
mesh-gradient(vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% red, vertex v11 100% 100% blue, patch p00 v00 v10 v11 v01)mesh-gradient(vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% red, vertex v11 100% 100% blue, patch p00 v00 v10 v11 v01)Каноническая строка для такого input будет содержать выведенный config grid 2 2 method bilinear.
Grid topology
rows и columns описывают topology, а не визуальное выравнивание. У 3 x 3 grid девять vertices и четыре patches:
vertices: rows * columns
patches: (rows - 1) * (columns - 1)Например:
mesh-gradient(grid 3 3 method bilinear in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)mesh-gradient(grid 3 3 method bilinear in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)Ids v00, v10, v20, v01 и так далее не случайные. gradiente читает их как v<column><row>. Такая регулярная схема id позволяет модели валидировать patch adjacency и позволяет bicubic sampling находить соседние vertices.
Vertices
Vertex record состоит из четырех частей:
vertex <id> <x> <y> <color>Id должен быть стабильным, потому что patches ссылаются на него. Значения x/y - это length-percentage values. Цвет может быть любой строкой, которую умеет читать Culori.
Vertices не обязаны визуально выстраиваться в ровную сетку. Grid описывает topology; реальную поверхность можно искажать, двигая позиции vertices:
mesh-gradient(grid 2 2 method bilinear in oklab, vertex v00 0% 8% #ff74f6, vertex v10 100% 0% #405de6, vertex v01 12% 100% #fb7655, vertex v11 92% 88% #0f172a, patch p00 v00 v10 v11 v01)mesh-gradient(grid 2 2 method bilinear in oklab, vertex v00 0% 8% #ff74f6, vertex v10 100% 0% #405de6, vertex v01 12% 100% #fb7655, vertex v11 92% 88% #0f172a, patch p00 v00 v10 v11 v01)Когда vertices не полностью закрывают paint rectangle, встроенные renderers добавляют внешние edge triangles. Благодаря этому весь output остается заполненным, без прозрачных зазоров вокруг mesh.
Patches
Patch record состоит из шести частей:
patch <id> <top-left> <top-right> <bottom-right> <bottom-left>Четыре ссылки на vertices должны быть уникальными и существовать. Для регулярных ids patches должны описывать соседние grid cells. Для 2 x 2 grid нужен один patch. Для 3 x 3 grid нужны четыре patches. Для 4 x 4 grid нужны девять patches.
Порядок patch имеет смысл. Записывайте ссылки по часовой стрелке от верхнего левого угла:
top-left -> top-right -> bottom-right -> bottom-leftТакой порядок дает renderers предсказуемую ориентацию поверхности и делает сериализованный DSL читаемым.
Sampling method
method управляет тем, как цвета семплятся внутри каждого patch.
bilinear - значение по умолчанию. Он смешивает верхний край, смешивает нижний край, а затем смешивает два промежуточных цвета между собой. Это быстрый, предсказуемый вариант, который хорошо подходит для простых поверхностей:
mesh-gradient(grid 3 3 method bilinear in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)mesh-gradient(grid 3 3 method bilinear in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)bicubic семплит более гладкую поверхность, используя соседние vertices в регулярном grid. Он дороже, но может убрать ощущение плоских граней у больших bilinear patches:
mesh-gradient(grid 3 3 method bicubic in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)mesh-gradient(grid 3 3 method bicubic in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)Для bicubic meshes используйте регулярные vertex ids. Если sampler не может построить regular vertex grid, он не сможет понять, какие соседние цвета должны быть вокруг patch.
Цветовая интерполяция
Mesh colors используют тот же словарь interpolation, что и другие виды градиентов:
grid 2 2 method bilinear in oklab
grid 2 2 method bicubic in oklch longer hue
grid 2 2 method bilinear in hsl decreasing hueInterpolation clause относится к grid, а не к отдельным vertices или patches. Каждый patch внутри mesh использует одинаковые настройки color interpolation.
Полярные color spaces могут использовать hue interpolation modes. Это особенно заметно, когда hue оборачивается вокруг color wheel:
mesh-gradient(grid 2 2 method bilinear in hsl longer hue, vertex v00 0% 0% hsl(10, 100%, 50%), vertex v10 100% 0% hsl(350, 100%, 50%), vertex v01 0% 100% hsl(10, 100%, 50%), vertex v11 100% 100% hsl(350, 100%, 50%), patch p00 v00 v10 v11 v01)mesh-gradient(grid 2 2 method bilinear in hsl longer hue, vertex v00 0% 0% hsl(10, 100%, 50%), vertex v10 100% 0% hsl(350, 100%, 50%), vertex v01 0% 100% hsl(10, 100%, 50%), vertex v11 100% 100% hsl(350, 100%, 50%), patch p00 v00 v10 v11 v01)Поддерживаемые color spaces:
oklab
lch
oklch
hsl
hwb
lab
srgb
srgb-linear
xyz
display-p3
a98-rgb
prophoto-rgb
rec2020Hue modes имеют смысл только для полярных color spaces. Если hue mode передан для прямоугольного пространства вроде oklab, gradiente сохранит color space и сериализует градиент без hue mode.
Patch Handles
Handles - это опциональные records, привязанные к стороне patch:
handle <patch-id> <side> <from-x> <from-y> <to-x> <to-y>side может быть top, right, bottom или left.
mesh-gradient(grid 2 2 method bicubic in oklch longer hue, vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% yellow, vertex v11 100% 100% green, patch p00 v00 v10 v11 v01, handle p00 top 25% 0% 75% 0%, handle p00 right 100% 25% 100% 75%)mesh-gradient(grid 2 2 method bicubic in oklch longer hue, vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% yellow, vertex v11 100% 100% green, patch p00 v00 v10 v11 v01, handle p00 top 25% 0% 75% 0%, handle p00 right 100% 25% 100% 75%)Handles являются частью mesh model, JSON representation, parser, validation и serializer. Встроенные raster renderers семплят цвет из vertices и patches; инструменты при этом могут использовать handles, чтобы сохранять editor-side cubic edge metadata или передавать эти metadata в custom renderer.
Программное создание
Большинству пользователей лучше начинать с parse(), потому что он держит authoring близко к DSL. Когда нужно собрать mesh напрямую, используйте GradientMesh.
Конструктор принимает три параметра:
new GradientMesh(vertices, patches, config?)vertices и patches обязательны. config опционален. Пропущенные config values по возможности выводятся автоматически, а затем подставляются из дефолтов класса.
import { GradientMesh } from 'gradiente'
const gradient = new GradientMesh(
[
{
id: 'v00',
x: { kind: 'percent', value: 0 },
y: { kind: 'percent', value: 0 },
color: 'red',
},
{
id: 'v10',
x: { kind: 'percent', value: 100 },
y: { kind: 'percent', value: 0 },
color: 'blue',
},
{
id: 'v01',
x: { kind: 'percent', value: 0 },
y: { kind: 'percent', value: 100 },
color: 'yellow',
},
{
id: 'v11',
x: { kind: 'percent', value: 100 },
y: { kind: 'percent', value: 100 },
color: 'green',
},
],
[
{
id: 'p00',
topLeft: 'v00',
topRight: 'v10',
bottomRight: 'v11',
bottomLeft: 'v01',
},
],
{
method: 'bilinear',
interpolation: {
colorSpace: 'srgb',
},
},
)Семплинг patch
GradientMesh может получить цвет внутри patch без рендера всего градиента. Используйте samplePatchColor(patchId, u, v).
u и v - локальные координаты patch от 0 до 1:
u: 0 левый край, 1 правый край
v: 0 верхний край, 1 нижний крайimport { GradientMesh } from 'gradiente'
const gradient = GradientMesh.fromString(
'mesh-gradient(grid 2 2 method bilinear in oklab, vertex v00 0% 8% #ff74f6, vertex v10 100% 0% #405de6, vertex v01 12% 100% #fb7655, vertex v11 92% 88% #0f172a, patch p00 v00 v10 v11 v01)'
)
const center = gradient.samplePatchColor('p00', 0.5, 0.5)mesh-gradient(grid 2 2 method bilinear in oklab, vertex v00 0% 8% #ff74f6, vertex v10 100% 0% #405de6, vertex v01 12% 100% #fb7655, vertex v11 92% 88% #0f172a, patch p00 v00 v10 v11 v01)Sampling полезен для editors, color pickers, generated design tokens, tests и любого workflow, где нужно проверить mesh без его полной отрисовки.
Трансформация mesh-градиента
Каждый renderer target получает одну и ту же исходную модель. В этом главная идея Core API: один раз распарсить, много раз трансформировать.
import { parse, transformTo } from 'gradiente'
const gradient = parse(
'mesh-gradient(grid 3 3 method bicubic in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)'
)
const css = transformTo('css', gradient)
const canvas2d = transformTo('canvas-2d', gradient)
const webgl = transformTo('canvas-webgl', gradient)
const svg = transformTo('svg', gradient)mesh-gradient(grid 3 3 method bicubic in oklab, vertex v00 0% 0% #ff00aa, vertex v10 46% 8% #faff00, vertex v20 100% 0% #7c00ff, vertex v01 7% 45% #00c2ff, vertex v11 55% 42% #fff7cc, vertex v21 94% 56% #ff4fd8, vertex v02 0% 100% #00ff7f, vertex v12 48% 93% #00f0ff, vertex v22 100% 100% #005eff, patch p00 v00 v10 v11 v01, patch p10 v10 v20 v21 v11, patch p01 v01 v11 v12 v02, patch p11 v11 v21 v22 v12)У transformer outputs разные формы:
Нормализация
Используйте format() перед сохранением пользовательского ввода. Он парсит строку во внутреннюю модель и сериализует ее обратно в каноническую строку gradiente.
import { format } from 'gradiente'
const input = 'mesh-gradient(vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% red, vertex v11 100% 100% blue, patch p00 v00 v10 v11 v01)'
const normalized = format(input)mesh-gradient(vertex v00 0% 0% red, vertex v10 100% 0% blue, vertex v01 0% 100% red, vertex v11 100% 100% blue, patch p00 v00 v10 v11 v01)Для mesh-градиентов нормализация особенно полезна, потому что делает inferred config явным. Это дает editors, snapshots и сохраненному user input стабильную форму.
Правила валидации
У mesh-градиентов валидация строже, чем у stop-based градиентов:
rowsиcolumnsдолжны быть integers больше или равны2.- Количество vertices должно быть равно
rows * columns. - Количество patches должно быть равно
(rows - 1) * (columns - 1). - Vertex ids и patch ids должны быть уникальными.
- Patch references должны ссылаться на существующие vertices.
- Каждый patch должен использовать четыре уникальные vertices.
- Распознанные regular ids вроде
v00иv10должны оставаться внутри объявленного grid. - Распознанные regular patches должны соответствовать соседним grid cells.
- Vertex colors должны читаться через Culori.
repeating-mesh-gradient(...)отклоняется.
Эти правила строгие намеренно. Mesh-градиент ближе к geometry, чем к простому списку цветов, поэтому invalid topology быстро превращается в renderer-specific undefined behavior, если не поймать ее заранее.
Заметки о производительности
Mesh rendering дороже обычного gradient rendering. Стоимость зависит от количества patches, sampling method и target.
Используйте такой практический порядок:
- Начинайте с самого маленького grid, который может описать поверхность.
- Используйте
bilinear, когда форма простая или интерактивная. - Используйте
bicubic, когда гладкость важнее raw generation cost. - Для live editors предпочитайте Canvas 2D или Canvas WebGL.
- Используйте CSS или SVG output, когда нужен portable serialized asset.
- Кешируйте сгенерированный CSS/SVG output, если mesh меняется редко.
- Используйте
format()перед сохранением user-authored mesh strings.
Практический чеклист
Используйте этот порядок, когда собираете или валидируете mesh-градиент:
- Определите topology:
2 x 2,3 x 3,4 x 4или больше. - Называйте vertices регулярными ids вроде
v00,v10,v01,v11. - Разместите каждую vertex через x/y length-percentage coordinates.
- Дайте каждой vertex валидный Culori-readable color.
- Создайте один patch для каждой grid cell.
- Записывайте patch references по часовой стрелке от top-left.
- Выберите
bilinearдля скорости илиbicubicдля более гладких поверхностей. - Выберите interpolation:
srgbдля простой совместимости,oklabилиoklchдля более плавных переходов. - Добавляйте handles только когда вашему editor или custom renderer нужны эти metadata.
- Используйте
format()перед сохранением пользовательского ввода. - Используйте
transformTo()для renderer output вместо ручной конвертации строки.