I am making a button with javascript. When I click on the box that says "click me", it should print the line "now you clicked me". However, when I embedded it in an HTML file and open it with chrome, something went wrong. Initially, the button appears, but when the mouse hovers over it, the entire button disappears. When I click on where the button was, it still prints the line. Here is my code, adapted from khan academy computer science courses on drawing buttons. Also, the code works in khan academy but not when I open it with chrome. Is there any way I can make the button stay on the screen even I hover above it?
<!DOCTYPE html>
<!-- This is based on DillingerLee's great template here:
https://github.com/Team-Code/KA_Offline -->
<html>
<head>
<title>Processing.JS inside Webpages: Template</title>
</head>
<body>
<p align="center">
<!--This draws the Canvas on the webpage -->
<canvas id="mycanvas"></canvas>
</p>
</body>
<!-- Run all the JavaScript stuff -->
<!-- Include the processing.js library -->
<!-- See https://khanacademy.zendesk.com/hc/en-us/articles/202260404-What-parts-of-ProcessingJS-does-Khan-Academy-support- for differences -->
<script src="https://cdn.jsdelivr.net/processing.js/1.4.8/processing.min.js"></script>
<script>
var sketchProc = function(processingInstance) {
with (processingInstance) {
size(600, 400);
frameRate(30);
/******************
*Button Object Type
*******************/
var Button = function(config) {
this.x = config.x || 0;
this.y = config.y || 0;
this.width = config.width || 80;
this.height = config.height || 50;
this.label = config.label || "Click";
this.color = config.color || color(207, 85, 85);
this.onClick = config.onClick || function() {};
};
//draw the button
Button.prototype.draw = function() {
if (this.isMouseInside() && mouseIsPressed()) {
fill(255, 255, 255);
}
else {
fill(this.color);
}
rectMode(CENTER);
rect(this.x, this.y, this.width, this.height, 5);
fill(0, 0, 0);
textSize(19);
textAlign(CENTER, CENTER);
text(this.label, this.x, this.y);
};
//check if mouse cursor is inside the button
Button.prototype.isMouseInside = function() {
return mouseX > this.x-this.width/2 &&
mouseX < (this.x + this.width/2) &&
mouseY > this.y - this.height/2 &&
mouseY < (this.y + this.height/2);
};
//handle mouse clicks for the button
Button.prototype.handleMouseClick = function() {
if (this.isMouseInside()) {
this.onClick();
}
};
/** create object instances **/
//create button
var btn1 = new Button(
{
x:width/2,
y:height/2,
width : 72,
height : 36,
label : "Click me",
color: color(35, 176, 110),
onClick : function(){
println("Now you clicked me");
}
}
);
draw = function() {
background(98, 122, 54);
//Draw the button
btn1.draw();
};
mouseClicked = function() {
btn1.handleMouseClick();
};
}};
// Get the canvas that Processing-js will use
var canvas = document.getElementById("mycanvas");
// Pass the function sketchProc (defined in myCode.js) to Processing's constructor.
var processingInstance = new Processing(canvas, sketchProc);
</script>
</html>
When running your code, as you hover over the button, the following error is printed in the browser's console (press F12 and click console to see this):
Meaning that something on line 42 of the HTML file throws an error. Let's investigate!
On this line, the following code can be found:
if (this.isMouseInside() && mouseIsPressed())
Apparently, the second part of this if statement is not defined.
Looking into the documentation on the p5.js website (link), it looks like mouseIsPressed is a boolean value (true or false) and not a function, and should thus not be invoked using brackets. The following should then work:
if (this.isMouseInside() && mouseIsPressed)
However, at least for me, this still does not work as intended. A similar error is thrown. Looking in the processing.org online documentation, I came across a reference to the mousePressed boolean instead (link).
Finally, using this, the code works as intended:
if (this.isMouseInside() && mousePressed)
If I understand it correctly, you can make your own custom functions called mouseIsPressed and mousePressed, which are then called automatically by processing when such an event occurs, but in this case using the boolean is simpler and should suffice.
The code is using a library called Processing. It is a problem in line 42. Instead of mouseIsPressed(), you should use this.mouseIsPressed
if (this.isMouseInside() && this.mouseIsPressed) {
fill(255, 255, 255);
}
With the wrong code, if you open Chrome dev tools (F12), and try to hover above it, you see an error message on the console, says "mouseIsPressed() is not a function", because you did not define this function. You should refer to the Button object with the this keyword, then access mouseIsPressed, which looks like to me a build-in Processing variable.
Related
I have used the pointer lock on my canvas element, and the canvas is on full screen. I want to detect right clicks and left clicks to respond to them. Is it possible to respond to clicks in full screen and pointer lock? I already know how to use the pointer lock api and the fullscreen api, I don't want any answers explaining how to use them. Any help would be appreciated.
Based on the experiments I've done, the short answer is "it depends." Take a look at the following demo. There is a canvas scaled to be a quarter of the screen size in each dimension. When you move the cursor over it, a white circle appears on the canvas. When you left click, you'll draw a red circle to the canvas, and when you right click, you'll draw a cyan circle to the canvas. When you click the "Full screen" button, you'll activate pointer lock and enter fullscreen mode. If you press the "Esc" key, you'll exit pointer lock and fullscreen mode.
Note that you'll need to copy and paste the code into a file and load it. The demo won't run if you just click "Run code snippet."
As far as your question, there are two issues, I'm aware of:
In Chrome, both right- and left-click events are triggered even while in fullscreen/pointer lock. However, in Firefox, only left-click events are triggered; I was unable to get right-click events using any of the handlers I tried (click, mousedown, mouseup, contextmenu). When not in fullscreen/pointer lock, both left- and right-click events get triggered as expected in both browsers. If anyone has any solutions for listening to right-click events while in fullscreen/pointer lock, I'd love to hear them.
It seems that in pointer lock in both Chrome/Firefox, events no longer trickle down to elements contained in the element with pointer lock, but they continue to bubble up to parent elements. So in the demo, the canvas is inside a div. The div has pointer lock. onclick handlers are attached to the canvas, div, and document to report click events in the console. Without pointer lock, clicking on the canvas triggers onclick handlers for all three elements (canvas, div, and document). However, with pointer lock on the div, the onclick handler for the canvas never gets triggered, though the handlers for the div and the document do.
I also identified a couple other quirks to Firefox that, while not directly related to your initial question, might be helpful to folks interested in implementing this sort of thing:
When fullscreen mode is entered, Firefox will apply styles to the fullscreen element to get it to fill the screen. I was unable to get the canvas styled correctly (i.e. to take up the full screen) when it was placed full screen. Rather, I had to wrap the canvas in a div and enter full screen on the div. See the Fullscreen API documentation on MDN for more info:
if you're trying to emulate WebKit's behavior on Gecko, you need to place the element you want to present inside another element, which you'll make fullscreen instead, and use CSS rules to adjust the inner element to match the appearance you want.
In Firefox, activating fullscreen mode deactivated pointer lock. In order to get both activated, I had to first activate fullscreen mode and then activate pointer lock. However the simple two lines of code:
canvasContainer.requestFullscreen();
canvasContainer.requestPointerLock();
did not work. My understanding of what was happening is that the call to requestPointerLock got initiated before full screen mode was fully established. This led to pointer lock being activated and then quickly deactivated again. I found it necessary to wait until fullscreen mode was fully established before calling requestPointerLock(). Checking that document.mozFullScreenElement !== null seemed to be sufficient for checking that full screen mode was completely operational. The following following click handler definition worked to solve this problem for me:
document.getElementById('fullscreen_button').onclick = function(e) {
// When button is clicked, enter both full screen and pointer lock
canvasContainer.requestFullscreen();
var timeout = 2000;
var interval = window.setInterval(function() {
if (document.mozFullScreenElement !== null) {
window.clearInterval(interval);
canvasContainer.requestPointerLock();
} else if (timeout <= 0) {
addErrorMessage('Unable to establish pointer lock.');
clearTimeout(interval);
} else {
timeout -= 50;
}
}, 50);
}
This function repeatedly checks if full screen mode is established. When it is, it initiate pointer lock. If fullscreen mode can't be determined after 2 s, it times out.
I haven't done any testing in IE.
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<style>
</style>
</head>
<body>
<p id="msgs">Click 'Full screen' button below to go full screen. <br>
Click the left mouse button to draw a red circle. <br>
Click any other mouse button to draw a cyan circle. <br>
Press the 'Esc' key to exit full screen.</p>
<div id="canvas_container">
<canvas id="canvas"> </canvas>
</div>
<br>
<button id='fullscreen_button'>Full screen</button>
</body>
<script>
// Display constants
var CANVAS_BG_COLOR = 'rgb(75, 75, 75)';
var LEFT_CLICK_COLOR = 'rgb(255, 150, 150)';
var OTHER_CLICK_COLOR = 'rgb(150, 255, 255)';
var CURSOR_COLOR = 'rgb(200, 200, 200)';
var CANVAS_SCALING_FACTOR = 4; // Ratio between screen dimension and canvas dimension before going full-screen
// Store mouse position
var mouseX, mouseY;
// Setup onscreen canvas, smaller than the screen by a factor of CANVAS_SCALING_FACTOR
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = screen.width/CANVAS_SCALING_FACTOR;
canvas.height = screen.height/CANVAS_SCALING_FACTOR;
// Create an offscreen canvas that's the same as the size of the screen
var offscreenCanvas = document.createElement('canvas');
var offscreenCtx = offscreenCanvas.getContext('2d');
offscreenCanvas.width = screen.width;
offscreenCanvas.height = screen.height;
var canvasContainer = document.getElementById('canvas_container');
// Radius of the circle drawn and of the circle cursor
var circleRadius = 12;
var cursorRadius = circleRadius/CANVAS_SCALING_FACTOR
offscreenCtx.drawCircle = ctx.drawCircle = function (x, y, color, radius) {
this.fillStyle = color;
this.beginPath();
this.arc(x, y, radius, 0, 2*Math.PI, true);
this.fill();
}
offscreenCtx.clearCanvas = function() {
this.fillStyle = CANVAS_BG_COLOR;
this.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
ctx.update = function() {
// Copy the offscreen canvas, scaling down if not in full-screen mode
this.drawImage(offscreenCanvas, 0, 0, offscreenCanvas.width, offscreenCanvas.height,
0, 0, canvas.width, canvas.height);
// Draw the cursor
this.drawCircle(mouseX, mouseY, CURSOR_COLOR, cursorRadius);
}
function pointerLockActive() {
return document.pointerLockElement===canvasContainer || document.mozPointerLockElement === canvasContainer;
}
// Perform initial canvas setup
offscreenCtx.clearCanvas();
ctx.update();
// Setup pointerlock and fullscreen API functions for cross-browser support
function addErrorMessage(msg) {
document.getElementById('msgs').innerHTML += ('<br><font color="red">' + msg + '</font>');
}
canvasContainer.requestPointerLock = canvasContainer.requestPointerLock || canvasContainer.mozRequestPointerLock;
canvasContainer.requestFullscreen = canvasContainer.webkitRequestFullscreen || canvasContainer.mozRequestFullScreen || canvasContainer.msRequestFullscreen
if (!canvasContainer.requestPointerLock) addErrorMessage('Error: Pointer lock not available');
if (!canvasContainer.requestFullscreen) addErrorMessage('Error: Full screen mode not available');
canvasContainer.addEventListener('mousemove', function(e) {
if (pointerLockActive()) {
// If in pointer lock, then cursor positions need to be updated manually;
// Normal cursor positions (e.g. e.clientX and e.clientY) don't get updated in pointer lock
mouseX += e.movementX, mouseY += e.movementY;
// Prevent the mouse from moving off-screen
mouseX = Math.min(Math.max(0, mouseX), canvas.width);
mouseY = Math.min(Math.max(0, mouseY), canvas.height);
} else {
// If pointer lock is inactive, then mouse position is just position relative to canvas offset
mouseX = (e.pageX - canvas.offsetLeft)
mouseY = (e.pageY - canvas.offsetTop)
}
ctx.update(); // Update the onscreen canvas
}, false);
// Handle entering and exiting pointer lock; pointer lock status is yoked to full screen status; both are entered and exited at the same time
document.addEventListener('pointerlockchange', function(e) {
if (!pointerLockActive()) {
console.log('Pointer lock deactivated');
canvas.width /= CANVAS_SCALING_FACTOR;
canvas.height /= CANVAS_SCALING_FACTOR
cursorRadius /= CANVAS_SCALING_FACTOR;
} else {
console.log('Pointer lock activated')
canvas.width *= CANVAS_SCALING_FACTOR;
canvas.height *= CANVAS_SCALING_FACTOR;
cursorRadius *= CANVAS_SCALING_FACTOR;
// Set the initial mouse position to be the middle of the canvas
mouseX = screen.width/2, mouseY = screen.height/2;
}
// Update the onscreen canvas
ctx.update();
});
document.getElementById('fullscreen_button').onclick = function(e) {
// When button is clicked, enter both full screen and pointer lock
canvasContainer.requestFullscreen();
var timeout = 2000;
var interval = window.setInterval(function() {
if (document.mozFullScreenElement !== null) {
window.clearInterval(interval);
canvasContainer.requestPointerLock();
} else if (timeout <= 0) {
addErrorMessage('Unable to establish pointer lock.');
clearTimeout(interval);
} else {
timeout -= 50;
}
}, 50);
}
canvasContainer.onclick = function(e) {
console.log('canvasContainer clicked');
if (pointerLockActive())
// If pointer lock is active, then use the mouseX and mouseY positions that are manually updated by the mousemove event handler
var cursorX = mouseX, cursorY = mouseY;
else
// Otherwise use the mouse positions passed in the event object
// If not in full screen mode, the cursor position has to be scaled up, because the mouse position is relative to the onscreen canvas, but we're drawing on the offscreen canvas, which is larger by a factor of fullscreenScale
var cursorX = (e.pageX - canvas.offsetLeft)*CANVAS_SCALING_FACTOR, cursorY = (e.pageY - canvas.offsetTop)*CANVAS_SCALING_FACTOR;
// If the left mouse button is clicked (e.which===1), draw a circle of one color
// If any other mouse button is clicked, draw a circle of another color
var color = e.which === 1 ? LEFT_CLICK_COLOR : OTHER_CLICK_COLOR;
offscreenCtx.drawCircle(cursorX, cursorY, color, circleRadius);
ctx.update();
};
// Detect canvas right-click events. Prevent default behavior (e.g. context menu display) and pass on to the onclick handler to do the rest of the work
canvasContainer.oncontextmenu = function(e) {
e.preventDefault();
this.onclick(e);
}
canvas.onclick = function() {
console.log('canvas clicked');
}
document.onclick = function() {
console.log('document clicked');
}
</script>
</html>
This worked for me to handle rightClick after pointer was locked.
const onMouseDown = (evt) => {
switch (evt.which) {
case 1: return handleLeftClick();
case 3: return handleRightClick();
}
};
document.body.addEventListener('mousedown', onMouseDown, true);
Below is an interactive example attempting to implement setStops to a radial gradient. When the 'setStops' button is clicked an error message occurs: "setStops is not a function"
Am I using this correctly?
<!DOCTYPE HTML>
<html>
<head>
<title>Snap.svg setStops</title>
<script type="text/javascript" src="http://svgDiscovery.com/_SNP/snap.svg-min.js"></script>
</head>
<body>
<svg id=mySVG width=400 height=200></svg>
<br><button onClick=stopsSet()>setStops</button>
<script>
var SNPsvg = Snap("#mySVG");
var radialGradient = SNPsvg.gradient("r(.5,.5,.5,.5)#000-#f00-#fff-green");
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
//---button---
function stopsSet()
{
radialGradient.setStops("#fff-#000-#f00-#fc0");
circle.attr({fill: radialGradient});
}
</script>
</body>
</html>
Seems to be a bug in snap.svg. The code for linearGradient looks like this:
function gradientLinear(defs, x1, y1, x2, y2) {
var el = Snap._.make("linearGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
el.setStops = GsetStops;
but for radialGradients it's this:
function gradientRadial(defs, cx, cy, r, fx, fy) {
var el = Snap._.make("radialGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (cx != null) {
The code to add the setStops function is missing.
I've created a pull request in Snap.svg to fix this so hopefully one of the maintainers will merge it and include it in a subsequent release. In the meantime you could always make the same change to a local copy of Snap.
I think that's because setStops is only available for a linearGradient and not a radialGradient (not sure if that's by design or not).
If you try gradient("L(0, 0, 100, 100)#000-#f00:25-#fff") I think the error will go away. Naturally that's probably not what you want, but I'm just explaining why I think the error is there.
One thing you can always do with Snap if you get stuck, is use a bit of your own markup if it's not supported direct, and add it in. Eg Snap.parse('Some SVG Markup')
var svgMarkup = Snap.parse('<defs><radialGradient id="exampleGradient"><stop offset="10%" stop-color="gold"/><stop offset="95%" stop-color="green"/></radialGradient></defs>');
SNPsvg.append( svgMarkup );
var radialGradient = SNPsvg.select('#exampleGradient')
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
Example jsfiddle
I am trying to learn Javascript by following a guide on creating a Minesweeper game using a canvas. It's all going great apart from the fact that when I click on the options bar, it registers it as a click on the canvas.This is a screenshot of the whole game so far. I can still click on the canvas and it works as it should, but clicking on the options bar (The part with the smiley face), it also registers as a click on the canvas, and removes one of the tiles.
Here is the code that registers the mouse click:
'
if(e.offsetX) { //Get's mouse pos relative to the canvas pos
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if(e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
//mouseX = e.pageX; //Gets mouse pos relatiove to page
//mouseY = e.pageY;
//console.log("Mouse Pos on screen (x, y) = " + mouseX + "," + mouseY);//Validate that the mouse position is being recorded.
//Algorithm to tell which cube the click was on.
if (Math.floor(mouseX/settings.width) < settings.columns && Math.floor(mouseY/settings.height) < settings.rows){
clickX = Math.floor(mouseX/settings.width);
clickY = Math.floor(mouseY/settings.height);
console.log("Coords of clicked box (x,y)" + clickX + "," + clickY);
}
`
Hopefully this is enough for someone to find a problem with it, because I can't.
I think the problem is that you're capturing the click events for the whole window in one function, so whether you're clicking on the canvas or the new game div, or elsewhere in the window, you're trying to apply that click to your canvas. You'd be better advised to have a click event for the canvas itself, and another click event for the new game (which should really be a button, not a div, but I digress)
If you really want to carry on this way, you could explicitly limit your function to only care about your canvas. if you make gCanvas global (shudder)
var gCanvas = null;
function canvasLoad() {
gCanvas = document.getElementById("gameCanvas");
...
}
Then in your event function you can check the target of the event and only action on things that fired from your canvas
window.onclick = function(e){
...
//Algorithm to tell which cube the click was on.
if (
(e.target === gCanvas )
)
{
if (Math.floor(mouseX/settings.width) < settings.columns &&
Math.floor(mouseY/settings.height) < settings.rows)
{
clickX = Math.floor(mouseX/settings.width);
clickY = Math.floor(mouseY/settings.height);
console.log("Coords of clicked box (x,y)" + clickX + "," + clickY);
}
}
Note that this isn't the solution I'd go with. A tighter binding of the event to the object that fired it is much more satisfactory.
<canvas id = "gameCanvas" width="153" height="153"
style="border:1px solid #000000;" #
onclick='return myCanvasEventHandler()'></canvas>
This will explicitly limit the myCanvasEventHandler() function to fire only on events from the canvas itself.
The app works fine so far except if the line is drawn really fast and leaves the edge of the canvas, the line is then not drawn to the edge of the canvas. There is a part missing from it.
I'm trying to fix the issue with:
canvasVar.addEventListener ('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
and
function clearPathIfMouseCursorLeavesCanvasFunc(e){
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
I've tried some things like adding a settimeout(); but nothing worked so far. I don't know what causes this and I've been searching if someone else had this problem and a fix for it, but every canvas drawing app I've come across has the same issues.
It's very important that the line is drawn to the edge and that the users mouse motion is recognized, not just a line to the last coordinates where the mouse left the canvas.
It's been days now that I'm stuck with this problem. Help is really appreciated!
Whole Code:
// Varibale declaration
var canvasVar = document.getElementById('canvasHtmlElement');
var contextVar = canvasVar.getContext('2d');
var pointRadiusVar = 0.5;
var mouseButtonHeld = false;
var pointsArrPosition = 0;
//Arrays
var pointsArr = [];
// Varibale declration end
//canvas setup
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
//canvas setup end
//resize fix
window.onresize = function() {
var tempImageVar = contextVar.getImageData(0, 0, canvasVar.width, canvasVar.height);
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
contextVar.putImageData(tempImageVar, 0, 0);
}
//resize fix end
//functions
// Objects
function pointObject() {
this.x = 0;
this.y = 0;
this.fill = '#444444';
}
function addFilledCircleFunc(x, y) {
//alert('works1');
var filledCircle = new pointObject;
filledCircle.x = x;
filledCircle.y = y;
pointsArr.push(filledCircle);
contextVar.lineWidth = 10; //pointRadiusVar * 2; // Line Width
contextVar.lineTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
contextVar.stroke();
//contextVar.beginPath();
contextVar.fillRect(filledCircle.x, filledCircle.y, 1, 1);
//contextVar.arc(filledCircle.x, filledCircle.y, pointRadiusVar, 0, Math.PI * 2);
//contextVar.fill();
//contextVar.lineWidth = 0.5;
//contextVar.stroke();
//contextVar.beginPath();
pointsArrPosition++;
//contextVar.moveTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
//alert(pointsArr[0].x);
}
//Objects end
// create circle on mouse clicked point while mousebutton is held
var addPointToCanvasVar = function(e) {
if (mouseButtonHeld) {
//alert('addpointfunc');
addFilledCircleFunc(e.clientX, e.clientY);
}
};
// MAKE SURE that lines work when drawn over the edge of the canvas
function clearPathIfMouseCursorLeavesCanvasFunc(e) {
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
}
// end
// mouse Up/Down functions
var mouseDownVar = function(e) {
//alert("mouseDown");
addPointToCanvasVar(e); // add point on first click, not just when mousebutton is held
mouseButtonHeld = true;
}
var mouseUpVar = function() {
//alert("mouseUp");
mouseButtonHeld = false;
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
}
// mouse Up/Down Switch end
//functions end
//listeners
canvasVar.addEventListener('mousemove', addPointToCanvasVar);
canvasVar.addEventListener('mouseup', mouseUpVar);
canvasVar.addEventListener('mousedown', mouseDownVar);
canvasVar.addEventListener('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
//listeners end
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Confident Drawing</title>
</head>
<body style="margin: 0">
<canvas id="canvasHtmlElement" style="display: block;">
Your Browser does not support Canvas! Please update to a newer version.
</canvas>
<script src="main_0.06.js"></script>
</body>
</html>
If you don't get what I mean: Run the snippet and draw a line as fast as you can while exiting the canvas.
The reason the line ends prematurely near the edge when you quickly draw a line across the edge is because the last mousemove event fired when the mouse was still in the canvas and just short of the edge, and the very next mousemove event fired after your mouse left the canvas. To fix that problem, simply draw your line from the last recorded mouse position in the canvas to the one outside of the canvas as soon as the mouseout event fires.
You can add a new global variable mousePosition and initialize it to {x:0,y:0}. Every time mousemove fires (whenever you call addPointToCanvasVar), record the e.clientX and e.clientY to your mousePosition. Then when mouseout fires (whenever you call clearPathIfMouseCursorLeavesCanvasFunc), draw the rest of the line from mousePosition to the current e.clientX and e.clientY position. This will complete the line to the end of the canvas edge.
We are working on visualization of sorting algorithms, required to add sleep and wait logic to help visualize the selected element and the element to which it is compared. After searching li'l bit, we found a code "function sleep(milliseconds){...}" which should work as desired but has failed so far.
In function insertionSort(){...}, the current element is depicted with color red and the element to which it is compared with is depicted with color blue, once the current element is swapped with the other the color of the element is again changed to white from blue (working correctly, verified using debugger), However during execution, these color transformations were not visible (only the element in red is displayed after each iteration)
var element = function(value, color)
{
this.value = value;
this.color = color;
};
var x = [];
x[0] = new element(2, "white");
x[1] = new element(1, "white");
x[2] = new element(5, "white");
x[3] = new element(4, "white");
x[4] = new element(3, "white");
x[5] = new element(7, "white");
x[6] = new element(6, "white");
x[7] = new element(8, "white");
x[8] = new element(10, "white");
x[9] = new element(9, "white");
var i = 1;
var context;
var delayTime = 1000;
function myFunction()
{
var bar = document.getElementById("bar");
width = bar.width;
height = bar.height;
context = bar.getContext("2d");
window.setInterval(insertionSort, 3000);
}
function insertionSort()
{
if(i>=0 && i<x.length)
{
var j = i;
x[j].color = "red";
drawGraph(j);
while(j>0 && x[j-1].value > x[j].value)
{
x[j-1].color = "blue";
x[j].color = "red";
drawGraph();
//need to add delay here
sleep(delayTime);
//swap
var temp = x[j];
x[j] = x[j-1];
x[j-1] = temp;
drawGraph();
// and here...
sleep(delayTime);
x[j].color = "white";
drawGraph();
j = j-1;
}
x[j].color = "white";
i++;
}
else if(i>=x.length)
{
for(k=0;k<x.length;k++)
{
x[k].color = "white";
}
drawGraph();
i=-1;
}
}
function sleep(milliseconds)
{
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++)
{
if ((new Date().getTime() - start) > milliseconds)
{
break;
}
}
}
function drawGraph()
{
context.StrokeStyle = "black";
context.clearRect ( 0 , 0 , width, height);
for(k=0;k<x.length;k++)
{
context.fillStyle = x[k].color;
//x and y coordinate of top left corner of rectangle
context.strokeRect(400+k*20, 18, 20, x[k].value*10);
context.fillRect(400+k*20, 18, 20, x[k].value*10);
}
}
<html>
<head>
<script language="javascript" type="text/javascript" src="../p5.js"></script>
<!-- uncomment lines below to include extra p5 libraries -->
<!--<script language="javascript" src="../addons/p5.dom.js"></script>-->
<!--<script language="javascript" src="../addons/p5.sound.js"></script>-->
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<!-- this line removes any default padding and style. you might only need one of these values set. -->
<style> body {padding: 0; margin: 0;} </style>
</head>
<body>
<button onclick="myFunction()">Try it</button>
<canvas id="bar" width="1000" height="400" style="border:2px"></canvas>
</body>
</html>
The approach to used in that implementation of sleep() would be terrible in any programming language, because it consumes a lot of CPU while waiting. In JavaScript, however, it's especially bad, because a JavaScript program is required to relinquish control frequently; it is not permitted to keep computing for an extended period of time. In Chrome browser, for example, Chrome will consider the program to be unresponsive, and will suggest to the user that they kill it.
But even if that weren't the case, it won't produce the desired effect, which I assume is that some animation happens on the screen, with some delay from one step to the next. The way JavaScript works in the browser, is that any changes you make to the page get rendered when your program relinquishes control; nothing updated on-screen while any JavaScript code is running. If you call a sleep function like that one, you are not relinquishing control, you are running JavaScript the whole time, and therefore the browser will not update the screen during that time. It will only update when your entire insertionSort method returns, and the browser has that 3000ms time window (from your setInterval) to take care of its own stuff (rendering).
Unfortunately, you will have to find a way to split up that algorithm, so that each step that you want to be distinctly visible to the user happens in its own timed callback.
It will probably be something along the lines of:
function stepOne() {
do the first bit;
setTimeout(secondStep, delay)
}
secondStep() {
do some more stuff;
setTimeout(thirdStep, delay)
}
and so on. The way you control the speed of the animation is with the delay parameter from one step to the next.
It's going to be tricky, especially because you aren't just trying to animate Insertion Sort, but various algorithms. So then, do you break them all up as in: insertionSortStepOne/Two/Three, shellSortStepOne/Two/Three? that would be quite ugly.
Depending on how ambitious you are, and how much you want to get out of this assignment, you might explore this feature of ES6 (a newer version of JavaScript)
function*
What this lets you do is let your function, with all its nested loops, remain structured pretty much as it is, but you insert points where it relinquishes control. Later, it is called back to continue from the point where it left off. You would use setTimeout or setInterval to do that. I've not experimented with this myself, but it seems super-cool.