Trajetos, formas, corte e mascaramento

A renderização de HTML é criada com base no modelo de caixa, mas há mais na vida (e no web design) do que retângulos. O CSS oferece várias maneiras de mudar quais áreas de um elemento são renderizadas, aos desenvolvedores a liberdade de criar designs compatíveis com todos os formatos e tamanhos. O recorte permite formas geométricas, enquanto o mascaramento afeta a visibilidade no nível do pixel.

Caminhos e formas

O CSS usa funções para definir formas. Abordamos informações gerais sobre funções no módulo de funções do CSS. Nesta seção, você vai aprender a criar formas em CSS. Todos os exemplos a seguir usam as formas criadas com a propriedade clip-path, que reduz a área visível apenas ao que está dentro da forma. Isso permite que os elementos sejam visualmente diferentes da caixa do elemento. Vamos falar sobre corte com mais detalhes depois.

As formas definidas em CSS podem ser básicas (como círculos, retângulos e polígonos) ou caminhos (que podem definir formas complexas e compostas).

Formas básicas

circle() e ellipse()

As funções circle() e ellipse() definem formas redondas e ovais com raios relativos a um elemento. A função circle() aceita um único tamanho ou porcentagem como argumento. Por padrão, as duas funções posicionam a forma em relação ao centro do elemento. Ambos aceitam uma posição opcional após a palavra-chave at, que pode ser expressa como comprimentos, porcentagens ou palavras-chave posicionais.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: circle(50%);
}

Quando a função circle() recebe um argumento de 50%, um círculo perfeito é renderizado.

O exemplo anterior mostra um caminho de recorte circular usando a função circle(). Um raio de 50% cria um círculo com a largura total do elemento. A função ellipse() aceita dois argumentos que representam os raios horizontal e vertical da forma.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25%);
}

A função ellipse() produz uma elipse por argumentos de porcentagem. Argumentos de 50% e 25% produzem uma elipse que se estende duas vezes mais no raio do eixo X do que no eixo Y.

O exemplo anterior mostra um caminho de recorte elíptico usando a função ellipse(). Um raio de 50% cria uma elipse com a largura total do elemento. O exemplo a seguir mostra a mesma elipse posicionada com o centro na parte de cima do elemento.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25% at center top);
}

rect() e inset()

As funções rect() e inset() oferecem diferentes maneiras de definir um retângulo definindo a posição dos lados dele em relação aos lados de um elemento. Isso permite criar retângulos que diferem visualmente da caixa padrão do elemento. Eles aceitam opcionalmente a palavra-chave round para criar um retângulo com cantos arredondados, usando a mesma sintaxe da propriedade abreviada border-radius.

A função rect() define a posição dos lados superior e inferior do retângulo em relação à borda superior do elemento e os lados esquerdo e direito em relação à borda esquerda do elemento. Essa função aceita quatro unidades de tamanho ou porcentagem como argumentos que definem os lados superior, direito, inferior e esquerdo. Você pode escolher a função rect() quando quiser um retângulo que não seja dimensionado quando o tamanho do elemento muda ou que mantenha as mesmas proporções à medida que o elemento muda.

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: rect(15px 75px 45px 10px);
}

A função rect() aceita quatro argumentos para definir o tamanho de um retângulo. Nesse caso, os argumentos são 15 px, 75 px, 45 px e 10 px.

O exemplo anterior mostra um caminho de recorte retangular definido usando a função rect(). As dimensões são relativas às bordas superior e esquerda do elemento, conforme mostrado no diagrama.

A função inset() define a posição dos lados de um retângulo pela distância para dentro de cada um dos lados de um elemento. Essa função aceita de uma a quatro unidades de tamanho ou porcentagem como argumentos, permitindo definir vários lados de uma só vez. Você pode escolher a função inset() quando quiser um retângulo que seja dimensionado com o elemento ou que tenha uma distância fixa das bordas dele.

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px);
}

A função inset() pode subtrair do tamanho intrínseco do elemento. Os argumentos dessa função no diagrama são 15px, 5px, 15px e 10px.

O exemplo anterior mostra um caminho de recorte retangular definido usando a função inset(). As dimensões são relativas aos lados do elemento.

As funções rect() e inset() aceitam opcionalmente a palavra-chave round para criar um retângulo com cantos arredondados, usando a mesma sintaxe da propriedade abreviada border-radius. O exemplo a seguir demonstra versões arredondadas dos retângulos mostrados anteriormente.

