Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript jQuery Basics (2014) Creating a Simple Drawing Application Perfect

Brendan Moran
Brendan Moran
14,052 Points

Some persisting questions for the the perfectionist...

I have just redone the code for this challenge (as much as I could) from memory and the knowledge of what I've learned so far in the course.

Now, I'm only left wondering two things:

Is it really best to put the strokestyle line in the middle of the mousemove event handler, so that it is running again and again and again, hundreds of times, every time the users mouse moves? Or would it be better to define this before any drawing begins, perhaps in the mousedown event handler, so that it is only happening several times, instead of hundreds of times. Is this a best practice? Does it give a significant performance benefit (e.g. is it more scalable to a larger application or many people all using the drawing application at once all over the world)? Or is there an even better way than I've suggested?

Also, it makes sense to me that if you've moused out of the canvas and the reentered without release the mouse, you should be able to keep drawing where you reentered, without drawing a straight line from the exit point to the entrance point. The modification he teaches gets rid of the straight line, but we also can't keep drawing when we've reentered, for obvious reasons (we've triggered mouseup). I tried several ways of trying to make it so that mouse up would be triggered when the user leaves the canvas, but that mousedown would automatically be retriggered if the user reentered the canvas with the mouse already pressed down. I could not figure out any way to do this. Any ideas, anyone?

3 Answers

alastair cooper
alastair cooper
30,617 Points

add the following few lines to the end. It works now!

I have called a mouseup event on the whole page and set mouseMovedOffCanvas to false so it will not draw when it goes back on.

var $restOfPage = $("body");

$restOfPage.mouseup(function(){
    mouseMovedOffCanvas = false;
    mouseDown = false;  
});

cheers

Alastair

alastair cooper
alastair cooper
30,617 Points

Although the above fix works (mostly), it is leading to some funny behaviour in my browser when I click off the canvas and then drag the pen onto it. It is sometimes as though it is stuck in draw mode even after I release the mouse. I can't help thinking that this is because the way I have selected the whole page is a bit of a hack. What I actually want is all the elements except the canvas. I have tried every conceivable combination of ways with the not() method, but I can't find a way. Let me know if you come up with a 'cleaner' solution.

Cheers

Alastair

alastair cooper
alastair cooper
30,617 Points

I have taken on your challenge, and come up with this:

//Problem: No user interaction causes no change to application
//Solution: When user interacts cause changes appropriately
var color = $(".selected").css("background-color");
var $canvas = $("canvas");
var context = $canvas[0].getContext("2d");
var lastEvent;
var mouseDown = false;
var mouseMovedOffCanvas = false;

//When clicking on control list items
$(".controls").on("click", "li", function(){
  //Deselect sibling elements
  $(this).siblings().removeClass("selected");
  //Select clicked element
  $(this).addClass("selected");
  //cache current color
  color = $(this).css("background-color");
});

//When "New Color" is pressed
$("#revealColorSelect").click(function(){
  //Show color select or hide the color select
  changeColor();
  $("#colorSelect").toggle();
});

//update the new color span
function changeColor() {
  var r = $("#red").val();
  var g = $("#green").val();
  var b = $("#blue").val();
  $("#newColor").css("background-color", "rgb(" + r + "," + g +", " + b + ")");
}

//When color sliders change
$("input[type=range]").change(changeColor);

//When "Add Color" is pressed
$("#addNewColor").click(function(){
  //Append the color to the controls ul
  var $newColor = $("<li></li>");
  $newColor.css("background-color", $("#newColor").css("background-color"));
  $(".controls ul").append($newColor);
  //Select the new color
  $newColor.click();
});

//On mouse events on the canvas
$canvas.mousedown(function(e){
  lastEvent = e;
  mouseDown = true;
}).mousemove(function(e){
  //Draw lines
  if(mouseDown) {
    context.beginPath();
    context.moveTo(lastEvent.offsetX, lastEvent.offsetY);
    context.lineTo(e.offsetX, e.offsetY);
    context.strokeStyle = color;
    context.stroke();
    lastEvent = e;
  }
}).mouseup(function(){
  mouseDown = false;
}).mouseleave(function(){
  if (mouseDown === true){
      mouseMovedOffCanvas = true;
  } else {
      mouseMovedOffCanvas = false;  
  }
  mouseDown = false;
}).mouseenter(function(e){
  lastEvent = e;
  if (mouseMovedOffCanvas === true){
      mouseDown = true;
      $canvas.mousedown(e);  
  }
});

I added a variable to track if the mouse was pressed when it left the canvas, and then checked that variable in a mouseenter event. If it is true, then reset the lastEvent and reapply the mouseDown variable.

Thanks for the challenge. I had just finished this course myself and was looking for a way to test my understanding of it.

As to your first question, I have no idea!

cheers

Alastair

Brendan Moran
Brendan Moran
14,052 Points

Several of the things I tried accomplished the same thing that you've got going on here, but here's the problem with this code (as well as the solutions I created): if you "mouseup" (release your hold on the mouse button) while off the canvas and reenter the canvas, you are going to keep drawing anyway! You are stuck in draw mode until you click again on the canvas. :-O

That is the exact bit that I have not been able to figure out.

Jachym Metlicka
Jachym Metlicka
3,937 Points

Just for the sake of future Treehouse travellers. Here is how I solved the issue with "mouseup" while off the canvas and reentering it.

// No user interaction causes no change to app
//When user interacts, cause changes properly.
var color = $(".selected").css("background-color");
var $canvas = $("canvas");
var context = $canvas[0].getContext("2d");
var mousedown = false;

//When clicking on control list items 
$(".controls").on("click", "li",function(){
  //deselect sibling elements
  $(this).siblings("li").removeClass("selected");
  //select clicked element
  $(this).addClass("selected");
  //set color to color variable
  color = $(this).css("background-color");
});

//When "revealColorSelect" is clicked
$("#revealColorSelect").click(function(){
  changeColor();  
  $("#colorSelect").toggle();   

});

function changeColor() {
var r= $("#red").val();
var g= $("#green").val();
var b= $("#blue").val();

$("#newColor").css("background-color", "rgb(" + r + ", " + g  + ", " + b + ")");
}
//When color sliders change
$("input[type=range]").change(changeColor);

  //Update the new color span

//When add color is pressed
$("#addNewColor").click(function(){
  var $newColor = $("<li></li>");
  $newColor.css("background-color", $("#newColor").css("background-color")); 
    //append the color to the controls ul
  //select the new color
  $(".controls ul").append($newColor);
  $newColor.click();
});




//On mouse events on the canvas
$canvas.mousedown(function(event){
  lastEvent = event;
  mousedown = true;
//Use additional variable to check for mouse leaving the canvas
  mouseout = false;
}).mousemove(function(event){
//if statement checks for both whether mousedown is true  and whether mouseout is false
  if(mousedown && !mouseout){
context.beginPath();
context.moveTo(lastEvent.offsetX, lastEvent.offsetY);
context.lineTo(event.offsetX, event.offsetY);
context.strokeStyle = color;
context.stroke();
  }
  lastEvent = event;
}).mouseup(function() {
  mousedown = false;  
}).mouseleave(function(){
//If mouse leaves, switch mouseout to true
  mouseout = true;
}).mouseenter(function(event){
//If mouse enters, first update the lastEvent to current event
  lastEvent = event;
//and then set mouseout to false.
  mouseout = false;
  });
//if mouseup occurs anywhere within the document, stop drawing.
$(window).mouseup(function(){
     mousedown = false;
});