Javascript Course

With the JavaScript object presented in this page you can draw arrow markers between two clicks coordinates inside a canvas element.
The script contains three optional buttons, to Enables /Disables the drawing action, a button to delete the arrow markers, and another button to get an image file of the canvas.

Code of the drawArrowCnv object

- Click on the code to select it.
 <button id="btn_drawar">Enable Drawing</button> 
 <button id="btn_delar">Delete Arrows</button>
 <a id="btn_getimg">Get Image</a>
<script>
//REPLACE 'cnv1' WITH THE ID OF CANVAS WHERE TO DRAW ARROWS
var cnv_id = 'cnv1';
 
//Draw arrows in the canvas with id from #cnv_id, between two clicks
function drawArrowCnv(cnv_id){
 //From: https://coursesweb.net/javascript/
  //style: lw=line-width, lc=line-color, aw=arrow-width, al=arrow-length, ac=arrow-color
  var style = {lw:1, lc:'red', aw:12, al:18, ac:'red'};
  var allow_draw =-1;  //if 1 allow to draw the arrow

  var cnv = document.getElementById(cnv_id);  //canvas elm.
  var cnv0 = {};  //cloned canvas to store initial canvas image /content
  var ctx = cnv.getContext('2d');  //canvas context
  var x, y = 0;  // variables that will contain the coordinates
  var clicks =0;  //for pair of clicks on canvas; 0 /1
  var last_clk = [0, 0];  //x,y coords of last click
  var nra =0;  //number of line-markers

  //set value of allow_draw
  this.allowDraw = function(){ allow_draw *=-1; }

  //return value of allow_draw
  this.letDraw = function(){ return allow_draw; }

  //to clone /copy canvas image /content
  function cloneCanvas(cnv){
    cnv.insertAdjacentHTML('afterend', '<canvas id="cnv0_cnv" style="'+ cnv.getAttribute('style') +'" width="'+ cnv.width +'" height="'+ cnv.height +'"></canvas>');
    //create a new canvas
    var cnv0 = document.getElementById('cnv0_cnv');
    var cnt = cnv0.getContext('2d');

    //set dimensions, and hide it
    cnv0.width = cnv.width;
    cnv0.height = cnv.height;
    cnv0.style.display ='none';

    //apply the old canvas to the new one; return the new canvas
    cnt.drawImage(cnv, 0, 0);
    return cnv0;
  }

  //to get the initial canvas
  this.restoreCanvas = function(){
    cnv0.style.display ='block';  //show cloned canvas
    cnv.outerHTML ='';  //delete original canvas
    cnv0.id = cnv_id;  //set original id to cloned canvas
    drawAr = new drawArrowCnv(cnv_id);  //recreate the object to draw line-marker
    if(drawAr.letDraw() ==-1 && allow_draw ==1) drawAr.allowDraw();  //make allow_draw 1 if initialy is -1 and currently 1
  }

  // Get X and Y position of the elm
  function getXYpos(elm){
    x = elm.offsetLeft;  //set x to elm’s offsetLeft
    y = elm.offsetTop;  //set y to elm’s offsetTop

    elm = elm.offsetParent;  // set elm to its offsetParent

    //use while loop to check if elm is null, if not. add current elm’s offset to x/y
    //offsetTop to y and set elm to its offsetParent
    while(elm != null) {
      x = parseInt(x) + parseInt(elm.offsetLeft);
      y = parseInt(y) + parseInt(elm.offsetTop);
      elm = elm.offsetParent;
    }

    // returns an object with 'x' (Left), 'y' (Top) position
    return {'x':x, 'y':y};
  }

  //return array with click coords [x,y] in element $e
  function getCoords(e){
    var xy_pos = getXYpos(e.target);

    // if IE
    if(navigator.appVersion.indexOf('MSIE') !=-1) {
      //in some IE scrolling page affects mouse coordinates into an element
      //This gets the page element that will be used to add scrolling value to correct mouse coords
      var standardBody = (document.compatMode =='CSS1Compat') ? document.documentElement : document.body;

      x = event.clientX + standardBody.scrollLeft;
      y = event.clientY + standardBody.scrollTop;
    }
    else {
      x = e.pageX;
      y = e.pageY;
    }
    return [x - xy_pos['x'], y - xy_pos['y']];
  }

  //class to draw line with arrow head
  function Line(x1,y1,x2,y2){
    this.x1=x1;
    this.y1=y1;
    this.x2=x2;
    this.y2=y2;
  }
  //set arrow and add it with drawArrowhead, to the end of the line (x2/y2)
  Line.prototype.addArrowhead = function(ctx){
    // arbitrary styling
    ctx.strokeStyle = style.lc;
    ctx.fillStyle = style.ac;
    ctx.lineWidth = style.lw;

    // draw the line
    ctx.beginPath();
    ctx.moveTo(this.x1,this.y1);
    ctx.lineTo(this.x2,this.y2);
    ctx.stroke();

    // draw the ending arrowhead
    var endRadians = Math.atan((this.y2-this.y1)/(this.x2-this.x1));
    endRadians += ((this.x2>=this.x1)?90:-90)*Math.PI/180;
    this.drawArrowhead(ctx,this.x2,this.y2,endRadians);
  }
  //draw arrow to $x/$y coords
  Line.prototype.drawArrowhead = function(ctx,x,y,radians){
    ctx.save();
    ctx.beginPath();
    ctx.translate(x,y);
    ctx.rotate(radians);
    ctx.moveTo(0,0);
    ctx.lineTo(style.aw /2,style.al);
    ctx.lineTo(-style.aw /2,style.al);
    ctx.closePath();
    ctx.restore();
    ctx.fill();
  }

  //set object to draw the line-marker
  function drawArrow(e){
    //if $draw is 1, get x,y coords in element $e and draw arrow
    if(allow_draw ==1){
      var coords = getCoords(e);
      nra++;
      if(nra ==1) cnv0 = cloneCanvas(cnv);  //clone canvas when 1st arrow

      if (clicks != 1) clicks++;
      else {
        // create a new line object
        var line = new Line(last_clk[0],last_clk[1],coords[0],coords[1]);
        line.addArrowhead(ctx);  // draw the line and arrow

        clicks = 0;  //reset nr. clicks
      }

      last_clk = [coords[0],coords[1]];  //store last click coords
    }
  };

  //register click event to draw line-marker in canvas
  cnv.addEventListener('click', drawArrow, false);
}

