1

I've written some code in JS that allows the user to click on the screen to create a square, and hold down the mouse to increase the size of the square before stamping it. The problem is that the speed at which the size of the square increases while the mouse is held down slows once the mouse is moved from the originally clicked position. I'm using intervals to change the size over a time of 1 millisecond. Here is the JS code (with Jquery):

UPDATE: If you run the code snippet you won't see the issue. Try saving it as a file and running it, then the issue will probably occur.

<!DOCTYPE html>

<html>

<head>
	<title>My Canvas</title>

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>

	<script type="text/javascript">
	$(function () {

		var mouseDown
		var c = document.getElementById('myCanvas');
		var ctx = c.getContext("2d");
		var objects = []

		c.addEventListener("mousedown", onMouseDown);
		c.addEventListener("mouseup", onMouseUp);

		function createSquare(x, y, size) {
			this.x = x;
			this.y = y;
			this.size = size;
			this.draw = draw;
			this.drawStylus = drawStylus;
			this.clear = clear;
		};

		function draw() {
			ctx.fillRect(this.x, this.y, this.size, this.size);
		};

		function drawStylus() {
			ctx.fillRect(this.x, this.y, this.size, this.size);
		};

		function clear() {
			ctx.clearRect(this.x, this.y, this.size, this.size);
		}

	//	var mySquare = new createSquare(100,100,100);
	//	mySquare.draw();

		function onMouseDown() {
			mouseDown = true
			x = event.clientX
			y = event.clientY
			size = 100
			console.log('clicked')

			interval = setInterval(function () {
				size = size + 5
				var cursorSquare = new createSquare(x,y,size);
				cursorSquare.clear();
				cursorSquare.drawStylus();
			}, 1);
		};

	   function onMouseUp() {
		   console.log('up')
		   if (mouseDown) {
		     clearInterval(interval);
		     var newSquare = new createSquare(x,y,size);
		     newSquare.draw();
		     mouseDown = false
			};
	};
	});
	</script>

</head>

<body>

<canvas id='myCanvas' width="5000" height="5000" style="border:1px solid #000000;">
	
</canvas>

</body>

</html>

7
  • 1
    when i run code snippet i'm not seeing the slow down you speak of Commented Mar 20, 2016 at 2:47
  • It appears it only works if it is a file opened directly from my PC. If you try pasting that snippet into an HTML file, open it in Chrome and you might see the issue I have. Commented Mar 20, 2016 at 3:01
  • i don't see it either - from a file pasted with the content using Chrome. Commented Mar 20, 2016 at 3:01
  • I see it. You have to hold down your mouse (WITHOUT moving it), and the square will increase in size. Now, while it's increasing in size, try wiggling/moving the mouse. The speed it's increasing at will slow. Commented Mar 20, 2016 at 3:09
  • Didn't tried your code but by reading it, you should get a lot of console errors raising up, which is a probable cause for slowing down. Learn about closure and object declarations. Commented Mar 20, 2016 at 3:41

1 Answer 1

2

The problem is the interval,

First. 1 millisecond is too short a time for any rendering, the mouse is only updated around 1/60th of a second, and rendering above 1/60th (shorter period) will put an overly heavy load on the browser and as the screen refresh is also at 1/60th of a second most of the rendering will be lost before it even gets a chance to make it to the the display.

Secondly, though 1 millisecond is too short a time for some browsers (forget the min interval time for each browser) the problem is that when you present a task that is longer than the interval period the browser still keeps adding the function calls to the call stack, effectively delaying other events and browser upkeep. Continued use of setInterval in such a situation will result in a call stack overflow, crashing all javascript on the page.

The simple rule is NEVER use setInterval for any reason. Use setTimeout instead, as it protects from call stack overflows and stops uncontrolled calls being placed on the call stack. Using setTimeout can actually result in better timed intervals.

// simple timeout
var interval = 50
function myTimer(){
     setTimeout(myTimer,interval); // set up the next interval
}
// start it
setTimeout(myTimer,interval);

By getting the time via new Date().valueOf() you can adjust the timeout for the next call to stay as close as possible to the required interval, something that setInterval makes very hard to do. To stop the calls simply don't call setTimeout if a stop flag is set.

But all that said for graphics you should be using requestAnimationFrame which is very similar to setTimeout but you do not provide the timeout, the browser will get the best time to keep you in sync with the rendering and layout engines and also with the display hardware so you dont get flickering and or shearing.

function update(time){ // requestAnimationFrame calls the function with the time in milliseconds
    // do what is needed
    requestAnimationFrame(update);  // get next frame when browser is ready 1/60th second
}
requestAnimationFrame(update);  // start it going 

For coordinating with a mouse just have your mouse listeners set some variables like mouseX,mouseY and buttonState and do nothing more. In the update function use these values to handle the mouse. This produces a much more manageable mouse interface.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Blindman67, that works well. But I ran into another issue, possible related to the animation frame, here: stackoverflow.com/questions/36115240/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.