Reputation: 111
I am working with browsers HTML5 canvas element.
I have code that (I've found online) that allows me to draw a rectangle to this canvas.
I'd like to apply a 'hatching' effect like shown in the image, while the mouse move event is firing. How can this be achieved?
My current method is to check the x and y coordinate of the mouse when the mouseMove event handler is fired. If the difference between the x and y coordinate compared to the original coordinate is greater than some predefined increment or ratio to the width and height of the rectangle, I will attempt to draw straight line/s between equidistant coordinates in the x and y directions
<!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>
Upvotes: 4
Views: 1478
Reputation: 12891
What you want to do can be achieved by filling the area depicted by your rectangle using a fill which resembles the red/white rotated pattern. But how do we actually create this pattern?
A tile is basically a shape which can be seamlessly repeated in either direction. In your case something like this should do the job:
which can be created on the fly using a linear gradient:
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);
If we repeat that tile 10 times both horizontally and vertically we get:
Actually it's not that hard as you already have the position and the size of the rectangle. So all we have to do is making the tile from step (1) a reusable pattern for the context we want to use it.
let tilePattern=ctx.createPattern(tile, 'repeat');
So everything that's left is a slight modification to your mouseMove
handler:
ctx.fillStyle = tilePattern;
ctx.fillRect(startX, startY, width,height);
ctx.strokeRect(startX, startY, width, height);
Now if we put everything together we get:
$(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>
Upvotes: 5