// set object of drawArrowCnv class
var drawAr =0;
if(document.getElementById(cnv_id)) drawAr = new drawArrowCnv(cnv_id);

//register click on #btn_drawar to enable /disable drawing action
var btn_drawar = document.getElementById('btn_drawar');
if(btn_drawar) btn_drawar.addEventListener('click', function(e){
  if(drawAr ==0 && document.getElementById(cnv_id)) drawAr = new drawArrowCnv(cnv_id);
  drawAr.allowDraw();
  e.target.style.background = (drawAr.letDraw() ==1) ? '#f00' :'#dadafb';
  e.target.innerHTML = (drawAr.letDraw() ==1) ? 'Disable Drawing' :'Enable Drawing';
});

//register click on #btn_delar to delete arrows
var btn_delar = document.getElementById('btn_delar');
if(btn_delar) btn_delar.addEventListener('click', function(e){
  drawAr.restoreCanvas();
});

//register click on #btn_getimg to get canvas image
var btn_getimg = document.getElementById('btn_getimg');
btn_getimg.addEventListener('click', function() {
  this.href = document.getElementById(cnv_id).toDataURL();  //set link to canvas data
  this.download ='canvas_'+ cnv_id +'.jpg';  //return for download with an image name
});
</script>

Usage

- Copy the code of the script above into your page (after the Canvas elements).
- To the cnv_id variable, replace "cnv1" with the ID of the Canvas where you want to draw arrows.
- To change the style (color, width) of the arrow marker, modify the values in the style variable in drawArrowCnv().
- To change the name of the canvas image, modify this line of code in script:this.download ='canvas_'+ cnv_id +'.jpg';
- The "Enable Drawing" button is Optional, if yon not want to use it, set the value 1 to the allow_draw variable in drawArrowCnv().

