3

I am a new learner of canvas in HTML5, and currently trying to make a simple draw line project for kids. I inserted background images into canvas, and try to come up with a freehand line drawing method but up to no result. Can someone help me fix my below code? Thank you all before.

<!DOCTYPE HTML>
<html>
    <script type="text/javascript">
    var canvas, ctx, flag = false,
        prevX = 0,
        currX = 0,
        prevY = 0,
        currY = 0,
        dot_flag = false;

    var x = "black",
        y = 2;

    function init() {
        canvas = document.getElementById('can');
        ctx = canvas.getContext("2d");
        w = canvas.width;
        h = canvas.height;

        canvas.addEventListener("mousemove", function (e) {
            findxy('move', e)
        }, false);
        canvas.addEventListener("mousedown", function (e) {
            findxy('down', e)
        }, false);
        canvas.addEventListener("mouseup", function (e) {
            findxy('up', e)
        }, false);
        canvas.addEventListener("mouseout", function (e) {
            findxy('out', e)
        }, false);
    }

    function color(obj) {
        switch (obj.id) {
            case "green":
                x = "green";
                break;
            case "blue":
                x = "blue";
                break;
            case "red":
                x = "red";
                break;
            case "yellow":
                x = "yellow";
                break;
            case "orange":
                x = "orange";
                break;
            case "black":
                x = "black";
                break;
            case "white":
                x = "white";
                break;
        }
        if (x == "white") y = 14;
        else y = 2;

    }

    function draw() {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(currX, currY);
        ctx.strokeStyle = x;
        ctx.lineWidth = y;
        ctx.stroke();
        ctx.closePath();
    }

    function erase() {
        var m = confirm("Want to clear");
        if (m) {
            ctx.clearRect(0, 0, w, h);
            document.getElementById("canvasimg").style.display = "none";
        }
    }

    function save() {
        document.getElementById("canvasimg").style.border = "2px solid";
        var dataURL = canvas.toDataURL();
        document.getElementById("canvasimg").src = dataURL;
        document.getElementById("canvasimg").style.display = "inline";
    }

    function findxy(res, e) {
        if (res == 'down') {
            prevX = currX;
            prevY = currY;
            currX = e.clientX - canvas.offsetLeft;
            currY = e.clientY - canvas.offsetTop;

            flag = true;
            dot_flag = true;
            if (dot_flag) {
                ctx.beginPath();
                ctx.fillStyle = x;
                ctx.fillRect(currX, currY, 2, 2);
                ctx.closePath();
                dot_flag = false;
            }
        }
        if (res == 'up' || res == "out") {
            flag = false;
        }
        if (res == 'move') {
            if (flag) {
                prevX = currX;
                prevY = currY;
                currX = e.clientX - canvas.offsetLeft;
                currY = e.clientY - canvas.offsetTop;
                draw();
            }
        }
    }
    </script>
    <body onload="init()" style="background-image: src=c:/WebProgram/Pictures/test1.png;">
        <canvas id="can" width="520" height="700" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
        <div style="position:absolute;top:12%;left:43%;">Choose Color</div>
        <div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" id="green" onclick="color(this)"></div>
        <div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" id="blue" onclick="color(this)"></div>
        <div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" id="red" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" id="yellow" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" id="orange" onclick="color(this)"></div>
        <div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" id="black" onclick="color(this)"></div>
        <div style="position:absolute;top:20%;left:43%;">Eraser</div>
        <div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" id="white" onclick="color(this)"></div>
        <img id="canvasimg" style="position:absolute;top:10%;left:52%;" style="display:none;">
        <input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:5%;left:10%;">
        <input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:5%;left:15%;">
    </body>
</html>
RealSkeptic
  • 33,993
  • 7
  • 53
  • 79

2 Answers2

10

Creating a canvas based drawing app.

There is not that much to a drawing app. Listen to the mouse, when the button is down draw at the mouse position.

If you want to have a responsive canvas and also include undos and more then you need to start at a slightly more complex level.

Drawing and display.

First you should separate the drawing from the display. This is done by creating an offscreen canvas that holds the drawing. Its size is constant and can be panned and zoomed (even rotated) by the user.

