How to Draw on Canvas and Save the Result Using Vanilla JavaScript

on in Canvas
Last modified on

This was part of a complex drawing project, which is not relevant right now. The idea was to have a fully configurable canvas, where you could pick colours and sizes to write and draw anything.

I’m sure this has been done before, so I tried to keep my code to a minimum, while making sure the code has no dependencies and it’s modern and fast.

Here’s the HTML structure:

<div class="container">
    <canvas width="800%" height="400px" id="art"></canvas>
    <div id="buttons">
        <a id="reset">Restart</a>

        <div id="red" class="color"></div>
        <div id="blue" class="color"></div>
        <div id="green" class="color"></div>
        <div id="purple" class="color"></div>
        <div id="orange" class="color"></div>
        <div id="black" class="color"></div>
        <div id="white" class="color"></div>

        <div id="default" class="color">1</div>
        <div id="three" class="color">3</div>
        <div id="five" class="color">5</div>
        <div id="ten" class="color">10</div>
        <div id="fifteen" class="color">15</div>
        <div id="twenty" class="color">20</div>

        <a id="downloadLnk" class="color" href="#" download="illustration.png">Export</a>
    </div>
</div>

Here’s the CSS:

canvas {
    border: 1px solid #cbcbcb;
    background: #ecf0f1;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
    cursor: crosshair;
}
.container {
    position: relative;
}
#buttons {
    position: absolute;
    top: 5px;
    left: 10px;
}
#reset {
    font-size: 24px;
    margin: 10px 10px 0 10px;
    float: left;
    cursor: pointer;
    color: #7f8c8d;
}
#downloadLnk {
    margin: 10px 10px 0 10px;
    color: #7f8c8d;
}
.color {
    color: #7f8c8d;
    width: 26px;
    height: 26px;
    line-height: 26px;
    float: left;
    margin: 10px 1px 0 0;
    cursor: pointer;
    text-align: center;
}
#red {
    background-color: #e74c3c;
}
#blue {
    background-color: #3498db;
}
#green {
    background-color: #2ecc71;
}
#purple {
    background-color: #9b59b6;
}
#orange {
    background-color: #f39c12;
}
#black {
    background-color: black;
}
#white {
    background-color: white;
}

And here’s the JavaScript functionality:

const canvas = document.getElementById('art');
const ctx = canvas.getContext('2d');

function getMousePos(canvas, evt) {
    let rect = canvas.getBoundingClientRect();
    return {
        x: evt.clientX - rect.left,
        y: evt.clientY - rect.top
    };
}

function mouseMove(evt) {
    let mousePos = getMousePos(canvas, evt);

    ctx.lineTo(mousePos.x, mousePos.y);
    ctx.stroke();
}

canvas.addEventListener('mousedown', (evt) => {
    let mousePos = getMousePos(canvas, evt);

    ctx.beginPath();
    ctx.moveTo(mousePos.x, mousePos.y);
    evt.preventDefault();
    canvas.addEventListener('mousemove', mouseMove, false);
});

canvas.addEventListener('mouseup', () => {
    canvas.removeEventListener('mousemove', mouseMove, false);
}, false);

document.getElementById('reset').addEventListener('click', () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}, false);

const colors = ['red', 'blue', 'green', 'purple', 'orange', 'black', 'white'];
const size = [1, 3, 5, 10, 15, 20];
const sizeNames = ['default', 'three', 'five', 'ten', 'fifteen', 'twenty'];

function listener(i) {
    document.getElementById(colors[i]).addEventListener('click', () => {
        ctx.strokeStyle = colors[i];
    }, false);
}

function fontSizes(i) {
    document.getElementById(sizeNames[i]).addEventListener('click', () => {
        ctx.lineWidth = size[i];
    }, false);
}

for (let i = 0; i < colors.length; i++) {
    listener(i);
}

for (let i = 0; i < size.length; i++) {
    fontSizes(i);
}

document.getElementById('downloadLnk').addEventListener('click', () => {
    let image = canvas.toDataURL("image/png");
    this.href = image;
});

See a live demo here.

While I’m not particularly happy with the CSS and I know there’s room for improvement, the code is pretty fast and does the job. It’s also mobile compatible.

Related posts