Example

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Draw arrow markers between clicks coords in canvas</title>
<style>
#cnv1 {
position:relative;
display:block;
margin:8px auto;
background:#fefefe;
border:2px dashed #111;
}
#btn_drawar {
display:block;
margin:8px auto;
background:#dadafb;
}
#btn_getimg {
margin:0 22px;
font-weight:700;
text-decoration:underline;
cursor:pointer;
}
</style>
</head>
<body>
<canvas id="cnv1" width="400" height="280"></canvas>
- Click on the "Enable Drawing" button, to Enable the action of drawing arrows, then click inside the canvas above to draw arrows between clicks coords.<br>
If you click again on the button, the drawing action is disabled.
<button id="btn_drawar">Enable Drawing</button> 
<button id="btn_delar">Delete Arrows</button>
<a id="btn_getimg">Get Image</a>
<script>
//REPLACE 'cnv1' WITH THE ID OF CANVAS WHERE TO DRAW ARROWS
var cnv_id = 'cnv1';

//Only to draw some context in canvas
function drawInCnv(){
  var cnv = document.getElementById(cnv_id);  //canvas elm.
  var ctx = cnv.getContext('2d');  //canvas context
  ctx.font = 'bold 28px sans-serif';
  ctx.strokeText('CANVAS - Draw Arrows', 60, 50);  //Text
  ctx.strokeStyle = '#00f';;
  ctx.lineWidth =2;
  ctx.arc(200,135,50,0,Math.PI*2,true);  //Face
  ctx.moveTo(235,135);
  ctx.arc(200,135,35,0,Math.PI,false);  //Mouth
  ctx.moveTo(190,125);
  ctx.arc(185,125,4,0,Math.PI*2,true);  //Left eye
  ctx.moveTo(220,125);
  ctx.arc(215,125,4,0,Math.PI*2,true);  //Right eye
  ctx.stroke();
}
drawInCnv();