Having an offscreen canvas to hold the drawing also lets you draw over the drawing if you are creating lines or boxes.

Some functions to aid in creating a canvas

function createCanvas(width, height) {
  const c = document.createElement("canvas");
  c.width = width;
  c.height = height;
  c.ctx = c.getContext("2d");
  return c;
}
const drawing = createCanvas(512,512);

You can draw that canvas to the display canvas with

ctx.drawImage(drawing,0,0);

The snippet draws the drawing in the center of the canvas with a shadow and border to look nice.

The mouse handler

It is important that you do the mouse interface correctly. Listening to the mouse events from the canvas has some problems. When the user drags off of the canvas you no longer get any mouse events. This means that when the mouse leaves the canvas you need to stop drawing as you don't know if the mouse is still down when it returns.

To solve this problem you listen to the document's mouse events. This will capture the mouse when a button is down, allowing the user to move the mouse anywhere on the screen while you still get the events. If the mouse goes up while off the canvas you still get that event.

NOTE the stack overflow snippet window prevents mouse capture ATM (a recent change) so the above mouse behaviour is restricted to the iFrame containing the snippet.

You should also never do any rendering from mouse events. Some rendering operations can be slow, much slower than the update speed of the mouse. If you render from the mouse events you will lose mouse events. Always do the minimum possible code inside mouse event listeners.

In the snippet the mouse event only records the current mouse state and if the mouse is down and drawing it will record the path the mouse creates. A separate loop that is synced to the display refresh rate via the function call requestAnimationFrame is responsible for rendering content. It runs at about 60fps. To stop it from drawing when nothing is happening a flag is used to indicate that the display needs updating updateDisplay When there are changes you set that to true updateDisplay=true; and the next time the display hardware is ready to display a frame it will draw all the updated content.

Drawing a line

A line is just a set of connected points. In the snippet I create a line object. It holds the points that make up the line and the line width and color.

When the mouse is down I create a new line object and start adding points to it. I flag that the display needs updating and in the display loop I draw the line on the display canvas via its draw method.

When the mouse moves up, then I draw the line on the drawing canvas. Doing it this way lets you apply some smarts to the line (snippet is simple and does nothing to the line), for example making it fade out along its length. You will only know its length when the mouse is up.

In the snippet I discard the line when the mouse is up, but if you wanted to have undos you would save each line drawn in an array. To undo you just clear the drawing and redraw all the lines except for the undone line.

The line object and associated code.

// a point object creates point from x,y coords or object that has x,y
const point = (x, y = x.y + ((x = x.x) * 0)) => ({ x, y });

// function to add a point to the line
function addPoint(x, y) { this.points.push(point(x, y)); }

// draw a line on context ctx and adds offset.x, offset.y
function drawLine(ctx, offset) { 
  ctx.strokeStyle = this.color;
  ctx.lineWidth = this.width;
  ctx.lineJoin = "round";
  ctx.lineCap = "round";
  ctx.beginPath();
  var i = 0;
  while (i < this.points.length) {
    const p = this.points[i++];
    ctx.lineTo(p.x + offset.x, p.y + offset.y);
  }
  ctx.stroke();
}
// creates a new line object
function createLine(color, width) {
  return {
    points: [], // the points making up the line
    color,      // colour of the line
    width,      // width of the line
    add: addPoint,  // function to add a point
    draw: drawLine,  // function to draw the whole line
  };
}

Example snippet

The snippet turned out a little longer than I wanted. I have added comments for relevant code but if you have any question please do ask in the comments and I will update the answer with more information.

