mpen
mpen

Reputation: 283163

Canvas stops tracking mouse after awhile

I've got kind of a strange bug that I'm having trouble figuring out. First, here's the code:

const canvas = document.getElementById('canvas');
const canvasContainer = document.getElementById('canvas-container');
const ctx = canvas.getContext('2d');


function resizeCanvas() {
    canvas.width = canvasContainer.clientWidth;
    canvas.height = canvasContainer.clientHeight;
}



async function init() {
    window.addEventListener('resize', resizeCanvas)
    resizeCanvas();



    window.requestAnimationFrame(draw);

    let poly = [];

    canvas.addEventListener('click', ev => {
        let x = ev.clientX - canvas.offsetLeft;
        let y = ev.clientY - canvas.offsetTop;
        poly.push([x,y]);
    })



    window.addEventListener('keyup', ev => {
        if(ev.key === 'Escape') {
            poly.length = 0;
        }
    });

    let mousePos = null;

    canvas.addEventListener('mouseleave', ev => {
        mousePos = null;
    })

    canvas.addEventListener('mousemove', ev => {
        let x = ev.clientX - canvas.offsetLeft;
        let y = ev.clientY - canvas.offsetTop;
        mousePos = {x,y};
        console.log(x,y);
    })



    function draw() {
        // ctx.save();

        ctx.fillStyle = '#ADD5F8';

        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.fillStyle = '#C38F4C';

        if(poly.length >= 3) {
            ctx.beginPath();
            ctx.moveTo(...poly[0]);
            for(let i=1; i<poly.length; ++i) {
                ctx.lineTo(...poly[i]);
            }
            ctx.closePath();
            ctx.fill();
        }

        if(poly.length && mousePos) {
            ctx.beginPath();
            ctx.moveTo(...poly[0]);
            for(let i=1; i<poly.length; ++i) {
                ctx.lineTo(...poly[i]);
            }
            if(mousePos) {
                console.log(mousePos);
                ctx.lineTo(mousePos.x, mousePos.y);
            }
            ctx.strokeStyle = '#ff0000';
            ctx.stroke();
        }


        window.requestAnimationFrame(draw);
    }
}

function createImage(path) {
    return new Promise((resolve,reject) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = err => reject(err);
        img.src = path;
    })
}

async function createPattern(path, repetition='repeat') {
    // TODO: https://stackoverflow.com/a/21128933/65387
    const img = await createImage(path);
    return ctx.createPattern(img, repetition);
}



init();

// TODO: add physics https://github.com/shakiba/planck.js
* {
    box-sizing: border-box;
}

.toolbox {
    width: 100px;
    background-color: #535353;
}

.content {
    overflow: hidden;
    background-color: green;
    flex: 1;
}

.main {
    display: flex;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

}
    <div class="main">
      <div class="toolbox">toolbox</div>
      <div id="canvas-container" class="content">
        <canvas id="canvas"></canvas>
      </div>
    </div>

Interestingly, it seems to work just fine in this fiddle. But when I run it under Electron, this is what happens (gifv link) -- (please ignore the fact that the cursor is a little off in the video -- that's jut my screen recorder messing up) -- but do notice that the last red line gets messed up after a few clicks.

The console.log in the mousemove handler is firing when I move my mouse, and it's logging the correct coordinates. However, the mousePos var that I use inside draw() becomes fixated on a certain position and then doesn't update anymore.

I'm updating that same variable on every mouse move event, I don't understand how the two console.logs can come out different?


If I log it like this instead, watch what happens

canvas.addEventListener('mousemove', ev => {
    let x = ev.clientX - canvas.offsetLeft;
    let y = ev.clientY - canvas.offsetTop;
    mousePos = {x,y};
    console.log('a',x,y);
})



function draw() {
    if(mousePos) {
        console.log('b', mousePos.x, mousePos.y);
    }

"b" gets stuck at {102, 570} even though "a" keeps updating. It's the same variable!

Upvotes: 0

Views: 57

Answers (1)

Blindman67
Blindman67

Reputation: 54089

Odd behaviour, Solution easy

The first thing that struck me looking at your code was the main init function was defined as an async function, yet contained no awaits.

I am unsure as to the expected behaviour of an async function without an await, but the problem seems to happen after the same time period.

This is an interesting problem and worth some further investigation. There is a lot to read in the spec (async function definitions) and a quick glance has not shown any obvious reason.

Easy fix.

The fix is easy, remove the async token from the function init and all works fine.

Change

async function init() {

to

function init() {

Example

const canvas = document.getElementById('canvas');
const canvasContainer = document.getElementById('canvas-container');
const ctx = canvas.getContext('2d');


function resizeCanvas() {
    canvas.width = canvasContainer.clientWidth;
    canvas.height = canvasContainer.clientHeight;
}



function init() {
    window.addEventListener('resize', resizeCanvas)
    resizeCanvas();



    window.requestAnimationFrame(draw);

    let poly = [];

    canvas.addEventListener('click', ev => {
        let x = ev.clientX - canvas.offsetLeft;
        let y = ev.clientY - canvas.offsetTop;
        poly.push([x,y]);
    })



    window.addEventListener('keyup', ev => {
        if(ev.key === 'Escape') {
            poly.length = 0;
        }
    });

    let mousePos = null;

    canvas.addEventListener('mouseleave', ev => {
        mousePos = null;
    })

    canvas.addEventListener('mousemove', ev => {
        let x = ev.clientX - canvas.offsetLeft;
        let y = ev.clientY - canvas.offsetTop;
        mousePos = {x,y};


    })



    function draw() {
        // ctx.save();

        ctx.fillStyle = '#ADD5F8';

        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.fillStyle = '#C38F4C';

        if(poly.length >= 3) {
            ctx.beginPath();
            ctx.moveTo(...poly[0]);
            for(let i=1; i<poly.length; ++i) {
                ctx.lineTo(...poly[i]);
            }
            ctx.closePath();
            ctx.fill();
        }

        if(poly.length && mousePos) {
            ctx.beginPath();
            ctx.moveTo(...poly[0]);
            for(let i=1; i<poly.length; ++i) {
                ctx.lineTo(...poly[i]);
            }
            if(mousePos) {
     
                ctx.lineTo(mousePos.x, mousePos.y);
            }
            ctx.strokeStyle = '#ff0000';
            ctx.stroke();
        }


        window.requestAnimationFrame(draw);
    }
}

function createImage(path) {
    return new Promise((resolve,reject) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = err => reject(err);
        img.src = path;
    })
}

async function createPattern(path, repetition='repeat') {
    // TODO: https://stackoverflow.com/a/21128933/65387
    const img = await createImage(path);
    return ctx.createPattern(img, repetition);
}



init();

// TODO: add physics https://github.com/shakiba/planck.js
* {
    box-sizing: border-box;
}

.toolbox {
    width: 100px;
    background-color: #535353;
}

.content {
    overflow: hidden;
    background-color: green;
    flex: 1;
}

.main {
    display: flex;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

}
<div class="main">
      <div id="info" class="toolbox">toolbox</div>
      <div id="canvas-container" class="content">
        <canvas id="canvas"></canvas>
      </div>
    </div>

Upvotes: 1

Related Questions