//Draw arrows in the canvas with id from #cnv_id, between two clicks
function drawArrowCnv(cnv_id){
 //From: https://coursesweb.net/javascript/
  //style: lw=line-width, lc=line-color, aw=arrow-width, al=arrow-length, ac=arrow-color
  var style = {lw:1, lc:'red', aw:12, al:18, ac:'red'};
  var allow_draw =-1;  //if 1 allow to draw the arrow

  var cnv = document.getElementById(cnv_id);  //canvas elm.
  var cnv0 = {};  //cloned canvas to store initial canvas image /content
  var ctx = cnv.getContext('2d');  //canvas context
  var x, y = 0;  // variables that will contain the coordinates
  var clicks =0;  //for pair of clicks on canvas; 0 /1
  var last_clk = [0, 0];  //x,y coords of last click
  var nra =0;  //number of line-markers

  //set value of allow_draw
  this.allowDraw = function(){ allow_draw *=-1; }

  //return value of allow_draw
  this.letDraw = function(){ return allow_draw; }

  //to clone /copy canvas image /content
  function cloneCanvas(cnv){
    cnv.insertAdjacentHTML('afterend', '<canvas id="cnv0_cnv" style="'+ cnv.getAttribute('style') +'" width="'+ cnv.width +'" height="'+ cnv.height +'"></canvas>');
    //create a new canvas
    var cnv0 = document.getElementById('cnv0_cnv');
    var cnt = cnv0.getContext('2d');

    //set dimensions, and hide it
    cnv0.width = cnv.width;
    cnv0.height = cnv.height;
    cnv0.style.display ='none';

    //apply the old canvas to the new one; return the new canvas
    cnt.drawImage(cnv, 0, 0);
    return cnv0;
  }

  //to get the initial canvas
  this.restoreCanvas = function(){
    cnv0.style.display ='block';  //show cloned canvas
    cnv.outerHTML ='';  //delete original canvas
    cnv0.id = cnv_id;  //set original id to cloned canvas
    drawAr = new drawArrowCnv(cnv_id);  //recreate the object to draw line-marker
    if(drawAr.letDraw() ==-1 && allow_draw ==1) drawAr.allowDraw();  //make allow_draw 1 if initialy is -1 and currently 1
  }

  // Get X and Y position of the elm
  function getXYpos(elm){
    x = elm.offsetLeft;  //set x to elm’s offsetLeft
    y = elm.offsetTop;  //set y to elm’s offsetTop

    elm = elm.offsetParent;  // set elm to its offsetParent

    //use while loop to check if elm is null, if not. add current elm’s offset to x/y
    //offsetTop to y and set elm to its offsetParent
    while(elm != null) {
      x = parseInt(x) + parseInt(elm.offsetLeft);
      y = parseInt(y) + parseInt(elm.offsetTop);
      elm = elm.offsetParent;
    }

    // returns an object with 'x' (Left), 'y' (Top) position
    return {'x':x, 'y':y};
  }

  //return array with click coords [x,y] in element $e
  function getCoords(e){
    var xy_pos = getXYpos(e.target);

    // if IE
    if(navigator.appVersion.indexOf('MSIE') !=-1) {
      //in some IE scrolling page affects mouse coordinates into an element
      //This gets the page element that will be used to add scrolling value to correct mouse coords
      var standardBody = (document.compatMode =='CSS1Compat') ? document.documentElement : document.body;

      x = event.clientX + standardBody.scrollLeft;
      y = event.clientY + standardBody.scrollTop;
    }
    else {
      x = e.pageX;
      y = e.pageY;
    }
    return [x - xy_pos['x'], y - xy_pos['y']];
  }

  //class to draw line with arrow head
  function Line(x1,y1,x2,y2){
    this.x1=x1;
    this.y1=y1;
    this.x2=x2;
    this.y2=y2;
  }
  //set arrow and add it with drawArrowhead, to the end of the line (x2/y2)
  Line.prototype.addArrowhead = function(ctx){
    // arbitrary styling
    ctx.strokeStyle = style.lc;
    ctx.fillStyle = style.ac;
    ctx.lineWidth = style.lw;

    // draw the line
    ctx.beginPath();
    ctx.moveTo(this.x1,this.y1);
    ctx.lineTo(this.x2,this.y2);
    ctx.stroke();

    // draw the ending arrowhead
    var endRadians = Math.atan((this.y2-this.y1)/(this.x2-this.x1));
    endRadians += ((this.x2>=this.x1)?90:-90)*Math.PI/180;
    this.drawArrowhead(ctx,this.x2,this.y2,endRadians);
  }
  //draw arrow to $x/$y coords
  Line.prototype.drawArrowhead = function(ctx,x,y,radians){
    ctx.save();
    ctx.beginPath();
    ctx.translate(x,y);
    ctx.rotate(radians);
    ctx.moveTo(0,0);
    ctx.lineTo(style.aw /2,style.al);
    ctx.lineTo(-style.aw /2,style.al);
    ctx.closePath();
    ctx.restore();
    ctx.fill();
  }

  //set object to draw the line-marker
  function drawArrow(e){
    //if $draw is 1, get x,y coords in element $e and draw arrow
    if(allow_draw ==1){
      var coords = getCoords(e);
      nra++;
      if(nra ==1) cnv0 = cloneCanvas(cnv);  //clone canvas when 1st arrow

      if (clicks != 1) clicks++;
      else {
        // create a new line object
        var line = new Line(last_clk[0],last_clk[1],coords[0],coords[1]);
        line.addArrowhead(ctx);  // draw the line and arrow

        clicks = 0;  //reset nr. clicks
      }

      last_clk = [coords[0],coords[1]];  //store last click coords
    }
  };

  //register click event to draw line-marker in canvas
  cnv.addEventListener('click', drawArrow, false);
}