// size of drawing and its starting background colour
const drawingInfo = {
  width: 384 ,
  height: 160,
  bgColor: "white",
}
const brushSizes = [1, 2, 3, 4, 5, 6, 7, 8];
const colors = "red,orange,yellow,green,cyan,blue,purple,white,gray,black".split(",");
var currentColor = "blue";
var currentWidth = 2;
var currentSelectBrush;
var currentSelectColor;
const colorSel = document.getElementById("colorSel");
colors.forEach((color, i) => {
  var swatch = document.createElement("span");
  swatch.className = "swatch";
  swatch.style.backgroundColor = color;
  if (currentColor === color) {
    swatch.className = "swatch highlight";
    currentSelectColor = swatch;
  } else {
    swatch.className = "swatch";
  }
  swatch.addEventListener("click", (e) => {
    currentSelectColor.className = "swatch";
    currentColor = e.target.style.backgroundColor;
    currentSelectColor = e.target;
    currentSelectColor.className = "swatch highlight";
  });
  colorSel.appendChild(swatch);
})
brushSizes.forEach((brushSize, i) => {
  var brush = document.createElement("canvas");
  brush.width = 16;
  brush.height = 16;
  brush.ctx = brush.getContext("2d");
  brush.ctx.beginPath();
  brush.ctx.arc(8, 8, brushSize / 2, 0, Math.PI * 2);
  brush.ctx.fill();
  brush.brushSize = brushSize;
  if (currentWidth === brushSize) {
    brush.className = "swatch highlight";
    currentSelectBrush = brush;
  } else {
    brush.className = "swatch";
  }

  brush.addEventListener("click", (e) => {
    currentSelectBrush.className = "swatch";
    currentSelectBrush = e.target;
    currentSelectBrush.className = "swatch highlight";
    currentWidth = e.target.brushSize;

  });
  colorSel.appendChild(brush);
})


const canvas = document.getElementById("can");
const mouse = createMouse().start(canvas, true);
const ctx = canvas.getContext("2d");
var updateDisplay = true; // when true the display needs updating
var ch, cw, w, h; // global canvas size vars


var currentLine;

var displayOffset = {
  x: 0,
  y: 0
};

// a point object creates point from x,y coords or object that has x,y
const point = (x, y = x.y + ((x = x.x) * 0)) => ({
  x,
  y
});
// function to add a point to the line
function addPoint(x, y) {
  this.points.push(point(x, y));
}

function drawLine(ctx, offset) { // draws a line
  ctx.strokeStyle = this.color;
  ctx.lineWidth = this.width;
  ctx.lineJoin = "round";
  ctx.lineCap = "round";
  ctx.beginPath();
  var i = 0;
  while (i < this.points.length) {
    const p = this.points[i++];
    ctx.lineTo(p.x + offset.x, p.y + offset.y);
  }
  ctx.stroke();
}

function createLine(color, width) {
  return {
    points: [],
    color,
    width,
    add: addPoint,
    draw: drawLine,
  };
}


// creates a canvas
function createCanvas(width, height) {
  const c = document.createElement("canvas");
  c.width = width;
  c.height = height;
  c.ctx = c.getContext("2d");
  return c;
}
// resize main display canvas and set global size vars
function resizeCanvas() {
  ch = ((h = canvas.height = innerHeight - 32) / 2) | 0;
  cw = ((w = canvas.width = innerWidth) / 2) | 0;
  updateDisplay = true;
}

