Canvas consumes a lot of memory

2019-06-23 22:46发布

问题:

I am having difficulties with my Canvas-implementation which I open in an overlay. The canvas element is 760px wide and 2640px high (I know, don't ask).

I am drawing lines at every 27.5px high.

ctx.moveTo(0, y);
ctx.lineTo(760, y);
ctx.strokeStyle = 'rgb(100,100,100)';
ctx.stroke();

Appearantly the browser seems to 'choke' on this when creating the canvas. Eventually it comes through (1-5secs) and memory is raised by 20MB.

Closing the overlay does not seem to free this memory. When I reopen the overlay (which redraws the canvas), the memory is again increased. And so on, and so on... My chrome process goes from 60MB memory to 600+ in no time this way.

Resizing the canvas to 264px high and drawing lines at every 2.75px goes way faster and consumes only about 4MB (which also does not seem to be cleared of course).

Who has some pointers on how to avoid this.


Here is more code data is an array of objects containing an Entries property which is also an array.

[ { Entries : [{...},{...},...] }, {...}, ... ]

var $canvas = container.find('canvas')
    , canvas = $canvas.get(0)
    , maxY = canvas.height
    , maxX = canvas.width
    , dX = maxX / (data.length + 1)
    , ctx = canvas.getContext('2d');


var x1, y1, y2, mh;

$.each(data, function (i, day) {
    if (!day.Entries) return;

     $.each(day.Entries, function (j, entry) {
         x1 = (i + 1) * dX;
         mh = entry.BeginDate.toHourMinutes();
         y1 = (((mh.h * 60) + mh.m) / 1440) * maxY;
         mh = entry.EndDate.toHourMinutes();
         y2 = (((mh.h * 60) + mh.m) / 1440) * maxY;

         switch (entry.Type) {
             case CALENDARTYPES.OPENINGHOUR:
                 ctx.beginPath();
                 ctx.rect(x1, y1, dX - 10, y2 - y1);
                 ctx.fillStyle = "rgb(125, 125, 125)";
                 ctx.fill();
                 ctx.closePath();
                 break;
             case CALENDARTYPES.BLOCKING:
                 ctx.clearRect(x1, y1, dX, y2 - y1);
                 break;
         };
      });
  });

       delete x1, y1, y2, mh;

       //Draw grid on canvas.

       var x = 0
           , y = +0.5
           , stepYH = maxY / 24
           , stepYQ = stepYH / 4
           , isHour = true;

       ctx.lineWidth = 1;

       while (y < maxY) {
           isHour = (((y - 0.5) % stepYH) == 0);
           ctx.moveTo(isHour ? x : x + dX, y);
           ctx.lineTo(maxX, y);
           ctx.strokeStyle = isHour ? 'rgb(175,175,175)' : 'rgb(100,100,100)';
           ctx.stroke();
           y += stepYQ;
       };

回答1:

As per the comments:

If you do not clear the path, you're basically extending the path, and since .stroke() strokes the (entire) path, you'll end up drawing more and more as you add more points using .moveTo/.lineTo.

It might make more sense to use .beginPath() so that you only stroke the new path and not the old path as well:

  • Path is cleared from memory - less leaks
  • Old path is not drawn again - less performance loss


回答2:

Edit: It looks like @pimvdb hit the nail on the head with his solution. Omitting the begin and end path calls will indeed continue to build a never-ending path.


Can you post enough of your code to allow us to view this issue?

Unless there's some very obscure bug in Chrome that I'm not aware of, using a canvas with the dimensions you specified is not an issue. I've worked on full-screen canvas applications of that magnitude and haven't had any issues.

The memory increasing by 20MB does seem somewhat excessive, so I would be inclined to believe that you are unnecessarily creating copies of the canvas element (or something related to it) in memory. The fact that you can increase the memory consumption on demand by closing and opening the overlay that displays and re-draws the canvas reinforces this belief.

If you can post your code, I'd be happy to take a look at it and help you identify the problem.