CSS

16 out, 2013

Gradiente cônico com CSS3

Publicidade

Quem conheceu a primeira versão do CSS, e hoje olha para o CSS3, tem o sentimento de ter saído do Paint para o Photoshop. A comparação pode até parecer exagerada, mas é exatamente assim que me sinto quando crio filtros, sombras, transformações ou backgrounds com gradiente. É incrível não precisar usar imagens para fazer gradientes, é como se o browser me falasse “não se preocupe, eu faço isso por você”.

Apesar de toda evolução, o CSS continua com algumas limitações. No caso do gradiente, por enquanto, ele só pode ser criado de forma linear ou radial, mas não de forma cônica.

Pensando nisso, em 2011, Lea Verou iniciou uma thread sobre a implementação de um gradiente cônico nativo do CSS, criando um rascunho da especificação e que já foi introduzido ao rascunho oficial da W3C. Ainda não existe uma previsão de quando poderemos usar o gradiente cônico nativo do CSS, pois é necessário que a W3C oficialize a funcionalidade e, em seguida, que os navegadores a implementem, mas, enquanto isso, mostrarei como simular um gradiente cônico utilizando apenas CSS3.

css-1

Muito bonito, mas agora mostre-me o código!

O começo

Para diminuir a repetição de código, estou usando o SASS. Um dos recursos mais interessantes dos preprocessadores de CSS é o @mixin. O @mixin é uma mistura de função e include, que, ao ser chamado, retorna o seu conteúdo, como o abaixo:

@mixin circle($size) {
  content: "";
  position: absolute;
  border-radius: 50%;
  left: calc(50% - #{$size/2});
  top: calc(50% - #{$size/2});
  width: $size;
  height: $size;
}

Este @mixin é utilizado apenas para definir propriedades de forma e posicionamento, criando um círculo de posição absoluta e centralizado horizontalmente e verticalmente em relação ao parent.

A mágica

Aqui é onde a mágica acontece! Combinando o @mixin circle com a propriedade clip, podemos obter um semicírculo. Então, se fizermos dois semicírculos que se completam (um sendo a metade direita e o outro a metade esquerda) e rotacionarmos um deles, teremos um resultado… mágico!

css-2

$wheel: 15em;
.color {
  background: red;
  @include circle($wheel);
  clip: rect(0, $wheel, $wheel, #{$wheel/2});
  &:after {
    background: blue;
    @include circle($wheel);
    clip: rect(0, #{$wheel/2}, $wheel, 0);
    transform:rotate(45deg);
  }
}

A propriedade clip: rect(top, right, bottom, left) restringe uma área visível retangular para o elemento, sendo assim, no elemento .color (vermelho), somente a metade direita é visível. Já no elemento .color:after (azul), somente a metade esquerda é visível. Contudo, ao rodá-lo 45º para a direita, a área visível do semicírculo azul “invade” a área visível do semicírculo vermelho (seu parent). Veja aqui o exemplo.

O guarda-chuva colorido

css-3

Agora que já sabemos fazer mágica, faremos um guarda-chuva de 12 cores:

<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>

Como estamos trabalhando com semicírculos e queremos um círculo, a partir do sétimo item (primeiro item da segunda metade), precisamos inverter o 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);
}

A partir do sétimo item, o .color passa a ser um semicírculo com a metade esquerda do círculo, e os elementos .color:after passam a ser a metade direita do círculo.

Agora falta pouco! Precisamos mudar as cores e o ângulo de cada elemento. Mais uma vez, vamos abusar do poder do SASS para escrever 26193^726 linhas de código em pouco mais de 10 😉

$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);
    }
  }
}

Primeiro definimos o array $colors com as “12 cores do arco-íris” e, em seguida, iteramos sobre a lista criando o seletor .color:nth-child(n):after com o background-color, rotate e z-index.

O rotate tem alguns pontos importantes, seu ângulo é definido de acordo com a quantidade de cores do círculo. No nosso caso, como são 12 cores, logo, 360º/12 = 30º será a rotação de cada cor. Mas, a partir do sétimo item, inicia a outra metade do círculo, lembra? Então, não devo aplicar a rotação ao sétimo item, apenas repetir o mesmo processo realizado na primeira metade do círculo. Por isso, existe o @else com a propriedade rotate(#{-30+(30*(1+$i))}deg), que subtrai 30º dos elementos da segunda metade do círculo.

Se você for um bom observador (e estiver entendendo tudo até aqui), deve ter percebido que nosso guarda-chuva, na verdade, é um leque! Bazinga! Então, para que a última cor da primeira metade do círculo não fique por cima das outras cores, precisamos inverter os index desses elementos. Por exemplo: z-index(6) = 1 e z-index(1) = 6.

Um toque de mágica

Para finalizar, precisamos amenizar a transição entre as cores, afinal, não queremos um guarda-chuva-leque-arco-íris, queremos um gradiente cônico!

.umbrella {
  -webkit-filter: blur(0.9em);
  -webkit-transform: scale(1.35);
}

.wheel {
  overflow: hidden;
}

Pra finalizar, o filtro blur é responsável por misturar as cores. Mas, ao aplicar o blur, as cores extrapolam o limite do círculo, por isso usamos o overflow hidden no elemento .wheel. Já o scale é utilizado para remover o efeito colateral do blur, que deixa as bordas “desbotadas”.

Veja como ficou o resultado final

O gradiente cônico pode ser aproveitado de diferentes formas e criar diferentes efeitos. Mas uma das aplicações mais interessantes é em Color Pickers.