function createMouse() {
  function preventDefault(e) { e.preventDefault() }
  const mouse = {
    x: 0,
    y: 0,
    buttonRaw: 0,
    prevButton: 0
  };
  const bm = [1, 2, 4, 6, 5, 3]; // bit masks for mouse buttons
  const mouseEvents = "mousemove,mousedown,mouseup".split(",");
  const m = mouse;
  // one mouse handler
  function mouseMove(e) {
    m.bounds = m.element.getBoundingClientRect();
    m.x = e.pageX - m.bounds.left - scrollX;
    m.y = e.pageY - m.bounds.top - scrollY;
    
    if (e.type === "mousedown") {
      m.buttonRaw |= bm[e.which - 1];
    } else if (e.type === "mouseup") {
      m.buttonRaw &= bm[e.which + 2];
    }
    // check if there should be a display update
    if (m.buttonRaw || m.buttonRaw !== m.prevButton) {
      updateDisplay = true;
    }
    // if the mouse is down and the prev mouse is up then start a new line
    if (m.buttonRaw !== 0 && m.prevButton === 0) { // starting new line
      currentLine = createLine(currentColor, currentWidth);
      currentLine.add(m); // add current mouse position
    } else if (m.buttonRaw !== 0 && m.prevButton !== 0) { // while mouse is down
      currentLine.add(m); // add current mouse position      
    }
    m.prevButton = m.buttonRaw; // remember the previous mouse state
    e.preventDefault();
  }
  // starts the mouse 
  m.start = function(element, blockContextMenu) {
    m.element = element;

    mouseEvents.forEach(n => document.addEventListener(n, mouseMove));
    if (blockContextMenu === true) {
      document.addEventListener("contextmenu", preventDefault)
    }
    return m
  }
  return m;
}
var cursor = "crosshair";
function update(timer) { // Main update loop
  cursor = "crosshair";
  globalTime = timer;
  // if the window size has changed resize the canvas
  if (w !== innerWidth || h !== innerHeight) {
    resizeCanvas()
  }
  if (updateDisplay) {
    updateDisplay = false;
    display(); // call demo code
  }
 
  ctx.canvas.style.cursor = cursor;
  requestAnimationFrame(update);
}
// create a drawing canvas.
const drawing = createCanvas(drawingInfo.width, drawingInfo.height);
// fill with white
drawing.ctx.fillStyle = drawingInfo.bgColor;
drawing.ctx.fillRect(0, 0, drawing.width, drawing.height);

// function to display drawing 
function display() {
  ctx.clearRect(0, 0, w, h);
  ctx.fillStyle = "rgba(0,0,0,0.25)";
  const imgX = cw - (drawing.width / 2) | 0;
  const imgY = ch - (drawing.height / 2) | 0;
  // add a shadow to make it look nice
  ctx.fillRect(imgX + 5, imgY + 5, drawing.width, drawing.height);

  // add outline
  ctx.strokeStyle = "black";
  ctx.lineWidth = "2";
  ctx.strokeRect(imgX, imgY, drawing.width, drawing.height);
  // draw the image
  ctx.drawImage(drawing, imgX, imgY);
  if (mouse.buttonRaw !== 0) {
    if (currentLine !== undefined) {
      currentLine.draw(ctx, displayOffset); // draw line on display canvas
      cursor = "none";
      updateDisplay = true; // keep updating 
    }
  } else if (mouse.buttonRaw === 0) {
    if (currentLine !== undefined) {
      currentLine.draw(drawing.ctx, {x: -imgX, y: -imgY }); // draw line on drawing
      currentLine = undefined;
      updateDisplay = true;
      // next line is a quick fix to stop a slight flicker due to the current frame not showing the line
      ctx.drawImage(drawing, imgX, imgY);

    }
  }
}

requestAnimationFrame(update);
#can {
  position: absolute;
  top: 32px;
  left: 0px;
  background-color: #AAA;
}

.colors {
  border: 1px solid black;
  display: inline-flex;
}

.swatch {
  min-width: 16px;
  min-height: 16px;
  max-width: 16px;
  border: 1px solid black;
  display: inline-block;
  margin: 2px;
  cursor: pointer;

}

.highlight {
  border: 1px solid red;
}
<canvas id="can"></canvas>
  <div class="colors" id="colorSel"></div>

Update In response to the comment by the OP I have added a HTML version that you should be able to copy and paste (everything and including <!DOCTYPE HTML> to </HTML>) into a html document (eg drawing.html) and then open in a browser that supports ES6. eg Chrome, Firefox, Edge.

Copy content of snippet below.

<!DOCTYPE HTML>
<html>
<head>
    <style>
#can {
  position: absolute;
  top: 32px;
  left: 0px;
  background-color: #AAA;
}

.colors {
  border: 1px solid black;
  display: inline-flex;
}

.swatch {
  min-width: 16px;
  min-height: 16px;
  max-width: 16px;
  border: 1px solid black;
  display: inline-block;
  margin: 2px;
  cursor: pointer;

}

.highlight {
  border: 1px solid red;
}
    </style>
</head>

<body>
    <canvas id="can"></canvas>
    <div class="colors" id="colorSel"></div>