// set object of drawArrowCnv class
var drawAr =0;
if(document.getElementById(cnv_id)) drawAr = new drawArrowCnv(cnv_id);

//register click on #btn_drawar to enable /disable drawing action
var btn_drawar = document.getElementById('btn_drawar');
if(btn_drawar) btn_drawar.addEventListener('click', function(e){
  if(drawAr ==0 && document.getElementById(cnv_id)) drawAr = new drawArrowCnv(cnv_id);
  drawAr.allowDraw();
  e.target.style.background = (drawAr.letDraw() ==1) ? '#f00' :'#dadafb';
  e.target.innerHTML = (drawAr.letDraw() ==1) ? 'Disable Drawing' :'Enable Drawing';
});

//register click on #btn_delar to delete arrows
var btn_delar = document.getElementById('btn_delar');
if(btn_delar) btn_delar.addEventListener('click', function(e){
  drawAr.restoreCanvas();
});

//register click on #btn_getimg to get canvas image
var btn_getimg = document.getElementById('btn_getimg');
btn_getimg.addEventListener('click', function() {
  this.href = document.getElementById(cnv_id).toDataURL();  //set link to canvas data
  this.download ='canvas_'+ cnv_id +'.jpg';  //return for download with an image name
});
</script>
</body>
</html>
- Demo:
You can draw arrow markers inside this canvas when the button "Enable Drawing" is pressed (has red color). - Click on the "Enable Drawing" button, to Enable the action of drawing arrows, then click inside the canvas above to draw arrows between clicks coords.
If you click again on the button, the drawing action is disabled. Get Image

Daily Test with Code Example

HTML
CSS
JavaScript
PHP-MySQL
Which tag is used to add lists into <ul> and <ol> elements?
<dt> <dd> <li>
<ul>
 <li>http://coursesweb.net/html/</li>
 <li>http://coursesweb.net/css/</li>
</ul>
Which value of the "display" property creates a block box for the content and ads a bullet marker?
block list-item inline-block
.some_class {
  display: list-item;
}
Which instruction converts a JavaScript object into a JSON string.
JSON.parse() JSON.stringify eval()
var obj = {
 "courses": ["php", "javascript", "ajax"]
};
var jsonstr = JSON.stringify(obj);
alert(jsonstr);    // {"courses":["php","javascript","ajax"]}
Indicate the PHP class used to work with HTML and XML content in PHP.
stdClass PDO DOMDocument
$strhtml = '<body><div id="dv1">CoursesWeb.net</div></body>';
$dochtml = new DOMDocument();
$dochtml->loadHTML($strhtml);
$elm = $dochtml->getElementById("dv1");
echo $elm->nodeValue;    // CoursesWeb.net
Draw arrow markers between clicks coords in Canvas

Last accessed pages

  1. Get and Modify content of an Iframe (32346)
  2. Making DIV Contents Scroll Horizontally, with multiple Div`s inside (59589)
  3. querySelector and querySelectorAll (30124)
  4. sPBM - Simple PHP Backup Manager (3402)
  5. Vue JS - Transition and Animation (490)

Popular pages this month

  1. Courses Web: PHP-MySQL JavaScript Node.js Ajax HTML CSS (473)
  2. CSS cursor property - Custom Cursors (79)
  3. The Mastery of Love (70)
  4. PHP-MySQL free course, online tutorials PHP MySQL code (62)
  5. CSS3 2D transforms (46)