Что я имею в виду под «изображениями CSS» — это изображения, созданные с использованием только HTML-элементов и CSS. Они выглядят так, как если бы они были SVG, нарисованные в Adobe Illustrator, но они были сделаны прямо в браузере. Некоторые методы, которые я видел, используются при рисовании с радиусами границы, теней коробок и иногда клип-путь
. Вы можете найти множество замечательных примеров, если вы будете искать ежедневные изображения css на CodePen.Я нарисовал некоторые из них, включая этот Infinity Gauntlet, но в одном элементе с только фоном и минимальным использованием других свойств.
Давайте посмотрим, как вы можете сами создавать изображения CSS.
Содержание статьи
Метод
Понимание синтаксиса стенограммы
а также того, как работают градиенты CSS, — это практически все, что вам нужно, чтобы нарисовать что-либо в одном элементе. В качестве обзора приводятся следующие аргументы:
фон: <'background-color'> || || [/]? || || || || ;
Они могут встречаться в любом порядке, за исключением того, что между позиция
и размером
должен быть /
. Мы должны сохранить эти два аргумента в этом порядке, иначе мы получим неожиданные результаты. Не все они должны быть включены, и для этой цели мы не будем использовать цвет
повторить
приложение
происхождение
]или клип
аргументы. Это оставляет нам изображение
размер
и положение
. Однако, поскольку фон повторяется по умолчанию, мы должны поместить background-repeat: no-repeat;
прямо под все на фоне
(если некоторые фоны должны повторяться, мы можем использовать repeating-linear-gradient ()
и повторительно-радиальный градиент ()
). В этом случае скелет CSS будет следующим:
.image {
фон: / ;
background-repeat: no-repeat;
}
Мы можем даже использовать несколько наборов фоновых аргументов! Поэтому мы можем складывать и разделять их запятыми следующим образом:
.image {
задний план:
/ ,
/ ,
/ ;
background-repeat: no-repeat;
}
Структура, приведенная выше, является основой того, как мы будем рисовать изображения — по одной линии в каждой форме. Имейте в виду, что порядок визуализации является противоположным тому, как упорядочены элементы с абсолютной или фиксированной позицией. Первый появится сверху, а не внизу. Другими словами, круги (радиальные градиенты) ниже будут отображаться снизу вверх (синий снизу, красный сверху).
.circles {
задний план:
радиальный градиент (7em 7em при 35% 35%, красный 50%, прозрачный 50%),
радиальный градиент (7em 7em при 50% 50%, золото 50%, прозрачный 50%),
радиальный градиент (7em 7em при 65% 65%, синий 50%, прозрачный 50%);
background-repeat: no-repeat;
ширина: 240 пикселей;
высота: 240 пикселей;
}
Рисунок
Мы будем использовать Sass (SCSS) для рисования этих изображений, чтобы мы могли использовать переменные для цветовой палитры. Это сделает код короче, легче читается и изменяет затемненные или более легкие варианты цветов. Вместо этого мы могли бы использовать переменные в обычном CSS и забыть Sass, но из-за отсутствия поддержки Internet Explorer, давайте придерживаться Sass. Чтобы объяснить, как это работает, мы будем экспериментировать с фигурами, используя как линейные, так и радиальные градиенты.
Настройка цветовой палитры
Наша палитра будет состоять из цветов RGB или HSL. Я объясню позже, почему сохранить цвета в любом из этих форматов. В этом примере мы будем использовать RGB.
$ r: rgb (255,0,0); // hsl (0,100%, 50%)
$ o: rgb (255,128,0); // hsl (32 100%, 50%)
Что мне нравится делать, чтобы код был коротким и легким для чтения, используйте как минимум одну букву для представления каждого цвета (например, $ r
для красного цвета). Если вы используете более темные или более светлые оттенки одного цвета, я добавляю d
перед базовой буквой или буквами для темного или l
для света. Я использовал бы $ dr
для темно-красного и $ lr
для светло-красного. Если есть потребность в более чем двух других оттенках, я добавляю число в конце, чтобы указать уровень тени. Например, $ dr1
для темно-красного, $ dr2
для более темного красного и $ lr1
для светло-красного, $ lr2
для светло-красный. Такая палитра будет выглядеть так (с темными сначала, нормальными и последними):
$ dr1: rgb (224,0,0);
$ dr2: rgb (192,0,0);
$ r: rgb (255,0,0);
$ lr1: rgb (255,48,48);
$ lr2: rgb (255,92,92);
Установка шкалы и холста
Мы будем использовать единицы измерения em
для размеров изображения, чтобы изображение можно было легко изменить пропорционально. Так как 1em равно размеру шрифта элемента, каждая единица изображения будет соответствующим образом скорректирована, если изменится. Давайте установим размер шрифта 10px и установим ширину и высоту 24em. Единицы 10px легче всего думать, потому что если вы мысленно выполните математику, вы сразу получите 240 × 240 пикселей. Затем, чтобы увидеть, где расположены края холста, мы будем использовать серый контур 1px.
$ r: rgb (255,0,0); // hsl (0,100%, 50%)
$ o: rgb (255,128,0); // hsl (32 100%, 50%)
.image {
background-repeat: no-repeat;
размер шрифта: 10 пикселей;
схема: 1px solid #aaa;
ширина: 24м;
высота: 24м;
}
Следует помнить об использовании меньших размеров шрифта; у браузеров минимальная настройка размера шрифта (по причинам доступности). Если вы установите размер шрифта 4px, а минимальный — 6px, он будет принудительно установлен на 6px.
Кроме того, вы можете включить отзывчивость, используя calc ()
и единицы просмотра. Возможно, мы сможем использовать что-то вроде calc (10px + 2vmin)
если хотите, но давайте придерживаться 10px.
Рисование фигур
Здесь начинается интересная часть. Чтобы нарисовать квадрат размером 8 × 8 мкм в центре, мы используем линейный градиент ()
с двумя одинаковыми остановками.
.image {
задний план:
линейный градиент ($ r, $ r) 50% 50% / 8em 8em;
...
}
Чтобы превратить его во что-то более похожее на трапецию, установите угол 60 градусов. В то же время добавим к нашей палитре $ T
для прозрачный
а затем разместим обе остановки для $ r
и $ T
на 63% (прямо перед тем, как нижний правый угол обрезается).
$ T: прозрачный;
.image {
задний план:
линейный градиент (60deg, $ r 63%, $ T 63%) 50% 50% / 8em 8em;
...
}
Установка обоих остановок с одним и тем же значением делает наклонную сторону такой же четкой, как и остальные. Если вы посмотрите на это более близко, но, похоже, он пикселирован:
Чтобы исправить это, мы слегка корректируем один из упоров (на 1% или примерно так), чтобы край был достаточно плавным. Итак, давайте изменим $ r
с 63% до 62%.
Это будет проблема с круглыми краями, а также при работе с радиальными градиентами, которые мы увидим позже. Если вы просматриваете это в браузере, отличном от Safari, все выглядит великолепно даже при переходе на непрозрачный цвет (скажем, оранжевый). Проблема с переходом на прозрачная
в Safari заключается в том, что вы заметите немного черной подкладки на наклонной стороне.
Это связано с тем, что ключевое слово прозрачное
в Safari всегда является черной прозрачностью, и в результате мы видим черный. Я действительно хочу, чтобы Apple исправила это, но они никогда не будут. В настоящее время добавим новую переменную $ redT
для красной прозрачности в $ r
. Сбросьте $ T
который мы использовали для прозрачного
поскольку мы больше не будем его использовать.
$ rT: rgba (255,0,0,0); // hsla (0,100%, 50%, 0)
Тогда заменим прозрачным
на $ redT
. Это позаботится о нашей проблеме Safari!
.image {
задний план:
линейный градиент (60deg, $ r 62%, $ rT 63%) 50% 50% / 8em 8em;
...
}
Если вы задавались вопросом, почему мы не использовали шестнадцатеричные цвета, Internet Explorer и Edge не поддерживают нотации #rgba и #rrggbbaa (yep, hex имеет альфа канал с конца 2016 года, если вы никогда не знали), и мы хотим, чтобы это работало как кросс-браузер, насколько это возможно. Мы также хотим оставаться в соответствии с нашим выбором цветового формата.
Теперь давайте переместим фигуру вертикально на 20% и нарисуем оранжевый круг того же размера. Кроме того, добавьте еще одну переменную для своей прозрачной версии. Для гладкого края вставьте 1% -ный зазор между твердым и прозрачным апельсинами.
$ oT: rgba (255,128,0,0); // hsla (32,100%, 50%, 0)
.image {
задний план:
линейный градиент (60deg, $ r 62%, $ rT 63%) 50% 20% / 8em 8em,
радиальный градиент (8em 8em при 50% 80%, $ o 49%, $ oT 50%);
...
}
Чтобы сохранить согласованность с нашим размером, вторая остановка цвета должна составлять 50% вместо 100%.
Позиционирующие фигуры
Способ расположения градиентов основан на том, фиксирован ли блок или процент. Предположим, мы переместим оба градиента на квадраты и попытаемся поместить их полностью через div по горизонтали.
.image {
задний план:
линейный градиент ($ r, $ r) 24em 20% / 8em 8em,
линейный градиент ($ o, $ o) 100% 80% / 8em 8em;
...
}
Красный квадрат полностью оторван от холста (обозначен), а правая сторона оранжевого квадрата касается другой стороны. Использование фиксированных единиц — это как размещение абсолютно позиционированных элементов или рисования фигур в холсте HTML5. В этом смысле верно, что точка происхождения находится в левом верхнем углу. При использовании процентов и заданного размера фона, div получает «поддельное заполнение» половины размера фона. В то же время точка происхождения фона центрирована (не путать с фоном-происхождением
что касается углов коробки).
Теперь, если мы превратили эти градиенты в радиальные градиенты в виде кругов и применили те же позиции x 24 и 100%, оба заканчиваются на другой стороне пополам. Это потому, что точка происхождения всегда находится в центре, если мы напишем фон следующим образом:
.image {
задний план:
радиальный градиент (8em 8em при 24em 20%, $ r 49%, $ rT 50%),
радиальный градиент (8em 8em при 100% 80%, $ o 49%, $ oT 50%);
...
}
Если мы переписали фон, чтобы положение и размер были после градиента и использованы 100% 100% в центре
они будут расположены как линейные градиенты. Красная выходит наружу, а оранжевая — к правому краю. «Поддельное заполнение» происходит еще раз оранжевым.
.image {
задний план:
радиальный градиент (100% 100% в центре, $ r 49%, $ rT 50%) 24em 20% / 8em 8em,
радиальный градиент (100% 100% в центре, $ o 49%, $ oT 50%) 100% 80% / 8em 8em;
...
}
Нет единого правильного способа позиционирования фигур, но чтобы разместить его как абсолютно или неподвижно расположенный элемент HTML, используйте фиксированные единицы. Если вам нужен быстрый способ разместить фигуру (используя параметры положение / размер
) в мертвой точке, 50% — лучший вариант, так как источником формы станет его центр. Используйте 100%, если он должен коснуться самой правой стороны.
Калибровочные фигуры
Размер в CSS-фоны работает так, как мы ожидали, но на него все еще влияет тип устройства, используемого для фиксированных позиций или процентов. Если мы снова возьмем наши квадраты и изменим их ширину на 10 м, красный будет расширяться вправо, а оранжевый будет расширяться в сторону.
.image {
задний план:
линейный градиент ($ r, $ r) 12em 20% / 10em 8em,
линейный градиент ($ o, $ o) 50% 80% / 10em 8em;
...
}
Если бы мы использовали em
единицы для позиции y, форма будет расти вниз или сжиматься вверх после изменения высоты. Если мы будем использовать процент, то он будет расширяться и по вертикали.
Минуту назад мы рассмотрели два способа рисования кругов с радиальными градиентами. Первый способ — указать ширину и высоту между (
и в
а затем положение после этого:
.image {
задний план:
радиальный градиент (8em 8em при 50% 50%, $ r 49%, $ rT 50%);
...
}
Второй способ — использовать 100% 100%
в центре, а затем указать позицию и размер:
.image {
задний план:
радиальный градиент (100% 100% при 50% 50%, $ r 49%, $ rT 50%) 50% 50% / 8em 8em;
...
}
Эти методы рисуют круги, но приводят к различным выходам, потому что:
- Первый способ занимает весь div, так как не было реального положения фона или его размера.
- Давая реальную позицию и размер для второго, это ограничивает рамку. Следовательно, он будет вести себя точно так же, как форма линейного градиента.
Предположим, что мы заменили $ rT
на $ o
. Вы увидите, что оранжевый будет покрывать то, что было белым или фигур под ним (если мы добавили какие-либо) для первого пути. Используя второй способ, вы легко заметите ограничительную рамку, открытую оранжевым.
Кроме того, целью 100% 100%
вместо использования круга
или эллипса
является разрешение круга занять всю ограничительную рамку. Это даже дает нам полный контроль над его размерами. Таким образом, он останется таким же, если вы измените позицию 50% 50%
на что-то еще. Если используется одно из двух ключевых слов, край круга останавливается только около 71% пути при центрировании и становится более искаженным, если его положение отрегулировано. Например, вот что происходит, когда мы меняем положение x на 0
для круга
и эллипса
:
В конечном итоге вы можете пересмотреть синтаксис как радиальный градиент (ширина высота в xy)
или радиальный градиент (100% 100% в x-in-bounding-box y-in-bounding-box) xy / width height
. Если вы рисуете только круг или овал, вы можете упростить код с первого взгляда. Если вы рисуете часть круга или часть кольца, тогда вступает в игру второй способ. В примерах, которые мы создадим далее, будет много применений.
Примеры
Готовы сейчас нарисовать что-то реальное? Мы будем проходить три примера шаг за шагом. Первые два будут статическими — одно с большим количеством полукругов, а другое — с закругленными прямоугольниками. Последний пример будет меньше, но сфокусирован на анимации.
Статическое изображение
Этот зонтик будет нашим первым статическим изображением:
Мы будем использовать палитру с красным ( $ r
и $ rT
), белый ( $ w
$ wT
), оранжевый ( $ o
и $ oT
) и темно-оранжевый ( $ do
и $ doT
).
$ r: rgb (255,40,40);
$ rT: rgba (255,40,40,0);
$ w: rgb (240 240 240);
$ wT: rgba (240,240,240,0);
$ o: rgb (255, 180, 70);
$ oT: rgba (255, 180, 70, 0);
$ do: rgb (232,144,0);
$ doT: rgba (232,144,0,0);
Давайте создадим нашу область рисования 30 × 29em.
.parasol {
// фон для перехода сюда
background-repeat: no-repeat;
размер шрифта: 10 пикселей;
схема: 1px solid #aaa;
ширина: 30 мкм;
высота: 29м;
}
Выше background-repeat
мы начнем рисовать части зонтика. Во-первых, добавьте градиенты, составляющие полюс (так как ни один из них не перекрывается, нижний порядок сверху не имеет значения в этой точке):
.parasol {
задний план:
// 1
радиальный градиент (200% 200% при 100% 100%, $ do 49%, $ doT 50%) 14em 0 / 1em 1em,
радиальный градиент (200% 200% при 0% 100%, $ o 49%, $ oT 50%) 15em 0 / 1em 1em,
// 2
линейный градиент (90deg, $ do 50%, $ o 50%) 14em 1em / 2em 25em,
// 3
радиальный градиент (100% 200% при 50% 0, $ oT 0.95em, $ o 1em, $ o 1.95em, $ do 2em, $ do 2.95em, $ doT 3em) 14em 26em / 6em 3em,
// 4
радиальный градиент (200% 200% при 100% 100%, $ o 49%, $ oT 50%) 18em 25em / 1em 1em,
радиальный градиент (200% 200% при 0% 100%, $ do 49%, $ doT 50%) 19em 25em / 1em 1em;
...
}
- Чтобы нарисовать каждую сторону верха полюса, мы использовали четверти круга, равного 1 × 1em. Чтобы они занимали свои ограничивающие прямоугольники, мы использовали круги, которые в два раза больше (
200% 200%
), расположенные внизу справа и внизу слева. Мы могли бы также использовать ключевые пары, такие каксправа внизу
илислева внизу
но использовать процентные эквиваленты короче. Обратите внимание на 1% зазоры между упорами, чтобы обеспечить гладкость. - Для длинной части мы использовали длинный прямоугольник с резким темным оранжевым до оранжевого. Нет необходимости в дробном крошечном промежутке, поскольку мы не работаем с кривой или наклоном.
- Эта часть полюса немного сложнее, чем другие, потому что мы должны поддерживать диаметр 2em. Чтобы нарисовать эту дугу, мы используем поле 6 × 3em, так что между концами есть 2em-пространство, которое также равно 2em. Затем мы используем радиальный градиент в центре вершины, где каждый стоп происходит по одному каждому (и распространяется на 0,05 мкм для гладкости).
- Последние два типа, как и первые, за исключением того, что они расположены на правом конце дуги, так что они легко вписываются. Кроме того, цвета меняются местами.
Затем над предыдущими градиентами добавьте следующее снизу вверх, чтобы нарисовать верхнюю часть зонтика без заостренных концов:
.parasol {
задний план:
радиальный градиент (100% 200% при 50% 100%, $ r 50%, $ rT 50,25%) 50% 1.5em / 9em 12em,
радиальный градиент (100% 200% при 50% 100%, $ w 50%, $ wT 50,25%) 50% 1,5em / 21em 12em,
радиальный градиент (100% 200% при 50% 100%, $ r 50%, $ rT 50,25%) 50% 1.5em / 30em 12em,
...
}
Чтобы нарисовать половину кругов, составляющих эту часть, мы использовали размер градиента 100% 200%
который заставляет каждый диаметр вписываться в его фоновую ширину, но имеют в два раза большую высоту и центрированы внизу. Заказывая их сверху вниз, чтобы самые большие были внизу и наименьшие сверху, мы получаем кривые, которые хотим.
По мере того, как наш стек градиентов растет выше, через какое-то время становится трудно определить, какой фон или группа фона соответствует той части изображения. Поэтому, чтобы упростить привязку их, мы можем разделить их на группы, возглавляемые комментарием, описывающим, для чего предназначена каждая группа. Прямо сейчас мы разделили стек на группы для верхней части зонтика и полюса.
.parasol {
задний план:
/* Вверх */
радиальный градиент (100% 200% при 50% 100%, $ r 50%, $ rT 50,25%) 50% 1.5em / 9em 12em,
радиальный градиент (100% 200% при 50% 100%, $ w 50%, $ wT 50,25%) 50% 1,5em / 21em 12em,
радиальный градиент (100% 200% при 50% 100%, $ r 50%, $ rT 50,25%) 50% 1.5em / 30em 12em,
/* столб */
радиальный градиент (200% 200% при 100% 100%, $ do 49%, $ doT 50%) 14em 0 / 1em 1em,
радиальный градиент (200% 200% при 0% 100%, $ o 49%, $ oT 50%) 15em 0 / 1em 1em,
линейный градиент (90deg, $ do 50%, $ o 50%) 14em 1em / 2em 25em,
радиальный градиент (100% 200% при 50% 0, $ oT 0.95em, $ o 1em, $ o 1.95em, $ do 2em, $ do 2.95em, $ doT 3em) 14em 26em / 6em 3em,
радиальный градиент (200% 200% при 100% 100%, $ o 49%, $ oT 50%) 18em 25em / 1em 1em,
радиальный градиент (200% 200% при 0% 100%, $ do 49%, $ doT 50%) 19em 25em / 1em 1em;
...
}
Затем, между вершиной и полюсом, мы добавим следующий фрагмент фона, чтобы отобразить концы. Чтобы определить ширину каждого сегмента, мы должны получить расстояние между каждой точкой, где встречаются красный и белый. Все они должны добавить до 30 м.
Начиная с белых и самых узких красных полукругов, мы вычитаем ширину красного 9м по ширине белого 21 мкм и разделим результат на 2, чтобы получить ширину двух белых сегментов (точка «b» на рисунке). Таким образом, результат будет равен 6em (b = (21 — 9) / 2 = 6). Тогда средний красный сегмент будет 9em (21 — (6 + 6) = 9). Теперь мы оставили внешние красные сегменты (точка «а» на рисунке). Вычтите сумму того, что у нас есть сейчас, из ширины большей красной половины круга и разделите этот результат на 2. Это будет делать значение точки a: (30em — (6 + 6 + 9)) / 2 = 4.5em .
.parasol {
задний план:
...
/ * pointy заканчивается * /
радиальный градиент () 0 13.5em / 4.5em 3em,
радиальный градиент () 4.5em 13.5em / 6em 3em,
радиальный градиент () 50% 13.5em / 9em 3em,
радиальный градиент () 19.5em 13.5em / 6em 3em,
радиальный градиент () 25.5em 13.5em / 4.5em 3em,
...
}
Чтобы нарисовать полукруги, похожие на то, как мы нарисовали верхнюю часть, начинаем с прозрачной копии цвета для каждой фигуры, чтобы они напоминали дуговые мосты. Мы также добавим дополнительную 5% к каждой ширине градиента (не ширину блока фона), чтобы каждая точка, образованная смежными фонами, не была слишком резкой и тонкой.
.parasol {
задний план:
...
/ * pointy заканчивается * /
радиальный градиент (105% 200% при 50% 100%, $ rT 49%, $ r 50%) 0 13.5em / 4.5em 3em,
радиальный градиент (105% 200% при 50% 100%, $ wT 49%, $ w 50%) 4.5em 13.5em / 6em 3em,
радиальный градиент (105% 200% при 50% 100%, $ rT 49%, $ r 50%) 50% 13.5em / 9em 3em,
радиальный градиент (105% 200% при 50% 100%, $ wT 49%, $ w 50%) 19.5em 13.5em / 6em 3em,
радиальный градиент (105% 200% при 50% 100%, $ rT 49%, $ r 50%) 25.5em 13.5em / 4.5em 3em,
...
}
Наконец, вам больше не понадобится контур 1px solid #aaa
. Наш зонтик завершен!
См. Pen Parasol от Джона Кантнера (@jkantner) на CodePen.
Что-то с закругленными прямоугольниками
Этот следующий пример будет старой моделью iPhone, в которой есть больше деталей, чем более новые модели. Дело в том, что это два закругленных прямоугольника, которые являются внешней и средней кнопкой дома.
Палитра будет состоять из черного ( $ bk
и $ bkT
) для края домашней кнопки, темно-серого ( $ dg
и $ dgT
) для тела, серый ( $ g
и $ gT
) для камеры и динамика, светло-серый ( $ lg
и $ lgT
) для внешней границы, синий ( $ bl
и $ blT
) для объектива камеры и очень темный фиолетовый ( $ p
и $ pT
) для экрана.
$ bk: rgb (10,10,10);
$ bkT: rgba (10,10,10,0);
$ dg: rgb (50,50,50);
$ dgT: rgba (50,50,50,0);
$ g: rgb (70,70,70);
$ gT: rgba (70,70,70,0);
$ lg: rgb (120, 120, 120);
$ lgT: rgba (120, 120, 120, 0);
$ bl: rgb (20, 20, 120);
$ blT: rgba (20,20,120,0);
$ p: rgb (25,20,25);
$ pT: rgba (25,20,25,0);
Давайте создадим наш холст размером 20 × 40 мкм и будем использовать тот же размер шрифта, который мы использовали для зонтика, 10px:
.iphone {
// фон идет здесь
background-repeat: no-repeat;
размер шрифта: 10 пикселей;
схема: 1px solid #aaa;
ширина: 20 мкм;
высота: 40м;
}
Прежде чем мы начнем рисовать наш первый округленный прямоугольник, нам нужно подумать о нашем пограничном радиусе, который будет 2em. Кроме того, мы хотим оставить некоторое пространство слева для переключателей блокировки и кнопок регулировки громкости, что будет 0,25 метра. По этой причине прямоугольник будет равен 19,75 × 40 мкм. Учитывая 2-кратный радиус границы, нам понадобятся два линейных градиента, пересекающих друг друга. Нужно иметь ширину 15.75em (19.75em — 2 × 2), а другая должна иметь высоту 36em (40em — 2 × 2). Поместите первый 2.25em слева, а затем второй 0.25em слева и 2em сверху.
.iphone {
задний план:
/ * body * /
линейный градиент () 2.25em 0 / 15.75em 40em,
линейный градиент () 0.25em 2em / 19.75em 36em;
...
}
Поскольку светло-серая граница будет иметь толщину 0,5 мм, давайте сделаем каждую градиентную остановку немедленно переключенной с светло-серого ( $ lg
) до темно-серого ( $ dg
) и наоборот на 0.5em и 0.5em до конца (40em — 0.5 = 39.5em для первого градиента, 19.75em — 0.5 = 19.25em для второго). Установите угол 90 градусов для второго, чтобы он стал горизонтальным.
.iphone {
задний план:
/ * body * /
линейный градиент ($ lg 0.5em, $ dg 0.5em, $ dg 39.5em, $ lg 39.5em) 2.25em 0 / 15.75em 40em,
линейный градиент (90deg, $ lg 0.5em, $ dg 0.5em, $ dg 19.25em, $ lg 19.25em) 0.25em 2em / 19.75em 36em;
...
}
В каждом квадратном углу, как показано оранжевым полем на рисунке, мы поместим закругленные края. Чтобы создать эти фигуры, мы используем радиальные градиенты, которые в два раза больше их ограничивающих прямоугольников и расположены в каждом углу. Вставьте их над фоном тела.
.iphone {
задний план:
/ * углы * /
радиальный градиент (200% 200% при 100% 100%, $ dg 1,45em, $ lg 1.5em, $ lg 50%, $ lgT 51%) 0.25em 0 / 2em 2em,
радиальный градиент (200% 200% при 0% 100%, $ dg 1,45em, $ lg 1.5em, $ lg 50%, $ lgT 51%) 18em 0 / 2em 2em,
радиальный градиент (200% 200% при 100% 0%, $ dg 1,45em, $ lg 1.5em, $ lg 50%, $ lgT 51%) 0.25em 38em / 2em 2em,
радиальный градиент (200% 200% при 0% 0%, $ dg 1.45em, $ lg 1.5em, $ lg 50%, $ lgT 51%) 18em 38em / 2em 2em,
...
}
Чтобы получить светло-серые концы толщиной 0,5 мкм, подумайте о том, где начинается градиент, а затем выполните математику. Поскольку светло-серый находится в конце, мы вычитаем 0.5em из 2em, чтобы правильно разместить первую остановку. Для гладкости мы отбираем крошечный бит с первого 1.5em и добавляем 1% к второму 50%, чтобы круглые края сливались с плоскими краями.
Теперь, если мы увеличили изображение, изменив размер шрифта до 40 пикселей или более, мы заметим швы между закругленными и плоскими краями (обведено оранжевым цветом):
. Поскольку они кажутся такими крошечными, мы можем легко их исправить, возвращаясь к фону тела и слегка изменяя градиентные остановки, пока все по-прежнему выглядит правильно при изменении размера шрифта назад к 10px.
.iphone {
задний план:
/ * body * /
линейный градиент ($ lg 0.5em, $ dg 0.55em, $ dg 39.5em, $ lg 39.55em) 2.25em 0 / 15.75em 40em,
линейный градиент (90deg, $ lg 0.5em, $ dg 0.55em, $ dg 19.175em, $ lg 19.25em) 0.25em 2em / 19.75em 36em;
...
}
Затем в одном линейном градиенте мы добавим кнопки блокировки и кнопки регулировки громкости, чтобы заполнить пространство 0,25 м слева. Если между кнопками и телом будет происходить 1px-пространство, мы можем добавить крошечное кровоточение от 0.05em к ширине фона (сделав его 0.3em), чтобы оно не торчало в темно-серый цвет.
.iphone {
задний план:
/ * кнопки регулировки громкости * /
линейный градиент ($ lgT 5em, $ lg 5em, $ lg 7.5em, $ lgT 7.5em, $ lgT 9.5em, $ lg 9.5em, $ lg 11em, $ lgT 11em, $ lgT 13em, $ lg 13em, $ lg 14.5em, $ lgT 14.5em) 0 0 / 0.3em 100%,
...
}
Похоже, мы могли использовать три серого цвета серого цвета, но, поскольку это было возможно, требовалось только одно. Это просто много неожиданных переходов между прозрачными и непрозрачными светлыми серыми бегущими вниз.
Затем добавим кромку кнопки дома, а также плоские края квадрата внутри него. Теперь квадрат внутри домашней кнопки будет 1.5 × 1.5em и будет следовать в основном той же процедуре, что и тело: два пересекающихся линейных градиента и радиальные поля для заполнения углов. Чтобы разместить их по горизонтали в центре, calc ()
пригодится. 50% + 0.125em будет выражением; если бы мы сосредоточили только тело телефона, на каждой стороне будет 0.125 м пробелов. Поэтому мы перемещаем его на 0.125 метра справа. Такое же x-позиционирование будет применяться к верхним двум фонам.
.iphone {
задний план:
/* Главная кнопка */
линейный градиент () calc (50% + 0.125em) 36.5em / 0.5em 1.5em,
линейный градиент () calc (50% + 0.125em) 37em / 1.5em 0.5em,
радиальный градиент (3em 3em at calc (50% + 0.125em) 37.25em, $ bkT 1.25em, $ bk 1.3em, $ bk 49%, $ bkT 50%),
...
}
Подобно тому, как мы заштриховали линейные градиентные части тела телефона, остановки начинаются и заканчиваются светло-серым, но с прозрачным посередине. Обратите внимание, что мы оставили промежутки между 0,05% между каждыми переходами от серого к прозрачному (и наоборот). Так же, как и углы тела, это должно обеспечить смесь между круглым углом и плоским концом внутри.
.iphone {
задний план:
/* Главная кнопка */
линейный градиент ($ lg 0.15em, $ lgT 0.2em, $ lgT 1.35em, $ lg 1.35em) calc (50% + 0.125em) 36.5em / 0.5em 1.5em,
линейный градиент (90deg, $ lg 0.15em, $ lgT 0.2em, $ lgT 1.3em, $ lg 1.35em) calc (50% + 0.125em) 37em / 1.5em 0.5em,
радиальный градиент (3em 3em at calc (50% + 0.125em) 37.25em, $ bkT 1.25em, $ bk 1.3em, $ bk 49%, $ bkT 50%),
...
}
. Кстати, поскольку контуры будут настолько малы, как вы видели ранее, мы можем лучше видеть, что делаем, увеличивая размер шрифта до 20 пикселей. Это похоже на инструмент масштабирования в программном обеспечении для редактирования изображений.
Теперь, чтобы получить углы серого квадрата точно там, где они должны быть, давайте сначала сосредоточимся на x-позиции. Мы начинаем с calc (50% + 0.125em)
затем добавляем или вычитаем ширину каждой части, или я должен указать радиус границы квадрата. Эти фоны будут выше трех последних.
.iphone {
задний план:
/* Главная кнопка */
radial-gradient(200% 200% at 100% 100%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em - 0.5em) 36.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 0% 100%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em + 0.5em) 36.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 100% 0%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em - 0.5em) 37.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 0% 0%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em + 0.5em) 37.5em / 0.5em 0.5em,
...
}
Then the screen will be a 17.25 × 30em rectangle. Just like parts of the home button, we’ll horizontally center it using calc(50% + 0.125em)
. From the top, it’ll be 5em.
.iphone {
background:
/* screen */
linear-gradient($p, $p) calc(50% + 0.125em) 5em / 17.25em 30em,
...
}
Lastly, we’ll add the camera and speaker. The camera is a straightforward 1 × 1 blue-to-gray radial with no fancy calculations. The pure-gray speaker though will be a bit more involved. It will be a 5 × 1em rectangle and have a 0.5em border radius. To draw that, we first draw a rectangle with the first 4ems of the width and center it with calc(50% + 0.125em)
. Then we use 0.5 × 1em half circles in which the gradient positions are 100% 50%
and 0% 50%
. The best way to position these left and right of the rectangle is to use some new calc()
expressions. For the left, we’ll subtract half the rectangle width and half the half circle width from the body center (50% + 0.125em - 2em - 0.25em
). The right will follow the same pattern but with addition, so 50% + 0.125em + 2em + 0.25em
.
.iphone {
background:
/* camera */
radial-gradient(1em 1em at 6.25em 2.5em, $bl 0.2em, $g 0.21em, $g 49%, $gT 50%),
/* speaker */
radial-gradient(200% 100% at 100% 50%, $g 49%, $gT 50%) calc(50% + 0.125em - 2em - 0.25em) 2em / 0.5em 1em,
radial-gradient(200% 100% at 0% 50%, $g 49%, $gT 50%) calc(50% + 0.125em + 2em + 0.25em) 2em / 0.5em 1em,
linear-gradient($g, $g) calc(50% + 0.125em) 2em / 4em 1em,
...
}
Remove the gray outline around the div, and the iPhone is complete!
See the Pen iPhone by Jon Kantner (@jkantner) on CodePen.
Animated Images
You might be thinking you could use background-position
to animate these sorts of images, but you can only do so much. For instance, it’s impossible to animate the rotation of an individual background by itself. In fact, background-position
animations don’t typically perform as well as transform
animations, so I don’t recommend it.
To animate any part of an image any way we wish, we can let the :before
or :after
pseudo-elements be responsible for that part. If we need more selections, then we can revert to multiple child divs, yet not needing one for each little detail. For our animated image example, we’ll create this animated radar:
We draw the static part first, which is everything except the gray frame and rotating hand. Before that, let’s supply our palette (note: We won’t need a $dgnT
for $dgn
) and base code.
$gn: rgb(0,192,0);
$gnT: rgba(0,192,0,0);
$dgn: rgb(0,48,0);
$gy: rgb(128,128,128);
$gyT: rgba(128,128,128,0);
$bk: rgb(0,0,0);
$bkT: rgba(0,0,0,0);
.radar {
background-repeat: no-repeat;
font-size: 10px;
outline: 1px solid #aaa;
width: 20em;
height: 20em;
}
Since this image is going to be completely round, we can safely apply a border radius of 50%. Then, we can use a repeating radial gradient to draw the rings—about 1/3 way apart from each other.
.radar {
background:
/* rings */
repeating-radial-gradient($dgn, $dgn 2.96em, $gn 3em, $gn 3.26em, $dgn 3.3em);
background-repeat: no-repeat;
border-radius: 50%;
...
}
Also note the extra $dgn
at the start. For repeating gradients to start, end, and loop as expected, we need to specify the starting color at 0 (or without 0).
Unlike the previous example, we’re not using calc()
to center the lines because Internet Explorer will render the whole thing awkwardly when we use a pseudo-element later. To draw four 0.4em lines that intersect one other in the center, know that half of the line should be half the div at 10em. So then, we subtract and add half of 0.4 (0.4 / 2 = 0.2) on each side. In other words, the left of the green should be 9.8em, and the right should be 10.2em. For the 45deg diagonals though, we must multiply 10em by the square root of 2 to get their center (10 × √2 ≈ 14.14). It’s the length of the longest side of a 10em right triangle. As a result, the sides of each diagonal would be approximately at 13.94 and 14.34em.
.radar {
background:
/* lines */
linear-gradient($gnT 9.8em, $gn 9.8em, $gn 10.2em, $gnT 10.2em),
linear-gradient(45deg,$gnT 13.94em, $gn 13.98em, $gn 14.3em, $gnT 14.34em),
linear-gradient(90deg,$gnT 9.8em, $gn 9.8em, $gn 10.2em, $gnT 10.2em),
linear-gradient(-45deg,$gnT 13.94em, $gn 13.98em, $gn 14.3em, $gnT 14.34em),
...
}
To prevent the pixelation of the diagonals, we left a tiny 0.04em gap between green and transparent green. Then, to create some lighting, add this transparent-to-black radial gradient:
.radar {
background:
/* lighting */
radial-gradient(100% 100%, $bkT, $bk 9.9em,$bkT 10em),
...
}
That completes the static part of the radar. Now we draw the gray frame and hand in another background stack under :before
and add the animation. There’s a reason we didn’t include the frame here. Because the hand container should fit the whole div, we don’t want it to overlap the frame.
This pseudo-element shall fill the space, and to ensure it stays in there, let’s absolutely position it. We’ll use the same border radius so that it stays round while animated in Safari.
.radar {
...
position: relative;
&:before {
background-repeat: no-repeat;
border-radius: 50%;
content: "";
position: absolute;
width: 100%;
height: 100%;
}
}
Then, to draw the hand, we make it half the size of its container and keep it at the top left corner. Finally, on top of that, we draw the frame.
.radar {
...
&:before {
animation: scan 5s linear infinite;
background:
/* frame */
radial-gradient($gyT 9.20em, $gy 9.25em, $gy 10em, $gyT 10.05em),
/* hand */
linear-gradient(45deg, $gnT 6em, $gn) 0 0 / 50% 50%;
...
}
}
@keyframes scan {
from {
transform: rotate(0);
}
to {
transform: rotate(1turn);
}
}
Now our little gadget is complete!
See the Pen Radar by Jon Kantner (@jkantner) on CodePen.
Benefits (Plus a Drawback)
This approach of drawing CSS images has several advantages. First, the HTML will be very lightweight compared to a rasterized image file. Second, it’s great for tackling images that are impossible to draw well without using experimental properties and APIs that might not be widely supported.
It’s not to say that this method is better than using a parent element nested with children for the shapes. There is a drawback though. You have to give up being able to highlight individual shapes using the browser dev tools. You’ll need to comment and uncomment a background to identify which it is. As long as you group and label each chunk of backgrounds, you can find that particular background faster.
Conclusion
In a nutshell, the method for drawing of CSS images we’ve covered in this post allows us to:
- Set up a palette made up of variables for the colors.
- Disable the background repeat, set a scale with
font-size
and a canvas width and height inem
units for the target element. - Use a temporary
outline
to show the edges as we work. - Draw each shape from bottom to top because backgrounds are rendered in that order. The
background
syntax for each shape followsimage position / size
(with or without the position and size).
There’s a lot of thinking outside the box going on as well as experimentation to get the desired result. The three examples we created were just enough to demonstrate the concept. We’ve looked at how we order each background, drawing parts of circles, rounded rectangles, and slightly adjusting gradient stops for smooth edges. To learn more, feel free to dissect and study other examples I’ve made in this CodePen collection!