Search code examples
javascripthtmlcanvasdrawing

JS Canvas - Can't draw smooth lines


I've been trying to draw smooth lines on my canvas but I've had no luck. I've tried setting the lineCap to round, using quadratic instead of lineTo, but nothing worked.

While the mouse moves, it records the position of the mouse into an array and then runs through the array to draw the lines.

**JavaScript**
// GLOBAL VARIABLES - CONTAINER/CANVAS
var wContainer  = document.getElementById('Whiteboard_Container');
var layerGrid   = document.getElementById('LayerGrid');
var lgctx       = layerGrid.getContext('2d');
var layer1      = document.getElementById('Layer1');
var l1ctx       = layer1.getContext('2d');

// GLOBAL VARIABLES - MOUSE/TOUCH
var cursorPos   = { top: 0, left: 0, x: 0, y: 0 };
var cursorType  = 'mouse';  // USED TO CONTROL MOUSE/TOUCH
var dragging    = false;    // SETS/STOPS DRAGGING CANVAS
var dragLock    = false;    // USED TO PREVENT ACCIDENTAL DRAGGING (esp. touch)

// GLOBAL VARIABLES - DRAWING
var layer   = 'LAYER1';
var vectors = [];

// MOUSE EVENT LISTENERS
layer1.addEventListener('mousedown', mouseDown);
layer1.addEventListener('mouseup', mouseUp);

// TOUCH EVENT LISTENERS
layer1.addEventListener('touchstart', touchDown);
layer1.addEventListener('touchend', touchUp);

// INITIALISER
function init()
{
    resize();

    // SET PEN STYLE
    lgctx.strokeStyle = 'rgba(230, 230, 230, 1)';
    lgctx.lineWidth   = 1;
    lgctx.lineCap     = 'round';
    lgctx.lineJoin    = 'round';

    // SET PEN STYLE
    l1ctx.strokeStyle = 'rgba(0, 0, 0, 1)';
    l1ctx.lineWidth   = 10;
    l1ctx.lineCap     = 'round';
    l1ctx.lineJoin    = 'round';
}
init();

// AUTO SIZE THE CANVAS
function resize()
{
    // SET THE CANVAS SIZE
    layerGrid.width     = window.innerWidth * 4;
    layerGrid.height    = window.innerHeight * 4;
    layer1.height       = window.innerHeight * 4;
    layer1.width        = window.innerWidth * 4;
    layer1.height       = window.innerHeight * 4;
}

// MOUSE DOWN
function mouseDown(e)
{
    // Set cursor type
    cursorType = 'mouse';

    // EMPTY CURRENT VECTOR LIST
    vectors = [];
    vectors.length = 0;

    if(e.button == 0) // LEFT MOUSE CLICK
    {
        layer1.addEventListener('mousemove', setVectors);
    }
    else if(e.button == 2 && dragLock == false) // RIGHT MOUSE CLICK
    {
        dragging = true;
        layer1.addEventListener('mousemove', canvasDrag);

        // SET START SCROLL POSITION
        cursorPos.top  = wContainer.scrollTop;
        cursorPos.left = wContainer.scrollLeft;

        // SET CURSOR POSITION
        cursorPos.x = e.clientX;
        cursorPos.y = e.clientY;
    }
}

// MOUSE UP
function mouseUp(e)
{
    // PREVENT DRAGGING & REMOVE GRID
    dragging = false;
    layer1.removeEventListener('mousemove', canvasDrag);
    layer1.removeEventListener('mousemove', setVectors);
    removeGrid();
}

// TOUCH DOWN
function touchDown(e)
{
    e.preventDefault();

    // Set cursor type
    cursorType = 'touch';

    // EMPTY CURRENT VECTOR LIST
    vectors = [];
    vectors.length = 0;

    if(e.touches.length == 1)       // SINGLE TOUCH
    {
        layer1.addEventListener('touchmove', setVectors);
    }
    else if(e.touches.length > 1 && dragLock == false)  // MULTI-TOUCH
    {
        dragging = true;
        layer1.addEventListener('touchmove', canvasDrag);

        // SET START SCROLL POSITION
        cursorPos.top  = wContainer.scrollTop;
        cursorPos.left = wContainer.scrollLeft;

        // SET CURSOR POSITION
        cursorPos.x = e.touches[0].clientX;
        cursorPos.y = e.touches[0].clientY;
    }
}

// TOUCH UP
function touchUp(e)
{
    // PREVENT DRAGGING & REMOVE GRID
    dragging = false;
    layer1.removeEventListener('mousemove', canvasDrag);
    layer1.removeEventListener('touchmove', setVectors);
    removeGrid();
}

// HANDLE MOVEMENT
function canvasDrag(e)
{
    if(dragging == true && cursorType == 'mouse')   // MOUSE DRAG
    {
        // DRAW GRID AS GUIDANCE
        drawGrid();

        const dx = e.clientX - cursorPos.x;
        const dy = e.clientY - cursorPos.y;

        wContainer.scrollTop  = cursorPos.top - dy;
        wContainer.scrollLeft = cursorPos.left - dx;
    }
    else if(dragging == true && cursorType == 'touch' && e.touches.length > 1) // TOUCH DRAG
    {
        // DRAW GRID AS GUIDANCE
        drawGrid();

        const dx = e.touches[0].clientX - cursorPos.x;
        const dy = e.touches[0].clientY - cursorPos.y;

        wContainer.scrollTop  = cursorPos.top - dy;
        wContainer.scrollLeft = cursorPos.left - dx;
    }
}

// SET VECTORS
function setVectors(e)
{
    if(cursorType == 'mouse') // MOUSE DRAW
    {
        // Set the drawing vectors
        vectors.push({x:e.clientX + wContainer.scrollLeft, y:e.clientY + wContainer.scrollTop});
    }
    else if(cursorType == 'touch' && e.touches.length == 1) // ONLY ALLOW IF SINGLE TOUCH
    {
        // Set the drawing vectors
        vectors.push({x:e.touches[0].clientX + wContainer.scrollLeft, y:e.touches[0].clientY + wContainer.scrollTop});
    }

    // Draw the path, based on the vectors array
    drawVectors();
}

// DRAW VECTORS
function drawVectors()
{
    l1ctx.beginPath();
    //l1ctx.clearRect(0, 0, layer1.width, layer1.height);
    for(var i = 0; i < vectors.length - 2; i++)
    {
        var xc = (vectors[i].x + vectors[i + 1].x) / 2;
        var yc = (vectors[i].y + vectors[i + 1].y) / 2;
        l1ctx.quadraticCurveTo(vectors[i].x, vectors[i].y, xc, yc);
        //l1ctx.lineTo(vectors[i].x, vectors[i].y);
    }
    l1ctx.stroke();
}

// DRAW GRID
function drawGrid()
{
    lgctx.beginPath();
    for(var i = 0; i < layer1.width; i = i + 100)
    {
        lgctx.moveTo(i, 0); 
        lgctx.lineTo(i, layer1.height);
    }
    lgctx.moveTo(0, 0);
    for(var i = 0; i < layer1.height; i = i + 100)
    {
        lgctx.moveTo(0, i); 
        lgctx.lineTo(layer1.width, i);
    }
    lgctx.stroke();
    lgctx.closePath();
}

// REMOVE GRID
function removeGrid()
{
    lgctx.clearRect(0, 0, layerGrid.width, layerGrid.height);
}

The first line is how its drawn, the second is how I'm trying to get it to appear: Line comparison


Solution

  • You never clear your l1ctx context.
    So you are always drawing new lines over the previous ones and killing the antialiasing.

    Simply add a clearRect call at the beginning of drawVectors.