От автора: в этой статье речь пойдет про углы CSS, а точнее, про их сглаживание. Недавно я поделился в Twitter статьей об оптических эффектах пользовательского интерфейса. Мне нравятся различные оптические эффекты, но для меня это новая сфера: преобразованный круг может выглядеть круче, чем обычная геометрическая фигура! Это справедливо и для прямоугольников со сглаженными углами. Удивительно, но я также обнаружил, что Apple использует этот прием для всех иконок в iOS7. В математике он известен, как кривая Ламе или суперэллипс.
Различия между формами значков в iOS6 и iOS7
Кроме того, я экспериментировал с Paint API от Houdini. Этот API определяет новый способ создания контента в CSS image во время фазы рисования процесса рендеринга. Это дает нам возможность программно рисовать изображение, которое будет использоваться в качестве фона, например. И нарисовать супер эллипс, оказывается, довольно просто.
Несколько недель спустя Sketch выпустили новую версию и представили функцию “Smooth corners”, которая, насколько мне известно, представляет собой не что иное, как тот же супер эллипс. Мне больше нравится название супер эллипс, поэтому давайте создадим его с помощью CSS. Сначала добавим новый модуль paintWorklet.
1 |
(CSS.paintWorklet || paintWorklet).addModule('smooth-corners.js') |
Затем из этого модуля мы регистрируем новый рисунок smooth-corners, с методом paint, который рисует супер эллипс (алгоритм из QT codebase):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
registerPaint('smooth-corners', class { paint(ctx, size) { ctx.fillStyle = 'black' // n=4 рисует круг const n = 4 let m = n if (n > 100) m = 100 if (n < 0.00000000001) m = 0.00000000001 const r = size.width / 2 const w = size.width / 2 const h = size.height / 2 ctx.beginPath(); for (let i = 0; i < (2*r+1); i++) { const x = (i-r) + w const y = (Math.pow(Math.abs(Math.pow(r,m)-Math.pow(Math.abs(i-r),m)),1/m)) + h if (i == 0) ctx.moveTo(x, y) else ctx.lineTo(x, y) } for (let i = (2*r); i < (4*r+1); i++) { const x = (3*r-i) + w const y = (-Math.pow(Math.abs(Math.pow(r,m)-Math.pow(Math.abs(3*r-i),m)),1/m)) + h ctx.lineTo(x, y) } ctx.closePath() ctx.fill() } }) |
Давайте рассмотрим аргументы метода paint подробнее:
сtx — это объект PaintRenderingContext2D, который является подмножеством CanvasRenderingContext2D, поэтому мы можем нарисовать что угодно (почти)
size — это объект PaintSize, который представляет размер изображения
Теперь мы можем использовать это в CSS с помощью новой функции paint(). Она нарисует черный округлый прямоугольник со сглаженными углами:
1 2 3 |
.el { background: paint(smooth-corners); } |
Для простоты мы будем использовать в качестве маски CSS сгенерированное изображение. Таким образом, мы можем легко установить для background цвет, градиент или изображение.
1 2 3 4 |
.el { background: linear-gradient(deeppink, orangered); mask-image: paint(smooth-corners); } |
Сглаженные углы в CSS
Это хороший, но не очень надежный способ. Поэтому мы нарисуем супер эллипс с именем squircle, потому что для переменной n установлено значение 4. Так как же нам нарисовать супер эллипс с другой экспонентой? Для иконок IOS используется 5. Давайте сделаем это с помощью пользовательских свойств CSS. Во-первых, мы используем пользовательское свойство —smooth-corners
1 2 3 4 5 |
.el { --smooth-corners: 4; background: linear-gradient(deeppink, orangered); mask-image: paint(smooth-corners); } |
И получим значение из функции registerPaint
1 2 3 4 5 6 7 8 9 10 11 12 |
registerPaint('smooth-corners', class { static get inputProperties() { return [ '--smooth-corners' ] } paint(ctx, size, styleMap) { const exp = styleMap.get('--smooth-corners').toString() const n = exp } }) |
Обратите внимание, что метод paint() получает третий аргумент styleMap, который является API для извлечения вычисляемых значений для свойств, перечисленных в inputProperties. Мы получаем значение —smooth-corners и используем его для переменной n.
И, самое замечательное, мы можем прописать —smooth-corners прямо в CSS, а само свойство можно также анимировать, если мы зарегистрируем его с помощью CSS.registerProperty (из CSS Houdini Properties & Values API). На данный момент API-интерфейс Paint от Houdini поддерживает только в Chrome, поэтому мы реализуем прогрессивное улучшение:
1 2 3 4 5 6 7 8 9 10 11 12 |
.el { border-radius: 60px; background: linear-gradient(...) } @supports (mask-image: paint(smooth-corners)) { .el.is-loaded { border-radius: 0; mask-image: paint(smooth-corners); --smooth-corners: 5; } } |
Кроме того, поскольку Houdini является JS-in-CSS, лучше подождать, пока JavaScript будет загружен. Здесь я решил добавить к элементу класс .is-loaded.
В процессе разработки нам нужно иметь возможность автоматизировать созданный CSS с помощью плагина PostCSS, например.
Вы можете поэкспериментировать с демо-версией //lab.iamvdo.me/houdini/smooth-corners в поддерживающем браузере
Примечания
Использование маски CSS маскирует все за пределами блока (в этом и заключается предназначение маски). Вы должны иметь возможность добавить градиент или изображение с помощью registerPaint, если это необходимо (но, похоже, этот тип image еще не поддерживается должным образом, поэтому нам приходится иметь дело с тем, что есть на данный момент).
Если вы хотите немного поэкспериментировать, посмотрите другие демо-версии: создание собственных свойств фона, например, background-opacity или рисование градиента из 4 углов, где мы передаем аргументы вместо свойств. Буду рад, если вам понравятся мои работы!
Источник: //iamvdo.me/
Редакция: Команда webformyself.