HTML canvas 矩形内的阴影效果

Hatching effect inside HTML canvas rectangle

背景

我正在使用浏览器 HTML5 canvas 元素。

我有代码(我在网上找到)允许我为此画一个矩形 canvas。

问题

我想在触发鼠标移动事件时应用如图所示的 'hatching' 效果。如何实现?

当前方法

我目前的方法是在触发 mouseMove 事件处理程序时检查鼠标的 x 和 y 坐标。如果 x 和 y 坐标与原始坐标之间的差异大于某个预定义的增量或与矩形的宽度和高度的比率,我将尝试在 x 和 y 的等距坐标之间绘制直线 line/s方向

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    // get references to the canvas and context
    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    // style the context
    ctx.strokeStyle = "blue";
    ctx.lineWidth=3;

    // calculate where the canvas is on the window
    // (used to help calculate mouseX/mouseY)
    var $canvas=$("#canvas");
    var canvasOffset=$canvas.offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var scrollX=$canvas.scrollLeft();
    var scrollY=$canvas.scrollTop();

    // this flage is true when the user is dragging the mouse
    var isDown=false;

    // these vars will hold the starting mouse position
    var startX;
    var startY;


    function handleMouseDown(e){
      e.preventDefault();
      e.stopPropagation();

      // save the starting x/y of the rectangle
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);

      // set a flag indicating the drag has begun
      isDown=true;
    }

    function handleMouseUp(e){
      e.preventDefault();
      e.stopPropagation();

      // the drag is over, clear the dragging flag
      isDown=false;
    }

    function handleMouseOut(e){
      e.preventDefault();
      e.stopPropagation();

      // the drag is over, clear the dragging flag
      isDown=false;
    }

    function handleMouseMove(e){
      e.preventDefault();
      e.stopPropagation();

      // if we're not dragging, just return
      if(!isDown){return;}

      // get the current mouse position
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mousemove stuff here

      // clear the canvas
      ctx.clearRect(0,0,canvas.width,canvas.height);

      // calculate the rectangle width/height based
      // on starting vs current mouse position
      var width=mouseX-startX;
      var height=mouseY-startY;

      // draw a new rect from the start position 
      // to the current mouse position
      ctx.strokeRect(startX,startY,width,height);

    }

    // listen for mouse events
    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

}); // end $(function(){});
</script>
</head>
<body>
    <h4>Drag the mouse to create a rectangle</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

可以通过使用类似于 red/white 旋转图案的填充来填充矩形描绘的区域来实现您想要做的。但是我们如何实际创建这种模式?

(1) 创建磁贴

瓷砖基本上是一种可以在任一方向上无缝重复的形状。在您的情况下,这样的事情应该可以完成工作:

可以使用线性渐变即时创建:

    let tile = document.createElement('canvas');
    tile.width = tile.height = 10;
    let ctx = tile.getContext('2d');
    let gradient = ctx.createLinearGradient(0, 0, tile.width, tile.height);
    let colorStops = [
        [0, 'white'],
        [0.35, 'white'],
        [0.35, 'red'],
        [0.5, 'red'],
        [0.5, 'white'],
        [0.85, 'white'],
        [0.85, 'red'],
        [1, 'red']
    ];
    colorStops.forEach(element => {
        gradient.addColorStop(element[0], element[1]);
    });

    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, tile.width, tile.height);

如果我们在水平和垂直方向重复该图块 10 次,我们将得到:

(2) 使用方块填充矩形

其实并不难,因为你已经知道了矩形的位置和大小。因此,我们所要做的就是将步骤 (1) 中的图块变成我们要使用它的上下文的可重用模式。

let tilePattern=ctx.createPattern(tile, 'repeat');

因此,剩下的就是对您的 mouseMove 处理程序稍作修改:

ctx.fillStyle = tilePattern;
ctx.fillRect(startX, startY, width,height);
ctx.strokeRect(startX, startY, width, height);

现在,如果我们将所有内容放在一起,我们会得到:

$(function() {
  let tile = document.createElement('canvas');
  tile.width = tile.height = 10;
  let ctx = tile.getContext('2d');
  let gradient = ctx.createLinearGradient(0, 0, tile.width, tile.height);
  let colorStops = [
    [0, 'white'],
    [0.35, 'white'],
    [0.35, 'red'],
    [0.5, 'red'],
    [0.5, 'white'],
    [0.85, 'white'],
    [0.85, 'red'],
    [1, 'red']
  ];
  colorStops.forEach(element => {
    gradient.addColorStop(element[0], element[1]);
  });

  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, tile.width, tile.height);


  let canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  let tilePattern = ctx.createPattern(tile, 'repeat');
  ctx.strokeStyle = "blue";
  ctx.lineWidth = 3;

  let $canvas = $("#canvas");
  let canvasOffset = $canvas.offset();
  let offsetX = canvasOffset.left;
  let offsetY = canvasOffset.top;
  let scrollX = $canvas.scrollLeft();
  let scrollY = $canvas.scrollTop();

  let isDown = false;

  let startX, startY, width, height;

  function handleMouseDown(e) {
    e.preventDefault();
    e.stopPropagation();

    startX = parseInt(e.clientX - offsetX);
    startY = parseInt(e.clientY - offsetY);
    width = 0;
    height = 0;
    isDown = true;
  }

  function handleMouseUp(e) {
    e.preventDefault();
    e.stopPropagation();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeRect(startX, startY, width, height);
    isDown = false;
  }

  function handleMouseOut(e) {
    e.preventDefault();
    e.stopPropagation();

    isDown = false;
  }

  function handleMouseMove(e) {
    e.preventDefault();
    e.stopPropagation();

    if (!isDown) {
      return;
    }

    mouseX = parseInt(e.clientX - offsetX);
    mouseY = parseInt(e.clientY - offsetY);

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    width = mouseX - startX;
    height = mouseY - startY;


    ctx.fillStyle = tilePattern;
    ctx.fillRect(startX, startY, width, height);
    ctx.strokeRect(startX, startY, width, height);
  }

  $("#canvas").mousedown(function(e) {
    handleMouseDown(e);
  });
  $("#canvas").mousemove(function(e) {
    handleMouseMove(e);
  });
  $("#canvas").mouseup(function(e) {
    handleMouseUp(e);
  });
  $("#canvas").mouseout(function(e) {
    handleMouseOut(e);
  });

});
body {
  background-color: ivory;
}

#canvas {
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h4>Drag the mouse to create a rectangle</h4>
<canvas id="canvas" width=300 height=300></canvas>