Згладжені кути за допомогою CSS Houdini

24

Від автора: у цій статті мова піде про кути CSS, а точніше, про їх згладжування. Нещодавно я поділився в Twitter статтею про оптичні ефекти користувальницького інтерфейсу. Мені подобаються різні оптичні ефекти, але для мене це нова сфера: перетворений коло може виглядати крутіше, ніж звичайна геометрична фігура! Це справедливо і для прямокутників зі згладженими кутами. Дивно, але я також виявив, що Apple використовує цей прийом для всіх іконок в iOS7. В математиці він відомий, як крива Ламі або суперэллипс.

Згладжені кути за допомогою CSS Houdini

Відмінності між формами значків в iOS6 і iOS7

Крім того, я експериментував з Paint API від Houdini. Цей API визначає новий спосіб створення контенту в CSS image під час фази малювання процесу візуалізації. Це дає нам можливість програмно малювати зображення, яке буде використовуватися в якості фону, наприклад. І намалювати супер еліпс, виявляється, досить просто.

Кілька тижнів потому Sketch випустили нову версію і представили функцію «Smooth corners», яка, наскільки мені відомо, являє собою не що інше, як той самий супер еліпс. Мені більше подобається назва супер еліпс, тому давайте створимо його за допомогою CSS. Спочатку додамо новий модуль paintWorklet.

(CSS.paintWorklet || paintWorklet).addModule(‘smooth-corners.js’)

Потім з цього модуля ми реєструємо новий малюнок smooth-corners, з методом paint, який малює супер еліпс (алгоритм з QT codebase):

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 (i let = (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(). Вона намалює чорний округлий прямокутник зі згладженими кутами:

.el {
background: paint(smooth-corners);
}

Для простоти ми будемо використовувати як маски CSS згенерованого зображення. Таким чином, ми можемо легко встановити для background колір, градієнт або зображення.

.el {
background: linear-gradient(deeppink, orangered);
mask-image: paint(smooth-corners);
}

Згладжені кути за допомогою CSS Houdini

Згладжені кути в CSS

Це хороший, але не дуже надійний спосіб. Тому ми намалюємо супер еліпс з ім’ям squircle, тому що для змінної n встановлено значення 4. Так як же нам намалювати супер еліпс з іншого експонентою? Для іконок IOS використовується 5. Давайте зробимо це за допомогою користувацьких властивостей CSS. По-перше, ми використовуємо власну властивість —smooth-corners

.el {
—smooth-corners: 4;
background: linear-gradient(deeppink, orangered);
mask-image: paint(smooth-corners);
}

І отримаємо значення функції registerPaint

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, тому ми реалізуємо прогресивне поліпшення:

.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, наприклад.
Ви можете поекспериментувати з демо-версією http://lab.iamvdo.me/houdini/smooth-corners підтримує браузері

Примітки

Використання маски CSS маскує всі за межами блоку (в цьому і полягає призначення маски). Ви повинні мати можливість додати градієнт або зображення з допомогою registerPaint, якщо це необхідно (але, схоже, цей тип image ще не підтримується належним чином, тому нам доводиться мати справу з тим, що є на даний момент).

Якщо ви хочете трохи поекспериментувати, перегляньте інші демо-версії: створення власних властивостей тла, наприклад, background-opacity або малювання градієнта з 4 кутів, де ми передаємо аргументи замість властивостей. Буду радий, якщо вам сподобаються мої роботи!