HTML5 canvas 创建模式 API

HTML5 canvas createPattern API

我有这样的代码:

<body>
    <canvas id="main" width=400 height=300></canvas>
<script>
var windowToCanvas = function(canvas, x, y) {
    var bbox = canvas.getBoundingClientRect();
    return {
        x: (x - bbox.left) * (canvas.width  / bbox.width),
        y: (y - bbox.top)  * (canvas.height / bbox.height)
    };
};

image = new Image();
image.src = "redball.png";
image.onload = function (e) {
    var canvas = document.getElementById('main'),
        context = canvas.getContext('2d');

    var pattern = context.createPattern(image, "repeat");

    function draw(loc) {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.fillStyle = pattern;
        context.beginPath();
        context.moveTo(loc.x, loc.y);
        context.lineTo(loc.x + 300, loc.y + 60);
        context.lineTo(loc.x + 70, loc.y + 200);
        context.closePath();
        context.fill();
    }

    canvas.onmousemove = function(e) {
        var event = e || window.event,
            x = event.x || event.clientX,
            y = event.y || event.clientY,
            loc = windowToCanvas(canvas, x, y);

        draw(loc);
    };
}
</script>
</body>

我调用createPattern API,用背景图填充三角形,但是当鼠标移动时,背景图也随之移动,我只想让背景图固定位置,请问如何解决?

将上下文模式视为 canvas 上的背景图像。

模式总是从 canvas 原点 [0,0] 开始。如果图案重复,则图案会向右和向下重复填充 canvas 块。

因此,如果您围绕 canvas 移动三角形,您的三角形将始终显示图案的不同部分。

有多种方法可以让三角形始终显示图案图像的相同部分。

选项#1 -- context.translate

将 canvas 原点从其默认 [0,0] 位置移动到三角形位置 [loc.x,loc.y]。您可以使用 canvas 转换来做到这一点。特别是,翻译命令将移动原点。移动原点也会移动图案左上角的起始位置,以便图案始终相对于移动三角形以相同的方式对齐:

var pattern = context.createPattern(image, "repeat");
context.fillStyle=pattern;

function draw(loc) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    // the origin [0,0] is now [loc.x,loc.y]
    context.translate(loc.x,loc.y);
    context.beginPath();
    // you are already located at [loc.x,loc.y] so
    // you don't need to add loc.x & loc.y to
    // your drawing coordinates
    context.moveTo(0,0);
    context.lineTo(300,60);
    context.lineTo(70,200);
    context.closePath();
    context.fill();
    // always clean up! Move the origina back to [0,0]
    context.translate(-loc.x,-loc.y);
}

使用翻译的演示:

var windowToCanvas = function(canvas, x, y) {
  var bbox = canvas.getBoundingClientRect();
  return {
    x: (x - bbox.left) * (canvas.width  / bbox.width),
    y: (y - bbox.top)  * (canvas.height / bbox.height)
  };
};

image = new Image();
image.src = "https://dl.dropboxusercontent.com/u/139992952/multple/jellybeans.jpg";
image.onload = function (e) {
  var canvas = document.getElementById('main'),
      context = canvas.getContext('2d');

  var pattern = context.createPattern(image, "repeat");
  context.fillStyle=pattern;

  draw({x:0,y:0});

  function draw(loc) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    // the origin [0,0] is now [loc.x,loc.y]
    context.translate(loc.x,loc.y);
    context.beginPath();
    // you are already located at [loc.x,loc.y] so
    // you don't need to add loc.x & loc.y to
    // your drawing coordinates
    context.moveTo(0,0);
    context.lineTo(300,60);
    context.lineTo(70,200);
    context.closePath();
    context.fill();
    // always clean up! Move the origina back to [0,0]
    context.translate(-loc.x,-loc.y);
  }


  canvas.onmousemove = function(e) {
    var event = e || window.event,
        x = event.x || event.clientX,
        y = event.y || event.clientY,
        loc = windowToCanvas(canvas, x, y);

    draw(loc);
  };
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>Move the mouse to move the triangle<br>The image is large, so be patient while it loads</h4>
<canvas id="main" width=600 height=600></canvas>

选项#2 -- 合成

使用合成而不是图案在三角形顶部绘制图像。合成是一种控制要在 canvas 上绘制的新像素如何与现有 canvas 像素交互的方法。特别是,source-atop 合成将导致仅在新像素与现有非透明像素重叠的地方绘制任何新像素。您要做的是以纯色绘制三角形,然后使用 source-atop 合成仅在纯色三角形像素所在的位置绘制图像:

function draw(loc) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
    context.moveTo(loc.x, loc.y);
    context.lineTo(loc.x + 300, loc.y + 60);
    context.lineTo(loc.x + 70, loc.y + 200);
    context.closePath();
    // fill the triangle with a solid color
    context.fill();
    // set compositing to 'source-atop' so
    // new drawings will only be visible if
    // they overlap a solid color pixel
    context.globalCompositeOperation='source-atop';
    context.drawImage(image,loc.x,loc.y);
    // always clean up! Set compositing back to its default value
    context.globalCompositeOperation='source-over';
}

使用合成的演示:

var windowToCanvas = function(canvas, x, y) {
  var bbox = canvas.getBoundingClientRect();
  return {
    x: (x - bbox.left) * (canvas.width  / bbox.width),
    y: (y - bbox.top)  * (canvas.height / bbox.height)
  };
};

image = new Image();
image.src = "https://dl.dropboxusercontent.com/u/139992952/multple/jellybeans.jpg";
image.onload = function (e) {
  var canvas = document.getElementById('main'),
      context = canvas.getContext('2d');

  var pattern = context.createPattern(image, "repeat");
  context.fillStyle=pattern;

  draw({x:0,y:0});

  function draw(loc) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
    context.moveTo(loc.x, loc.y);
    context.lineTo(loc.x + 300, loc.y + 60);
    context.lineTo(loc.x + 70, loc.y + 200);
    context.closePath();
    // fill the triangle with a solid color
    context.fill();
    // set compositing to 'source-atop' so
    // new drawings will only be visible if
    // they overlap a solid color pixel
    context.globalCompositeOperation='source-atop';
    context.drawImage(image,loc.x,loc.y);
    // always clean up! Set compositing back to its default value
    context.globalCompositeOperation='source-over';
  }

  canvas.onmousemove = function(e) {
    var event = e || window.event,
        x = event.x || event.clientX,
        y = event.y || event.clientY,
        loc = windowToCanvas(canvas, x, y);

    draw(loc);
  };
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>Move the mouse to move the triangle<br>The image is large, so be patient while it loads</h4>
<canvas id="main" width=600 height=600></canvas>

更多选项...

还有更多可能的选择。我将在不提供代码示例的情况下提及其中一些:

  • 创建填充三角形的 img 元素并使用 drawImage(img,loc.x,loc.y) 围绕 canvas

  • 移动三角形图像
  • 从你的三角形创建一个剪裁区域。裁剪区域导致新绘图仅显示在定义的裁剪区域中。在这种情况下,新的 drawImage 只会在三角形内部可见,而在三角形外部不可见。

  • 还有更多不太常规的选项...