Tutorial de Canvas – Parte 6 – Composição

Este é o sexto artigo de uma série que irá apresentar os recursos disponibilizados pela nova tag <canvas> do HTML5. Nesse artigo, veremos como aplicar efeitos de composição aos objetos desenhados no Canvas. Você pode acessar uma lista de todos os artigos da série nesse link.

Índice do conteúdo

  1. globalCompositeOperation
  2. Caminhos de corte
    1. Um exemplo de clip

Em todos os exemplos anteriores, as formas sempre eram desenhadas umas sobre as outras. Isso é mais do que adequado para a maioria das situações. Isso, por exemplo, limita a ordem em que formas compostas são construídas. Podemos, porém, mudar esse comportamento pelo ajuste da propriedade globalCompositeOperation.

globalCompositeOperation

Podemos não apenas desenhar novas formas atrás de formas existentes, mas podemos também usa-la para mascarar certas áreas,limpar seções do canvas (não limitando-se por áreas retangulares como o método clearRect() faz) e maisa.

globalCompositeOperation = type
Isso ajusta o tipo de composição a ser aplicada quando se desenha novas formas, onde tipo é uma String que identifica quais das doze operações de composição será usada.

Nota: Em todos os exemplo abaixo, o quadrado azul é desenhado primeiro e referenciado como “conteúdo existente”. O círculo vermelho é desenhado em segundo lugar e referenciado como “nova forma”.

source-over Esse é o padrão e desenha a nova forma sobre o conteúdo existente. Image:Canvas_composite_srcovr.png destination-over As novas formas são desenhadas atrás do conteúdo existente. Image:Canvas_composite_destovr.png
source-in A nova forma é desenhada apenas onde se sobrepõe ao conteúdo existente. Todo o resto é feito transparente. Image:Canvas_composite_srcin.png destination-in O conteúdo existente é mantido onde se sobrepõe à nova forma. Todo o resto é feito transparente. Image:Canvas_composite_destin.png
source-out A nova forma é desenhada onde não se sobrepõe o conteúdo existente. Image:Canvas_composite_srcout.png destination-out O conteúdo existe é mantido onde não se sobrepõe à nova forma. Image:Canvas_composite_destout.png
source-atop A nova forma é desenhada apenas onde se sobrepõe ao conteúdo existente. Image:Canvas_composite_srcatop.png destination-atop O conteúdo existente é mantido apenas onde se sobrepõe à nova forma. A nova forma é desenhada por trás dele. Image:Canvas_composite_destatop.png
lighter Onde as duas formas se sobrepõem a cor é determinada pela soma dos seus valores. Image:Canvas_composite_lighten.png darker Where Onde as duas formas se sobrepõem a cor é determinada pela subtração dos seus valores. Esse tipo foi removido da especificação do canvas há algum tempo e não é mais suportado. Image:Canvas_composite_darken.png
xor As formas são feitas transparentes onde se sobrepõem e desenhadas nornalmente em todo o resto. Image:Canvas_composite_xor.png copy Desenha apenas a nova forma e remove todo o resto. Image:Canvas_composite_copy.png

Você pode visualizar um exemplo desses modos de composição em operação; pode visualizar a saída aqui.

Caminhos de corte

Um caminho de corte é quase uma forma normal do canvas mas age como uma máscara para esconder partes não desejadas de formas. Isso é visto na imagem à direita. A forma da estrela em vermelho é nosso caminho de corte. Tudo o que está fora desse caminho não será desenhado no canvas.

Se compararmos caminhos de corte à propriedade globalCompositeOperation vista acima, veremos que dois modos de composição alcançam mais ou menos o mesmo efeito ( source-in e source-atop). As diferenças mais importantes entre os dois é que o caminho de corte nunca é desenhado de fato no canvas e não é afetado pela adição de novas formas. Isso faz com que os caminhos de corte sejam a solução ideal para desenhar múltiplas formas em uma área restrita.

No capitulo Desenhando formas foram mencionados os método stroke() e fill(), mas existe um terceiro método que podemos usar para os caminhos, chamado clip().

clip()
Transforma o caminho que está sendo construído em um caminho de corte.

Você usa clip() ao invés de closePath() para finalizar uma caminho e transforma-lo em um caminho de corte ao invés de contornar ou preencher o caminho.

Por padrão, o elemento <canvas> possui um caminho de corte que tem exatamente o mesmo tamanho do canvas propriamente dito. Em outras palavras, nenhum corte ocorre.

Um exemplo de clip

Nesse exemplo usaremos um caminho de corte para desenhar um conjunto aleatório de estrelas em uma região em particular.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.fillRect(0,0,150,150);
  ctx.translate(75,75);

  // Create a circular clipping path
  ctx.beginPath();
  ctx.arc(0,0,60,0,Math.PI*2,true);
  ctx.clip();

  // draw background
  var lingrad = ctx.createLinearGradient(0,-75,0,75);
  lingrad.addColorStop(0, '#232256');
  lingrad.addColorStop(1, '#143778');

  ctx.fillStyle = lingrad;
  ctx.fillRect(-75,-75,150,150);

  // draw stars
  for (var j=1;j<50;j++){
    ctx.save();
    ctx.fillStyle = '#fff';
    ctx.translate(75-Math.floor(Math.random()*150),
                  75-Math.floor(Math.random()*150));
    drawStar(ctx,Math.floor(Math.random()*4)+2);
    ctx.restore();
  }

}

function drawStar(ctx,r){
  ctx.save();
  ctx.beginPath()
  ctx.moveTo(r,0);
  for (var i=0;i<9;i++){
    ctx.rotate(Math.PI/5);
    if(i%2 == 0) {
      ctx.lineTo((r/0.525731)*0.200811,0);
    } else {
      ctx.lineTo(r,0);
    }
  }
  ctx.closePath();
  ctx.fill();
  ctx.restore();
}

Nas primeiras linhas de código, desenhamos um retângulo preto do tamanho do canvas como fundo, e depois transladamos a origem para o centro. Em seguida, criamos um caminho de corte circular desenhando um arco e chamando clip(). Caminhos de corte também são parte do estado salvo do canvas. Se quisermos manter o caminho de corte original poderíamos ter salvo o estado do canvas antes de criar um novo.

Tudo o que é desenhado depois da criação do caminho de corte irá aparecer dentro desse caminho. Você pode ver isso claramente no gradiente linear que desenhamos em seguida. Depois disso, um conjunto de 50 estrelas posicionadas e escalonadas randomicamente é desenhado, usando a função personalizada drawStar(). Novamente, as estrelas aparecem somente dentro do caminho de corte definido.

Screenshot Live sample