Javascript Canvas 内容随着时间的推移变慢

Javascript Canvas Content Get Slow Over Time

这项工作需要一些帮助。基本上我使用 Javascript canvas 来模拟高速公路的道路。请注意,最初一切都很顺利且状况良好。然而,随着时间的推移,内容将不再是顺畅的。

有人知道这里的问题是什么吗?我注意到刷新浏览器确实解决了问题。在那种情况下,我可以知道如何在没有可见刷新的情况下刷新内容吗?

window.requestAnimationFrame = window.requestAnimationFrame
                               || window.mozRequestAnimationFrame
                               || window.webkitRequestAnimationFrame
                               || window.msRequestAnimationFrame
                               || function (f) { return setTimeout(f, 1000 / 50) }

window.cancelAnimationFrame = window.cancelAnimationFrame
                              || window.mozCancelAnimationFrame

var canvas = document.getElementById("canV"); //obtain the content 
var ctx = canvas.getContext("2d");
var angle = 0;
var rect = []; //initialize for predefined width purposes.
var mouse = {
    x: 0,
    y: 0,
    w: 0,
    alt: false,
    shift: false,
    ctrl: false,
    buttonLastRaw: 0, // user modified value
    buttonRaw: 0,
    over: false,
    buttons: [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
};

function mouseMove(event) {
    mouse.x = event.offsetX;
    mouse.y = event.offsetY;
    if (mouse.x === undefined) {
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    }
    mouse.alt = event.altKey;
    mouse.shift = event.shiftKey;
    mouse.ctrl = event.ctrlKey;
    if (event.type === "mousedown") {
        event.preventDefault();
        //console.log("this is mouse.buttonRaw before (mousedown): " + mouse.buttonRaw);
        console.log("this is event.which (mousedown): " + event.which);
        var valu = mouse.buttonRaw |= mouse.buttons[event.which - 1]; //bitwise AND operator (convert to binary and do the operation)
        //console.log("this is value value: " + value);
    } else if (event.type === "mouseup") {
        //console.log("this is mouse.buttonRaw before (mouseup): " + mouse.buttonRaw);
        //console.log("this is event.which (mouseup): " + event.which);
        mouse.buttonRaw &= mouse.buttons[event.which + 2]; //bitwise OR operator (convert to binary and do the operation)
    } else if (event.type === "mouseout") {
        mouse.buttonRaw = 0;
        mouse.over = false;
    } else if (event.type === "mouseover") {
        mouse.over = true;
    } else if (event.type === "mousewheel") {
        event.preventDefault();
        mouse.w = event.wheelDelta;
    } else if (event.type === "DOMMouseScroll") { // FF you pedantic doffus
        mouse.w = -event.detail;
    }


}

function setupMouse(e) {
    //syntax addEventListener (event, function, useCapture)
    e.addEventListener('mousemove', mouseMove);
    e.addEventListener('mousedown', mouseMove);
    e.addEventListener('mouseup', mouseMove);
    e.addEventListener('mouseout', mouseMove);
    e.addEventListener('mouseover', mouseMove);
    e.addEventListener('mousewheel', mouseMove);
    e.addEventListener('DOMMouseScroll', mouseMove); // fire fox

    e.addEventListener("contextmenu", function(e) {
        e.preventDefault();
    }, false);
}
setupMouse(canvas);


// terms.
// Real space, real, r (prefix) refers to the transformed canvas space.
// c (prefix), chase is the value that chases a required value
var displayTransform = {
    x: 0,
    y: 0,
    ox: 0,
    oy: 0,
    scale: 6,
    rotate: 0,

    cx: 0, // chase values Hold the actual display
    cy: 0,
    cox: 0,
    coy: 0,
    cscale: 1,
    crotate: 0,

    dx: 0, // deltat values
    dy: 0,
    dox: 0,
    doy: 0,
    dscale: 1,
    drotate: 0,

    drag: 0.2, // drag for movements
    accel: 0.5, // acceleration
    matrix: [0, 0, 0, 0, 0, 0], // main matrix
    invMatrix: [0, 0, 0, 0, 0, 0], // invers matrix;
    mouseX: 0,
    mouseY: 0,
    ctx: ctx,
    setTransform: function() {
        var m = this.matrix;
        var i = 0;
        this.ctx.setTransform(m[i++], m[i++], m[i++], m[i++], m[i++], m[i++]);
    },
    setHome: function() {
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);

    },
    update: function() {
        // smooth all movement out. drag and accel control how this moves
        // acceleration 
        this.dx += (this.x - this.cx) * this.accel;
        console.log("this is dx: " + this.dx);
        this.dy += (this.y - this.cy) * this.accel;
        this.dox += (this.ox - this.cox) * this.accel;
        this.doy += (this.oy - this.coy) * this.accel;
        this.dscale += (this.scale - this.cscale) * this.accel;

        // drag
        this.dx *= this.drag;
        this.dy *= this.drag;
        this.dox *= this.drag;
        this.doy *= this.drag;
        this.dscale *= this.drag;

        // set the chase values. Chase chases the requiered values
        this.cx += this.dx;
        this.cy += this.dy;
        this.cox += this.dox;
        this.coy += this.doy;
        this.cscale += this.dscale;

        // create the display matrix
        this.matrix[0] = Math.cos(this.crotate) * this.cscale;
        this.matrix[1] = Math.sin(this.crotate) * this.cscale;
        this.matrix[2] = -this.matrix[1];
        this.matrix[3] = this.matrix[0];

        // set the coords relative to the origin
        this.matrix[4] = -(this.cx * this.matrix[0] + this.cy * this.matrix[2]) + this.cox;
        this.matrix[5] = -(this.cx * this.matrix[1] + this.cy * this.matrix[3]) + this.coy;

        // create invers matrix
        var det = (this.matrix[0] * this.matrix[3] - this.matrix[1] * this.matrix[2]);
        this.invMatrix[0] = this.matrix[3] / det;
        this.invMatrix[1] = -this.matrix[1] / det;
        this.invMatrix[2] = -this.matrix[2] / det;
        this.invMatrix[3] = this.matrix[0] / det;

        // check for mouse. Do controls and get real position of mouse.
        if (mouse !== undefined) { // if there is a mouse get the real canvas coordinates of the mouse
            if (mouse.oldX !== undefined && (mouse.buttonRaw & 1) === 1) { // check if panning (middle button)
                var mdx = mouse.x - mouse.oldX; // get the mouse movement
                var mdy = mouse.y - mouse.oldY;
                // get the movement in real space
                var mrx = (mdx * this.invMatrix[0] + mdy * this.invMatrix[2]);
                var mry = (mdx * this.invMatrix[1] + mdy * this.invMatrix[3]);
                this.x -= mrx;
                this.y -= mry;
            }
            // do the zoom with mouse wheel
            if (mouse.w !== undefined && mouse.w !== 0) {
                //this.ox = mouse.x;
                //this.oy = mouse.y;
                //this.x = this.mouseX;
                //this.y = this.mouseY;
                /* Special note from answer */
                // comment out the following is you change drag and accel
                // and the zoom does not feel right (lagging and not 
                // zooming around the mouse 
                /*
                this.cox = mouse.x;
                this.coy = mouse.y;
                this.cx = this.mouseX;
                this.cy = this.mouseY;
                */
                if (mouse.w > 0) { // zoom in
                    this.scale *= 1.1;
                    console.log(this.scale);
                    mouse.w -= 20;
                    if (mouse.w < 0) {
                        mouse.w = 0;
                    }
                }
                if (mouse.w < 0) { // zoom out
                        this.scale *= 1/1.1;
                        mouse.w += 20;
                        if(this.scale <=0.5){
                            this.scale = 0.5;
                        }
                    if (mouse.w > 0) {
                        mouse.w = 0;
                    }
                }
            }
            // get the real mouse position 
            var screenX = (mouse.x - this.cox);
            var screenY = (mouse.y - this.coy);
            this.mouseX = this.cx + (screenX * this.invMatrix[0] + screenY * this.invMatrix[2]);
            this.mouseY = this.cy + (screenX * this.invMatrix[1] + screenY * this.invMatrix[3]);
            mouse.rx = this.mouseX; // add the coordinates to the mouse. r is for real
            mouse.ry = this.mouseY;
            // save old mouse position
            mouse.oldX = mouse.x;
            mouse.oldY = mouse.y;
        }

    }
}
// set up font
ctx.font = "14px verdana";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// timer for stuff
var timer = 0;
var w = 1000,
    h = 500;
