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 definition lists into a <dl> element?
<dt> <dd> <li>
<dl>
 <dt>HTML</dt>
  <dd> - Hyper Text Markup Language</dd>
  <dd> - Language for web pages</dd>
</dl>
Which CSS property can hide an element on page, letting an empty space in its place?
display position visibility
#id {
  visibility: hidden;
}
Click on the event which is triggered when the mouse clicks on an object.
onclick onmouseover onfocus
document.getElementById("id").onclick = function(){
  alert("http://CoursesWeb.net/");
}
Indicate the PHP variable that contains the contents of both $_GET, $_POST, and $_COOKIE arrays.
$_SESSION $_GET $_REQUEST
if(isset($_REQUEST["id"])) {
  echo $_REQUEST["id"];
}
Draw arrow markers between clicks coords in Canvas

Last accessed pages

  1. Sending data with GET and POST in the same request (3603)
  2. Node.js Move and Copy file (3518)
  3. Get the value of the selected /checked checkboxes in a form (37630)
  4. Read Excel file data in PHP - PhpExcelReader (56711)
  5. Get and Modify content of an Iframe (12777)

Popular pages this month

  1. Read Excel file data in PHP - PhpExcelReader (448)
  2. PhpSpreadsheet - Read, Write Excel and LibreOffice Calc files (352)
  3. Register and show online users and visitors (321)
  4. Courses Web: PHP-MySQL JavaScript Ajax HTML CSS Flash-AS3 (302)
  5. Insert, Select and Update NULL value in MySQL (214)