<script>
// size of drawing and its starting background colour
const drawingInfo = {
  width: 384 ,
  height: 160,
  bgColor: "white",
}
const brushSizes = [1, 2, 3, 4, 5, 6, 7, 8];
const colors = "red,orange,yellow,green,cyan,blue,purple,white,gray,black".split(",");
var currentColor = "blue";
var currentWidth = 2;
var currentSelectBrush;
var currentSelectColor;
const colorSel = document.getElementById("colorSel");
colors.forEach((color, i) => {
  var swatch = document.createElement("span");
  swatch.className = "swatch";
  swatch.style.backgroundColor = color;
  if (currentColor === color) {
    swatch.className = "swatch highlight";
    currentSelectColor = swatch;
  } else {
    swatch.className = "swatch";
  }
  swatch.addEventListener("click", (e) => {
    currentSelectColor.className = "swatch";
    currentColor = e.target.style.backgroundColor;
    currentSelectColor = e.target;
    currentSelectColor.className = "swatch highlight";
  });
  colorSel.appendChild(swatch);
})
brushSizes.forEach((brushSize, i) => {
  var brush = document.createElement("canvas");
  brush.width = 16;
  brush.height = 16;
  brush.ctx = brush.getContext("2d");
  brush.ctx.beginPath();
  brush.ctx.arc(8, 8, brushSize / 2, 0, Math.PI * 2);
  brush.ctx.fill();
  brush.brushSize = brushSize;
  if (currentWidth === brushSize) {
    brush.className = "swatch highlight";
    currentSelectBrush = brush;
  } else {
    brush.className = "swatch";
  }

  brush.addEventListener("click", (e) => {
    currentSelectBrush.className = "swatch";
    currentSelectBrush = e.target;
    currentSelectBrush.className = "swatch highlight";
    currentWidth = e.target.brushSize;

  });
  colorSel.appendChild(brush);
})


const canvas = document.getElementById("can");
const mouse = createMouse().start(canvas, true);
const ctx = canvas.getContext("2d");
var updateDisplay = true; // when true the display needs updating
var ch, cw, w, h; // global canvas size vars


var currentLine;

var displayOffset = {
  x: 0,
  y: 0
};

// a point object creates point from x,y coords or object that has x,y
const point = (x, y = x.y + ((x = x.x) * 0)) => ({
  x,
  y
});
// function to add a point to the line
function addPoint(x, y) {
  this.points.push(point(x, y));
}

function drawLine(ctx, offset) { // draws a line
  ctx.strokeStyle = this.color;
  ctx.lineWidth = this.width;
  ctx.lineJoin = "round";
  ctx.lineCap = "round";
  ctx.beginPath();
  var i = 0;
  while (i < this.points.length) {
    const p = this.points[i++];
    ctx.lineTo(p.x + offset.x, p.y + offset.y);
  }
  ctx.stroke();
}

function createLine(color, width) {
  return {
    points: [],
    color,
    width,
    add: addPoint,
    draw: drawLine,
  };
}


// creates a canvas
function createCanvas(width, height) {
  const c = document.createElement("canvas");
  c.width = width;
  c.height = height;
  c.ctx = c.getContext("2d");
  return c;
}
// resize main display canvas and set global size vars
function resizeCanvas() {
  ch = ((h = canvas.height = innerHeight - 32) / 2) | 0;
  cw = ((w = canvas.width = innerWidth) / 2) | 0;
  updateDisplay = true;
}