setInterval(update, 6);
//requestAnimationFrame(update);
function update() {
    timer += 1; // update timer
    console.log("this is timer" + timer);
    if(timer > 1000){
        timer = 0;
        console.log("clear");
        clearInterval(update);
    }
    // update the transform
    displayTransform.update();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // set home transform to clear the screen
    displayTransform.setHome();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    displayTransform.setTransform();
    ctx.clearRect(0, 0, canvas.width, canvas.height);

        //first segment
        this.width = 23300;
        this.height = 150;
        this.x = 0;
        this.y = 150;
        this.color = "#605A4C"; //light grey (road)
        ctx.save(); //save the unrotated context of the canvas
        ctx.fillStyle = this.color;
        ctx.fillRect(this.x, this.y, this.width / 8, this.height);
        ctx.fillStyle = "#DCDCDC"; //white grey
        ctx.fillRect(this.x, this.y + ((this.height / 2) - 1), this.width / 8, 2); //middle white line

        ctx.fillStyle = "ffffff";
        ctx.beginPath();
        ctx.setLineDash([2, 6]);
        ctx.moveTo(this.x, this.y + ((this.height / 4) - 1));
        ctx.lineTo((this.x + this.width / 8), this.y + ((this.height / 4) - 1));
        ctx.closePath();
        ctx.strokeStyle = "#A09383"; //lighter grey
        ctx.lineWidth = 1;
        ctx.fill();
        ctx.stroke();

        ctx.beginPath();
        ctx.setLineDash([2, 6]);
        ctx.moveTo(this.x, this.y + ((this.height / (4 / 3)) - 1));
        ctx.lineTo((this.x + this.width / 8), this.y + ((this.height / (4 / 3)) - 1));
        ctx.closePath();
        ctx.strokeStyle = "#A09383"; //lighter grey
        ctx.lineWidth = 1;
        ctx.fill();
        ctx.stroke();


        ctx.fillStyle = "#A09383"; //lighter grey
        ctx.fillRect(this.x, this.y - 10, this.width / 8, 10);
        ctx.fillStyle = "#A09383"; //lighter grey
        ctx.fillRect(this.x, this.y + this.height, this.width / 8 + 2 /*a little long to cover up white region)*/ , 10);
        //requestAnimationFrame(update);
}
.canC {
    width: 100%;
    height: 100%;
    background-color: rgba(87, 80, 86, 0.5);
}
<div>
    <canvas class="canC" id="canV" width="3840" height="2160">
    </canvas>
</div>

正如 Ryan 所建议的那样。在更新函数末尾添加 ctx.restore() 解决了我的问题。为了更好地理解此类问题。凯多提供了参考linkHow to delete the state stack for a JavaScript canvas rendering context

再次感谢您对我的帮助。