Когда я создаю фильтры, тени, трансформации или градиентные фоны в CSS, я чувствую себя превосходно. Кто бы мог подумать CSS зайдет так далеко. Теперь нет необходимости использовать изображения для создания подобных эффектов. Я почти слышу как браузер говорит мне: "не парься, я сам все сделаю."
Конический градиент на CSS
Несмотря на всю свою эволюцию, на текущий момент CSS по-прежнему имеет свои ограничения. Например, фоновые градиенты могут быть созданы с помощью линейных или радиальных стилей, но не конусообразных.
В 2011 году Lea Verou начал начал вести разговор о реализации с помощью CSS конического градиента, создавая проект спецификации, которая уже была введена в официальном проекте W3C. Тем не менее, мы все еще должны ждать одобрения W3C этой функции и реализации ее браузерами, что займет довольно много времени. В то же время, я покажу вам, как имитировать конический градиент, используя только CSS3.
Симпатично, не так ли? Давайте попробуем воспроизвести нечто подобное!
Для уменьшения количества повторов кода, я буду использовать SASS. Функционал CSS имеет одну очень интересную возможность – это использование «примешиваний» - @mixin
. Примешивания (mixins) позволяют включать все свойства класса в другой класс путем простого включения имени класса как значение одного из свойств.
@mixin circle($size) {
content: "";
position: absolute;
border-radius: 50%;
width: $size;
height: $size;
left: calc(50% - #{$size/2});
top: calc(50% - #{$size/2});
}
В данном примере с помощью @mixin
мы назначили нашей фигуре свойства формы и местоположения, в итоге создав круг с абсолютным позиционированием, а также отцентрированный по горизонтали и вертикали по отношению к своему родительскому элементу.
Объединив @mixin circle
со свойством clip
, мы получили полукруг. Итак, давайте сперва создадим полный круг, соединив вместе два полукруга (окрашенные в разные цвета). Можете ли вы вообразить, что получится, если мы зададим вращение одному из этих полукругов? Волшебный эффект!
<!-- HTML -->
<div class="color"></div>
<!-- SASS -->
@mixin circle($size) {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - #{$size/2});
top: calc(50% - #{$size/2});
width: $size;
height: $size;
}
$wheel: 15em;
.color {
@include circle($wheel);
clip: rect(0, $wheel, $wheel, #{$wheel/2});
background: red;
&:after {
@include circle($wheel);
clip: rect(0, #{$wheel/2}, $wheel, 0);
background: blue;
transform:rotate(45deg);
}
}
Свойство clip: rect (top, right, bottom, left)
ограничивает видимую область прямоугольным элементом, из-за чего видимой становится только половина красного круга (смотрите пример выше). Тот же самый принцип применен и к синему кругу, т.е. к элементу .color:after
. На этой стадии мы должны получить полный круг - наполовину красный и наполовину синий. Однако, благодаря свойству transform, видимая часть синего круга наезжает на область красного круга:
Разноцветный зонтик с помощью CSS
Мы уже знаем, как выполнить этот волшебный трюк. Давайте создадим наш 12-цветный зонтик:
<div class="wheel">
<ul class="umbrella">
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
</ul>
</div>
Так как нам нужно создать полный круг, соединив полукруги (начиная с седьмого элемента – т.е. первого элемента второй половины круга), то нам нужно инвертировать обрезку (clip).
.color, .color:nth-child(n+7):after {
clip: rect(0, $wheel, $wheel, #{$wheel/2});
}
.color:after, .color:nth-child(n+7) {
@include circle($wheel);
clip: rect(0, #{$wheel/2}, $wheel, 0);
}
Начиная с седьмого элемента, свойство .color применяется к сегментам, составляющим правую половину круга, и свойство .color:after применяется к сегментам, составляющим левую половину круга.
Все почти готово. Теперь нам нужно изменить цвета и угол каждого сегмента. Давайте снова воспользуемся силой SASS для того, чтобы сгенерировать 26193^42 строк кода, написав всего лишь чуть больше десяти. ;)
$colors: (#9ED110, #50B517, #179067,
#476EAF, #9f49ac, #CC42A2, #FF3BA7, #FF5800,
#FF8100, #FEAC00, #FFCC00, #EDE604);
@for $i from 0 to length($colors) {
.color:nth-child(#{1+$i}):after {
background-color: nth($colors, $i+1);
@if $i < 6 {
transform: rotate(#{30*(1+$i)}deg);
z-index: #{length($colors)-$i};
} @else {
transform: rotate(#{-30+(30*(1+$i))}deg);
}
}
}
Во-первых, мы назначаем массив $colors
в виде списка «12 цветов радуги» и затем через итерацию последовательно применяем ко всем цветам селектор .color:nth-child(n):after
и свойства background-color, rotate и z-index.
О свойстве rotate нужно помнить несколько важных моментов. Его угол определяется согласно количеству цветов круга. В нашем случае это 12 цветов, поэтому 360/12 = 30 это угол ротации для каждого цвета. Но помните ли вы, что начиная с седьмого элемента начинается вторая половина круга? Таким образом, процесс, только что нами описанный, останавливается на седьмом элементе. Далее мы снова начинаем тот же самый процесс, но в этот раз ротация пойдет в другом направлении. Вот почему мы применили условие @else {transform: rotate(#{30+(30*(1+$i))}deg);}
, которое вычитает 30 градусов из элементов второй половины круга.
Если вы внимательный наблюдатель (и поняли весь вышеизложенный материал), вы должны были заметить, что наш зонт, на самом деле, это не что иное, как вентилятор! Хе-хе. Как я вас подколол? Итак, для последнего цвета первой половины круга, который не виден поверх остальных цветов, нам нужно инвертировать значение z-index. Например, z-index (6) = 1
и z-index (1) = 6
.
Наконец, нам нужно смягчить переход между цветами, ведь наша задача получить не зонтик цветов радуги, а конический градиент!
.umbrella {
-webkit-filter: blur(1.7em);
}
.wheel {
overflow: hidden;
box-shadow: inset 0 0 0 3em rgba(0, 0, 0, 0.3);
}
Фильтр размытия отвечает за смешивание цветов. Но применение размытия деформирует края круга. Вот почему мы добавили для элемента .wheel свойство overflow:hidden
. Внутренняя тень box-shadow применена для того, чтобы затемнить края, которые были размыты.
А вот и наш результирующий SASS код:
@mixin circle($size) {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - #{$size/2});
top: calc(50% - #{$size/2});
width: $size;
height: $size;
}
$wheel: 15em;
.wheel, .umbrella, .color {
@include circle($wheel);
}
.wheel {
overflow: hidden;
width: $wheel;
height: $wheel;
position: relative;
}
.umbrella {
position: relative;
-webkit-filter: blur(1.7em);
-webkit-transform: scale(1.35);
}
.color, .color:nth-child(n+7):after {
clip: rect(0, $wheel, $wheel, #{$wheel/2});
}
.color:after, .color:nth-child(n+7) {
@include circle($wheel);
clip: rect(0, #{$wheel/2}, $wheel, 0);
}
$colors: (#9ED110, #50B517, #179067, #476EAF,
#9f49ac, #CC42A2, #FF3BA7, #FF5800, #FF8100,
#FEAC00, #FFCC00, #EDE604);
@for $i from 0 to length($colors) {
.color:nth-child(#{1+$i}):after {
background-color: nth($colors, $i+1);
@if $i < 6 {
transform: rotate(#{30*(1+$i)}deg);
z-index: #{length($colors)-$i};
} @else {
transform: rotate(#{-30+(30*(1+$i))}deg);
}
}
}
body {
background: #ffffff;
padding: 50px;
}
С помощью конического градиента можно создать разнообразные эффекты. Самое же интересное его применение можно наблюдать на примере селектора цвета (смотрите ниже):
<!-- HTML -->
<div class="button">
<div class="center">
<div class="pin"></div>
</div>
<div class="wheel">
<ul class="colors">
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
</ul>
</div>
</div>
<!-- SASS -->
@mixin circle($size) {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - #{$size/2});
top: calc(50% - #{$size/2});
width: $size;
height: $size;
}
.button {
margin: 30px auto 0;
width: 13em;
height: 13em;
border-radius: 3em;
position: relative;
background-image: linear-gradient(to bottom, #ddd, #d5d5d5);
box-shadow: inset 0 3px 7px 0px #FFF,
inset 0 -5px 5px 0px rgba(0, 0, 0, 0.2),
0 27px 51px -10px rgba(0, 0, 0, 0.5);
&:before, &:after {
box-sizing: border-box;
display: block;
}
&:before {
@include circle(12.5em);
background-image: linear-gradient(to bottom, #fff, #aaa);
-webkit-filter: blur(4px);
}
&:after {
@include circle(11em);
background-image: linear-gradient(to bottom, #c5c5c5, #ddd 80%);
box-shadow: 0 4px 28px -10px rgba(0, 0, 0, 0.2);
}
}
$wheel: 9.5em;
.wheel, .colors, .color {
@include circle($wheel);
}
.wheel {
display: block;
z-index: 1;
box-shadow: inset 0 16px 32px 14px rgba(0, 0, 0, .7);
}
.colors {
list-style: none;
background: #ddd;
position: relative;
-webkit-filter: blur(10px);
transform: rotate(170deg) scaleX(-1);
}
.color {
clip: rect(0px, $wheel, $wheel, #{$wheel/2});
&:after {
@include circle($wheel);
clip: rect(0px, #{$wheel/2}, $wheel, 0px);
}
}
$colors: (#9ED110, #50B517, #179067, #476EAF, #9f49ac, #CC42A2,
#FF3BA7, #FF5800, #FF8100, #FEAC00, #FFCC00, #EDE604);
@for $i from 0 to length($colors) {
.color:nth-child(#{1+$i}):after {
background-color: nth($colors, $i+1);
z-index: #{length($colors)-$i};
@if $i < 6 {
transform:rotate(#{30*(1+$i)}deg);
} @else {
transform:rotate(#{-30+(30*(1+$i))}deg);
}
}
}
.color:nth-child(n+7) {
transform:rotate(180deg);
}
.center {
@include circle(6.3em);
z-index: 2;
background-image: linear-gradient(to bottom, #eee, #ccc);
box-shadow: inset 0 3px 7px 0px #FFF,
0 25px 30px -5px rgba(0, 0, 0, 0.5);
&:before {
@include circle(4.2em);
background-image: linear-gradient(to bottom, #FFF 20%, #AAA);
box-shadow: 0 25px 30px -5px rgba(0, 0, 0, .5);
}
&:after {
@include circle(3em);
background-image: linear-gradient(to bottom, #aaa, #f5f5f5 80%);
box-shadow: inset 0 -1px 4px 0px #FFF;
}
}
.pin {
@include circle(5.5em);
z-index: -1;
-webkit-animation: spin infinite 4s linear;
&:after {
content: "";
width: 40px;
height: 40px;
transform: rotate(10deg) skew(40deg, 20deg);
box-shadow: inset 0px 0px 4px 1px rgba(0, 0, 0, 0.1),
0 0 15px 3px rgba(0,0,0,.2);
background: #FFF;
position: absolute;
z-index: -1;
left: 6px;
top: 10px;
}
}
@-webkit-keyframes spin {
from{
-webkit-transform: rotate(0deg);
}
to{
-webkit-transform: rotate(360deg);
}
}
body {
background: white;
}