function createMouse() {
  function preventDefault(e) { e.preventDefault() }
  const mouse = {
    x: 0,
    y: 0,
    buttonRaw: 0,
    prevButton: 0
  };
  const bm = [1, 2, 4, 6, 5, 3]; // bit masks for mouse buttons
  const mouseEvents = "mousemove,mousedown,mouseup".split(",");
  const m = mouse;
  // one mouse handler
  function mouseMove(e) {
    m.bounds = m.element.getBoundingClientRect();
    m.x = e.pageX - m.bounds.left - scrollX;
    m.y = e.pageY - m.bounds.top - scrollY;
    
    if (e.type === "mousedown") {
      m.buttonRaw |= bm[e.which - 1];
    } else if (e.type === "mouseup") {
      m.buttonRaw &= bm[e.which + 2];
    }
    // check if there should be a display update
    if (m.buttonRaw || m.buttonRaw !== m.prevButton) {
      updateDisplay = true;
    }
    // if the mouse is down and the prev mouse is up then start a new line
    if (m.buttonRaw !== 0 && m.prevButton === 0) { // starting new line
      currentLine = createLine(currentColor, currentWidth);
      currentLine.add(m); // add current mouse position
    } else if (m.buttonRaw !== 0 && m.prevButton !== 0) { // while mouse is down
      currentLine.add(m); // add current mouse position      
    }
    m.prevButton = m.buttonRaw; // remember the previous mouse state
    e.preventDefault();
  }
  // starts the mouse 
  m.start = function(element, blockContextMenu) {
    m.element = element;

    mouseEvents.forEach(n => document.addEventListener(n, mouseMove));
    if (blockContextMenu === true) {
      document.addEventListener("contextmenu", preventDefault)
    }
    return m
  }
  return m;
}
var cursor = "crosshair";
function update(timer) { // Main update loop
  cursor = "crosshair";
  globalTime = timer;
  // if the window size has changed resize the canvas
  if (w !== innerWidth || h !== innerHeight) {
    resizeCanvas()
  }
  if (updateDisplay) {
    updateDisplay = false;
    display(); // call demo code
  }
 
  ctx.canvas.style.cursor = cursor;
  requestAnimationFrame(update);
}
// create a drawing canvas.
const drawing = createCanvas(drawingInfo.width, drawingInfo.height);
// fill with white
drawing.ctx.fillStyle = drawingInfo.bgColor;
drawing.ctx.fillRect(0, 0, drawing.width, drawing.height);

// function to display drawing 
function display() {
  ctx.clearRect(0, 0, w, h);
  ctx.fillStyle = "rgba(0,0,0,0.25)";
  const imgX = cw - (drawing.width / 2) | 0;
  const imgY = ch - (drawing.height / 2) | 0;
  // add a shadow to make it look nice
  ctx.fillRect(imgX + 5, imgY + 5, drawing.width, drawing.height);

  // add outline
  ctx.strokeStyle = "black";
  ctx.lineWidth = "2";
  ctx.strokeRect(imgX, imgY, drawing.width, drawing.height);
  // draw the image
  ctx.drawImage(drawing, imgX, imgY);
  if (mouse.buttonRaw !== 0) {
    if (currentLine !== undefined) {
      currentLine.draw(ctx, displayOffset); // draw line on display canvas
      cursor = "none";
      updateDisplay = true; // keep updating 
    }
  } else if (mouse.buttonRaw === 0) {
    if (currentLine !== undefined) {
      currentLine.draw(drawing.ctx, {x: -imgX, y: -imgY }); // draw line on drawing
      currentLine = undefined;
      updateDisplay = true;
      // next line is a quick fix to stop a slight flicker due to the current frame not showing the line
      ctx.drawImage(drawing, imgX, imgY);

    }
  }
}

requestAnimationFrame(update);

/* load and add image to the drawing. It may take time to load. */
function loadImage(url){
    const image = new Image();
    image.src = url;
    image.onload = function(){
        if(drawing && drawing.ctx){
            drawing.width = image.width;
            drawing.height = image.height;
            drawing.ctx.drawImage(image,0,0);
        };
    }

 }
 loadImage("https://i.stack.imgur.com/C7qq2.png?s=328&g=1");
        

</script>

