globalCompositeOperation和同心,空心,移动形状 [英] globalCompositeOperation and concentric, hollow, moving shapes

查看:93
本文介绍了globalCompositeOperation和同心,空心,移动形状的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现以下目标:



在画布上绘制了许多同心圆(或圆环)。每个圆都有一个孔,因此在它后面绘制的较小的圆是部分可见的。每帧(我们使用window.requestAnimationFrame进行渲染)每个圆/形状/环的半径都略有增加。



一个带有两个环的场景在图片是我用作globalCompositeOperation值参考的教程。



我正在使用Firefox 28.0。



我更喜欢使用裁剪,这使我明白了:



http://jsbin.com/guzubeze/1/edit?js,output





因此,要在平局中构建洞,如何使用剪切?

->>定义正剪切子路径,并在该区域中剪切负部分,这次使用顺时针子路径:





剪切必须使用一条路径完成,因此不能使用rect():每次都会开始一条路径,并且不允许选择Clockwisity(:-)),因此您必须定义这两个函数,它们只会创建所需的子路径:

  / / rect 
函数的顺时针子路径rectPath(x,y,w,h){
ctx.moveTo( x,y);
ctx.lineTo(x + w,y);
ctx.lineTo(x + w,y + h);
ctx.lineTo(x,y + h);
}

//矩形
函数的逆时针子路径revRectPath(x,y,w,h){
ctx.moveTo(x, y);
ctx.lineTo(x,y + h);
ctx.lineTo(x + w,y + h);
ctx.lineTo(x + w,y);
}

然后您可以编写绘图代码:

 函数drawShape(cx,cy,d,缩放,旋转){
ctx.save();
ctx.translate(cx,cy);
比例=比例|| 1;
if(scale!= 1)ctx.scale(scale,scale);
旋转=旋转|| 0;
if(rotation)ctx.rotate(rotation);
//具有矩形孔的剪辑
ctx.beginPath();
var r = d / 2;
rectPath(-r,-r,d,d);
revRectPath(-0.25 * r,-0.8 * r,0.5 * r,1.6 * r);
ctx.closePath();
ctx.clip();
ctx.beginPath();
//我们被裁剪了!
ctx.arc(0,0,r,0,2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();
}

编辑:



记录下来,有一种更简单的方法来绘制所要的方案:只画一个圆,然后逆时针在其中画一个矩形。您要填充的将是矩形外部的圆内部分,这就是您想要的:

  function drawTheThing(x ,y,r){
ctx.beginPath();
ctx.arc(x,y,r,0,2 * Math.PI);
revRectPath(x-0.25 * r,y-0.8 * r,0.5 * r,1.6 * r);
ctx.fill();
ctx.closePath();
}

(我不发布图片:它是相同的)。



根据需要,如果您更改抽奖或要引入某种通用性,请使用第一个或第二个。
如果以后不更改方案,第二种解决方案则更简单=>更好。


I'm trying to achieve the following:

A number of concentric circles (or rings) are drawn on a canvas. Each circle has a "hole" in it, so the smaller circles, drawn behind it are partially visible. Each frame (we're using window.requestAnimationFrame to render) the radius of each circle/shape/ring is slightly increased.

A scenario with two rings is depicted in the image here.

The code:

function draw() {
    drawBgr();
    for (var i = 0, len = rings.length; i < len; i++) {
        rings[i].draw();
    }
}

function drawBgr() {
    context.globalCompositeOperation = "source-over";
    context.clearRect(0, 0, WIDTH, HEIGHT);
    context.rect(0, 0, WIDTH, HEIGHT);
    context.fillStyle = '#FFFFFF';
    context.fill();
}

function squareRing(ring) { //called by rings[i].draw();
    context.globalCompositeOperation = "source-over";

    context.fillRect(ring.centerX - ring.radius / 2, ring.centerY - ring.radius / 2, ring.radius, ring.radius);
    context.globalCompositeOperation = "source-out";

    context.beginPath();
    context.arc(CENTER_X, CENTER_Y, ring.radius, 0, 2 * Math.PI, false);
    //context.lineWidth = RING_MAX_LINE_WIDTH * (ring.radius / MAX_SIDE);
    context.fillStyle = '#000000';
    context.fill();
    context.globalCompositeOperation = "source-over";

}

  1. What exactly is the problem here? I'm calling clearRect before the circles are drawn. See "What I'm actually getting" image. This is the result of a SINGLE RING being drawn over a number of frames. I shouldn't be getting anything different than a black circle with a hollow square in the middle. (Note that radius is increasing each frame.)

  2. I do realize switching globalCompositeOperation might not suffice for the effect I desire. How can I draw a "hole" in an object drawn on the canvas without erasing everything in the "hole" underneath the object I'm trying to modify?

This is the tutorial I used as a reference for the globalCompositeOperation values.

I'm using Firefox 28.0.

解决方案

I would not try to use globalCompositeOperation, since i find it hard to figure out what will happen after several iterations, and even harder if the canvas was not cleared before.

I prefer to use clipping, which gets me to that :

http://jsbin.com/guzubeze/1/edit?js,output

So, to build a 'hole' in a draw, how to use clipping ?
-->> Define a positive clipping sub-path, and within this area, cut off a negative part, using this time a clockwise sub-path :

Clipping must be done with one single path, so rect() cannot be used : it does begin a path each time, and does not allow to choose clockwisity (:-)), so you have to define those two functions which will just create the desired sub-paths :

// clockwise sub-path of a rect
function rectPath(x,y,w,h) {
  ctx.moveTo(x,y);
  ctx.lineTo(x+w,y);
  ctx.lineTo(x+w,y+h);
  ctx.lineTo(x,y+h);
}

// counter-clockwise sub-path of a rect
function revRectPath(x,y,w,h) {
  ctx.moveTo(x,y);
  ctx.lineTo(x,y+h);
  ctx.lineTo(x+w,y+h);
  ctx.lineTo(x+w,y);  
}

then you can write your drawing code :

function drawShape(cx, cy, d, scale, rotation) {
  ctx.save();
  ctx.translate(cx,cy);
  scale = scale || 1;
  if (scale !=1) ctx.scale(scale, scale);
  rotation = rotation || 0;
  if (rotation) ctx.rotate(rotation);
  // clip with rectangular hole
  ctx.beginPath();
  var r=d/2; 
  rectPath(-r,-r, d, d);
  revRectPath(-0.25*r,-0.8*r, 0.5*r, 1.6*r);
  ctx.closePath();
  ctx.clip();
  ctx.beginPath();
  // we're clipped !
  ctx.arc(0,0, r, 0, 2*Math.PI);
  ctx.closePath();
  ctx.fill();
  ctx.restore();
}

Edit :

For the record, there is a simpler way to draw the asked scheme : just draw a circle, then draw counter clockwise a rect within. What you fill will be the part inside the circle that is outside the rect, which is what you want :

function drawTheThing(x,y,r) {
   ctx.beginPath();
   ctx.arc(x ,y, r, 0, 2*Math.PI);
   revRectPath(x-0.25*r, y-0.8*r, 0.5*r, 1.6*r);
   ctx.fill();
   ctx.closePath();
}

(i do not post image : it is the same).

Depending on your need if you change the draw or if you want to introduce some kind of genericity, use first or second one. If you do not change the scheme later, the second solution is simpler => better.

这篇关于globalCompositeOperation和同心,空心,移动形状的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