.rounded-rect {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

.rounded-inset {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

polygon()

Para outras formas, como triângulos, pentágonos, estrelas etc., a função polygon() permite criar formas conectando vários pontos com linhas retas. A função polygon() aceita uma lista de pares que consistem em duas unidades de comprimento ou porcentagem. Cada par descreve um ponto no polígono: o primeiro valor é a distância da borda esquerda do elemento, e o segundo é a distância da borda superior. Não é necessário fechar o polígono, porque ele será concluído conectando o último ponto ao primeiro.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: polygon(
    50% 0,
    0 100%,
    100% 100%
  );
}

A função polygon() aceita um número variável de argumentos para desenhar formas complexas. Nesse caso, os argumentos são criados de forma que um triângulo seja criado.

O exemplo anterior cria um caminho de recorte triangular definindo três pontos.

Por padrão, a função polygon() renderiza áreas sobrepostas como preenchidas. É possível mudar esse comportamento com um primeiro argumento opcional chamado regra de preenchimento. Para alternar entre áreas preenchidas e não preenchidas, defina a regra de preenchimento como evenodd. Para usar a regra de preenchimento padrão, defina-a como nonzero.

O exemplo anterior mostra a função polygon() com funções trigonométricas para criar polígonos regulares e formas de estrela. Isso não cria o maior polígono regular possível que se encaixa em um elemento ou o centraliza. Deixamos isso como um exercício para você tentar. As formas de estrela neste exemplo também demonstram as regras de preenchimento nonzero e evenodd.

Formas complexas

Quando as funções de forma básica não são suficientes para descrever uma forma complexa, o CSS oferece funções que usam uma sintaxe mais sofisticada para descrever recursos como curvas e linhas. Essas funções também são úteis para formas compostas (formas compostas por várias formas, como um círculo com um buraco).

path()

A função path() aceita uma string de sintaxe de caminho SVG para descrever uma forma. Isso permite criar formas complexas usando instruções que descrevem as linhas e curvas que compõem a forma. Editar diretamente a sintaxe SVG pode ser complicado. Por isso, recomendamos considerar um editor visual dedicado que possa exportar a sintaxe ao criar formas com a função path().

A função path() não usa unidades de dimensionamento CSS, e todos os valores são interpretados como pixels. Isso significa que as formas criadas com a função de caminho não são responsivas ao tamanho do elemento ou contêiner. Recomendamos usar path() apenas para formas com dimensões fixas.

shape()

A função shape() usa uma sintaxe de comando para descrever uma forma, semelhante à função path(). No entanto, os comandos da função shape() são CSS nativos e podem usar unidades de tamanho do CSS. Isso permite que formas definidas usando a função shape() sejam dimensionadas de maneira responsiva.

O exemplo anterior usa as funções path() e shape() para definir um formato de coração e um círculo com um buraco no centro. O exemplo usa o mesmo valor em pixels para as duas funções, mas as funções shape() também poderiam ter usado outras unidades de tamanho CSS, como porcentagens ou unidades relativas ao contêiner.

Corte

O recorte define quais áreas de um elemento estão visíveis, semelhante a recortar uma imagem de uma revista. A propriedade clip-path define o caminho usado para definir a área de corte.

Como você viu nos exemplos da seção anterior, qualquer uma das funções básicas de forma ou caminho pode ser usada como clip-path. A propriedade clip-path também aceita caminhos definidos em um elemento clipPath SVG, que pode ser incorporado ou estar em um arquivo separado.

Como o corte pode afetar, em particular, uma imagem: nesta imagem, uma foto de um gatinho é cortada em um círculo e em um caminho de corte complexo que descreve o gatinho por completo.

O diagrama anterior mostra como a adição de um clip-path a um elemento de imagem muda a área visível dela. O caminho de recorte superior usa a função circle(), enquanto o inferior usa um clipPath SVG. O círculo criado usando a função circle() é posicionado centralizado no elemento por padrão.

A propriedade clip-path aceita apenas um caminho. Para cortar um elemento com várias formas que não se sobrepõem, use as funções path() ou shape() para definir um caminho composto ou use um clipPath SVG. Outra opção para cenários complexos é usar mascaramento em vez de corte, que abordaremos em uma seção posterior.

Como cortar com formas

Para fazer o recorte usando uma forma básica ou uma função de caminho, defina a propriedade clip-path como o valor retornado pela função, como nos exemplos anteriores. Cada função posiciona a forma de corte de maneira diferente em relação ao elemento. Consulte a referência de cada função.

No exemplo anterior, dois elementos têm um clip-path circular aplicado usando a classe .clipped. O clip-path é posicionado em relação a cada elemento, e o texto dentro dele não é reformatado para seguir a forma.clip-path

Uma caixa de referência de um caminho de recorte

Por padrão, o caminho de corte de um elemento inclui a borda dele. Ao usar uma das funções de forma básica, é possível definir a caixa de referência do clip-path para incluir apenas a área do elemento dentro da borda. Os valores válidos para a caixa de referência são stroke-box (o padrão) e fill-box (para incluir apenas a área dentro da borda).

O exemplo anterior mostra elementos com uma borda grande (20px), cada um usando a função inset() para definir o clip-path. O elemento que corta em relação à borda ainda mostra uma parte dela. Os elementos que são cortados em relação à área dentro da borda não mostram nenhuma borda e são menores, mesmo com o mesmo valor de encarte.

Recorte com gráficos

Um caminho de recorte pode ser definido em um documento SVG, incorporado ao documento HTML ou referenciado externamente. Isso pode ser útil para definir caminhos de recorte complexos criados em programas gráficos ou caminhos de recorte que combinam várias formas.

<img id="kitten" src="kitten.png">

<svg>
  <defs>
    <clipPath id="kitten-clip-shape">
      <circle cx="130" cy="175" r="100" />
    </clipPath>
  </defs>
</svg>

<style>
  #kitten {
    clip-path: url(#kitten-clip-shape);
  }
</style>

No exemplo anterior, o clipPath com um id de kitten-clip-shape é aplicado ao elemento <img>. Nesse caso, o documento SVG está incorporado ao HTML. Se o documento SVG for um arquivo externo chamado kitten-clipper.svg, o clipPath será referenciado como url(kitten-clipper.svg#kitten-clip-shape).

Mascaramento

O mascaramento é outro método para definir quais áreas de um elemento são mostradas ou ocultadas. Enquanto o corte usa formas ou trajetórias básicas, a máscara usa os pixels de uma imagem ou um gradiente para determinar a visibilidade. Ao contrário do recorte, a máscara permite que áreas de um elemento sejam parcialmente transparentes. Várias imagens de máscara podem ser aplicadas a um elemento para produzir uma variedade de efeitos.

Para aplicar uma máscara, defina a propriedade mask-image. Essa propriedade aceita uma ou mais imagens, gradientes ou referências a elementos <mask> em um documento SVG. É possível aplicar várias imagens de máscara separando-as com vírgulas.

.my-element {
  mask-image: url(my-mask.png),
              linear-gradient(black 0%, transparent 100%);
}

No exemplo anterior, .my-element é mascarado usando uma imagem PNG, seguida por um gradiente linear. Várias máscaras são adicionadas por padrão para criar a máscara final.

O exemplo anterior mostra uma imagem com uma ou mais máscaras aplicadas. Alterne cada máscara para ver como elas se combinam e produzem o efeito final.

Máscara alfa x máscara de luminância

É possível aplicar uma máscara usando o alpha ou o luminance da imagem. Ao mascarar com base em alpha, a transparência de cada pixel na imagem da máscara é aplicada ao elemento, ignorando as informações de cor desse pixel. Ao mascarar com base em luminance, a transparência e o valor de cada pixel (brilho ou escuridão) são aplicados ao elemento. A máscara por luminância trata cores mais claras como visíveis e cores mais escuras como invisíveis.

Para definir o modo de mascaramento, use a propriedade mask-mode. Por padrão, a propriedade mask-mode é definida como match-source, que define um modo com base no tipo da imagem de máscara. Para imagens e gradientes, o padrão é alpha. Para máscaras SVG, o padrão será o valor da propriedade mask-type do elemento <mask> ou luminance, se não houver um mask-type definido.

No exemplo anterior, um padrão de teste que mostra diferentes valores de cor e alfa é usado como máscara. Ao alternar o mask-mode, você pode ver como o modo alpha se baseia na transparência, enquanto o modo luminance se baseia no brilho da cor e na transparência.

Outras propriedades de mascaramento

O CSS oferece outras propriedades para ajustar o comportamento das máscaras. Cada uma das propriedades aceita uma lista de valores separados por vírgulas, que serão correspondentes à lista de máscaras definidas pela propriedade mask-image. Se houver menos valores do que máscaras, a lista vai se repetir até que um valor seja definido para cada máscara. Se houver mais valores do que máscaras, os valores excedentes serão descartados.

Propriedade Descrição
mask-clip

Define a caixa de referência a que as máscaras de elemento são aplicadas. O valor padrão é border-box..

mask-composite

Define a interação entre máscaras quando várias delas são aplicadas ao mesmo elemento. O valor padrão é add.

mask-origin

Define a caixa de referência que atua como a origem de uma máscara. O valor padrão é border-box. O comportamento é semelhante ao de background-origin e aceita as mesmas palavras-chave.

mask-position

Define a posição de uma máscara em relação ao mask-origin. Aceita valores de palavras-chave de posição, como top ou center, porcentagens, unidades de tamanho ou valores relativos a uma palavra-chave de posição. Ele se comporta de maneira semelhante a background-position e aceita os mesmos tipos de argumentos.

mask-repeat

Define como uma máscara se repete se o elemento mascarado for maior que a máscara. O valor padrão é repeat. Ele se comporta de maneira semelhante a background-repeat e aceita os mesmos tipos de argumentos.

mask-size

Define como uma máscara é redimensionada em relação ao tamanho do elemento mascarado. O valor padrão é auto. Ele se comporta de maneira semelhante a background-size e aceita os mesmos tipos de argumentos.

A abreviação de máscara

É possível definir várias propriedades de máscara de uma só vez com a abreviação de máscara. Isso pode simplificar a configuração de várias máscaras agrupando todas as propriedades de cada uma. O atalho de máscara equivale a definir estas propriedades em ordem: mask-image, mask-mode, mask-position, mask-size, mask-repeat, mask-origin, mask-clip e mask-composite. Nem todas as propriedades precisam ser incluídas, e as que não forem serão redefinidas para o valor inicial. Com suporte para até oito propriedades por máscara, é útil ter uma referência completa disponível.

.longhand {
  mask-image: linear-gradient(white, black),
              linear-gradient(90deg, black, transparent);
  mask-mode: luminance, alpha;
  mask-position: bottom left, top right;
  mask-size: 50% 50%, 30% 30%;
}

.shorthand {
  mask: linear-gradient(white, black) luminance bottom left / 50% 50%,
        linear-gradient(90deg, black, transparent) alpha top right / 30% 30%;
}

No exemplo anterior, cada classe tem duas máscaras aplicadas. O primeiro usa propriedades individuais, enquanto o segundo usa o atalho mask. Os dois estilos são equivalentes.

Texto corrido ao redor de elementos flutuantes

Ao cortar ou mascarar um elemento, você só muda a área visível dentro da caixa dele, mas a caixa em si permanece inalterada. Isso significa que um elemento flutuante vai afetar o fluxo do documento com base na caixa delimitadora original, não nas partes visíveis do elemento. Para definir o fluxo ao redor de um elemento, use a propriedade shape-outside com o caminho de corte.

A propriedade shape-outside define o formato em que o conteúdo vai fluir ao redor de um elemento. Essa forma pode ser qualquer uma das funções de forma básica, mas não as definidas usando as funções path() ou shape(), ou um clipPath definido em um documento SVG.

A propriedade shape-outside também aceita uma imagem ou um gradiente. Assim como no mascaramento, os limites da forma são determinados pela transparência da imagem ou do gradiente. A propriedade shape-image-threshold define quais níveis de transparência são considerados dentro da forma.

Formas em animação

Como animar clip-path

É possível animar a propriedade clip-path, misturando forma a forma. Use a mesma função de forma para cada frame-chave e produzir animações suaves. Ao usar as funções polygon() ou shape(), o mesmo número de pontos precisa ser usado em cada frame-chave.

No exemplo anterior, o clip-path de um elemento faz a transição entre um pentágono e uma estrela definidos usando a função polygon(). O exemplo usa a regra de preenchimento evenodd para mostrar como os pontos de animação criam áreas sobrepostas.

Animação com offset-path

Também é possível animar elementos ao longo dos caminhos criados com essas funções de forma. A propriedade offset-path define a forma a ser usada como o caminho, e offset-distance define a posição ao longo desse caminho. Você também pode usar a função ray() com a propriedade offset-path para animar ao longo de uma linha reta.

O exemplo anterior demonstra o uso do mesmo polígono para um clip-path e um offset-path. A animação usa offset-distance para mover as estrelas menores ao longo do mesmo polígono que a estrela grande usa como clip-path.

Teste seu conhecimento

Quais das opções a seguir são funções de forma válidas?

circle()
Correto.
square()
Incorreto.
hexagon()
Incorreto.
polygon()
Correto.
rectangle()
Incorreto.
inset()
Correto.

Verdadeiro ou falso: as formas definidas com a função path() podem ser definidas usando porcentagens.

Verdadeiro
Incorreto.
Falso
Correto.

Verdadeiro ou falso: definir o caminho de recorte de um elemento não muda o fluxo de texto ao redor dele.

Verdadeiro
Correto.
Falso
Incorreto.

Qual das opções a seguir pode ser usada como um caminho de recorte?

Uma forma básica
Correto.
Um elemento clipMask SVG
Correto.
Uma imagem bitmap
Incorreto.
Um gradiente
Incorreto.

Qual das opções a seguir pode ser usada como máscara?

Uma imagem bitmap
Correto.
Um gradiente
Correto.
Um elemento de máscara SVG
Correto.
Uma forma básica, como circle() ou rect()
Incorreto.