</body>
</html>
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • I already tried your snippet, it worked here, but when I copy into html file it just wont show anything. I guess I make another mistake in implementing the codes again. And the code that starts with #can, does it included in the original html file? Sorry for being such a newbie. Thank you so much for helping me out Mr. Blindman67. – Raphael Richfields Riyanto Jul 06 '17 at 13:58
  • @RaphaelRichfieldsRiyanto I have added a snippet to the answer that you can copy and paste into a HTML document (sorry it's a bit of a mess and just the basics of a HTML doc). See update at bottom of my answer for more info. – Blindman67 Jul 06 '17 at 15:10
  • It's wonderful, I have tried them and it works. If I want to put images to the empty rectangle so I may scrap upon it, what should I add to the code then? Thank you so much for helping me out Mr. Blindman67. – Raphael Richfields Riyanto Jul 06 '17 at 15:29
  • @RaphaelRichfieldsRiyanto I have added a quick image loader at the bottom of the new snippet. Just call the function `loadImage("imageURL.jpg")` with the url of the image, It will load the image and once loaded resize the drawing and add the image to it. Note there will be a delay while the image loads and there is no error checking – Blindman67 Jul 06 '17 at 15:46
  • @ Blindman67 I tried the newest snippet, and the image is not showing. Maybe it's my slow connection or something, but after several tries, it is still not loading up any image. – Raphael Richfields Riyanto Jul 06 '17 at 17:31
  • @RaphaelRichfieldsRiyanto Hard to know what the problem is. You can get the dev tools console by hitting F12 on the browser, then find the console tab. If there are problems loading the image it should have something about it in there. If you see nothing just refresh the page with the console open. If you still see nothing type the image URl into the browser's address bar. If it does not appear then you have the wrong URL, if it does appear then copy the line with `loadImage(???)` (??? is the image url) into the comments and I will see what could be wrong. – Blindman67 Jul 06 '17 at 18:01
  • net::ERR_INSECURE_RESPONSE. I got this from Chrome. – Raphael Richfields Riyanto Jul 06 '17 at 18:13
  • @RaphaelRichfieldsRiyanto what is the image url you are trying to load?, that is a encryption error when making a request via `https` have you tried to load your image via `http://yourDomain.com/yourImageFile.jpg` (no s) – Blindman67 Jul 06 '17 at 18:30
  • @ Blindman67 : Terribly sorry for falling asleep last night, The image is the one that you provide in the snippet. I'll try to load my own image first then. Will let you know ASAP about the result. Thank you. – Raphael Richfields Riyanto Jul 07 '17 at 00:41
  • Good morning Mr. Blindman67, I finally got the image showing, by replacing the loadImage(https:???) with loadImage(http:???). Thank you so much for your kind assistance. God Bless You. – Raphael Richfields Riyanto Jul 07 '17 at 02:02
  • Good morning Mr. Blindman67, what should I do if I wanted to add music/sound into the previous webpage? Thank you so much for the quick answer and your great help. – Raphael Richfields Riyanto Jul 10 '17 at 03:16
  • @RaphaelRichfieldsRiyanto The is not enough room in the comments to explain. There are plenty of online tutorials, and Stackoverflow has plenty of answered questions on the subject. Or you could ask another question if you have tried some code and its not working. – Blindman67 Jul 10 '17 at 09:38
  • @Blindman67 sorry for imposing..i've tried this code and the drawing is showing but i'm having an error "Expected ')' " at the line `const point = (x, y = x.y + ((x = x.x) * 0)) => ({ x, y })` . Could you help me out please? – Nurul Nov 07 '18 at 02:26
0

The process is quite simple really when you think about it:

  • First, you take the points (x and y coordinates) of the mouse when clicked (make sure you only take these points when the mouse is being clicked).List item
  • You then draw lines between the points. This should also be easy. (Remember that the lines should only be drawn when the mouse is down and should begin from the first point at which the user presses the mouse or touches the screen. Pretty much whenever there's input)
  • Now to save the drawing you
    • convert the image to a data URL using the canvas' toDataURL() function.
    • then use this code to convert the dataURL into a Blob.
    • use URL.createObjectURL(blob) to create a link to the blob.
    • create an <a> tag and set it's href to the link previously mentioned. Also set it's download attribute to whatever you want the file to be called (e.g. 'myImage.png'). This should leave you with an <a> tag that looks something like this <a href="blob:https://example.com/3d45a4f6-7302-485a-a255-d7f56ecf8074" download="myImage.png">Click to Save</a>

This, of course, is how I see you completing this app.

One more thing, please read the following tutorials to learn more:

Great day! Hope this helps, sorry for being so lazy and not giving actual code to back up this answer.

Kitanga Nday
  • 3,517
  • 2
  • 17
  • 29