Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I seeing streaks with <canvas> rotation on mobile browsers?

I'm performing a rotation of a 2d canvas which works pretty well on the desktop but there's one small issue in the mobile space. Here's a zoomed in screenshot:

enter image description here

The thumb image is rotated about 0.2rad over the course of 500ms. What I think is all of the relevant code is inline below. As you can see, there is some sort of "trail" left by each of the top corners of the image.

var duration = 500;
var start = 0;
var stop = 0.287554326;
var step = (stop - start) / 10;
var steps = (stop - start) / step;
var current = 0;
var delay = duration / steps;
var first = true;
if (navigator.userAgent.match(/iP(hone|[ao]d)|android/i)) step *= 1.5;
var rotate_int = setInterval(function() {
  if (current >= stop) {
    clearInterval(rotate_int);
    callback && callback();
    return;
  }
  ctx.clearRect(0, 0, cvs.width, cvs.height);
  ctx.translate(cvs.width / 2, cvs.height / 2);
  ctx.rotate(step);
  current += step;
  ctx.translate(cvs.width / -2, cvs.height / -2);
  ctx.drawImage(i, 0, 0);
  if (first) {
    first = false;
    thumb.hide();
  }
}, delay);

Notes:

  1. The code works very well on the desktop (the most recent incarnations of Firefox, Chrome, Safari, Opera, and even IE)
  2. I've tested the following devices and browsers:
    1. iPhone 3GS: Safari, Opera Mini
    2. iPhone 4S: Safari
    3. iPad (1st gen): Safari
    4. Android Galaxy S (with gingerbread): default browser, Firefox mobile
    5. Motorola Droid X (with gingerbread): default browser, Firefox mobile
  3. I haven't found a desktop browser (supporting <canvas>) which exhibits the behavior
  4. I haven't found a mobile device which doesn't exhibit the behavior
  5. The posted image is a zoomed-in screenshot from the iPad
  6. If it matters, the <canvas> (while rotating) is being animated (via jQuery) to transit across the image behind it and come to a halt, which is visible in the screenshot.
  7. There is second <canvas> on the page also. It uses the same thumbs-up .png, and rotates using the same code posted above, and is also animated to transit across a different background image (but in the opposite direction, i.e., one "thumbs up" moves northwest, and one southeast), and the trails appear there as well, in the same location relative to the canvas context.

I've thrown all the mud on this wall I can think of, in hopes that something may lead to a diagnosis. Has anyone else seen anything like this before? What could I try differently? Have I missed some big red warning label on an HTML5 tutorial site somewhere that warns of such behavior?

==EDIT 1==

Per @GGG's comment, I removed the UA sniff (which is designed to decrease the number and frequency of canvas redraws because the mobile browsers all chug if I use the same settings as for desktop) but that just caused the trails to become more pronounced (e.g. thicker). I then experimented by putting the UA sniff back in, but instead of decreasing the loops by 50%, I actually increased them by 500%. Again, this caused the trails to become even more pronounced. It does seem, however, that this thickening is asymptotic - in other words, there is a limit to how thick I can cause the trails to be by tweaking the parameters of the animation speed.

==EDIT 2==

Per @GGG's other comment, I went to go edit the image to add some transparent data in an attempt to find a suitable workaround. What I saw was that the image jutted up against the top and left edges of the canvas (that's "of the Photoshop canvas," not "of the HTML5 <canvas>"). When I added an equal padding of transparent data to the top and left sides, the streaking problem disappeared. Here was the original PSD (pre me-adding-extra-spacing):

enter image description here

So my question then becomes: even if the image fills (with non-transparent pixels) the entirety of its [Photoshop] canvas, why isn't my canvas context clearRect() behaving itself? Should that not obliterate anything within the bounds of the canvas? If so, why is it leaving these few pixels?

==EDIT 3==

After some research, it turns out that Cairo is pretty commonly used by several major rendering engines (at least WebKit and Gecko). Could it be the case, as @JonasWielicki first suggested, that the Cairo library - when optimized for mobile execution - is perhaps a bit overzealous?

like image 245
Chris Tonkinson Avatar asked May 03 '12 19:05

Chris Tonkinson


1 Answers

Per comments, try adding some transparent pixels around the edge of the image as a workaround.

I really have no idea why this happens. I think it has something to do with odd handling of alpha channels on mobile devices, but this is nothing more than a guess.

I've noticed that mobile browsers seem to drop or "estimate" the alpha channel while scrolling (slowly scroll up and down, even the fonts look more "crispy"). I wonder if they render things in two stages, leaving the alpha channel for the second stage, and skipping the second stage if there is another "frame" to render immediately following the current "frame," if that makes any sense. Maybe that somehow confuses the renderer into thinking it hasn't drawn things in places where it has.

Anyway, this probably does warrant a bug report. I'd be curious to hear a real explanation for what's going on if nothing else.

like image 66
Dagg Nabbit Avatar answered Sep 28 '22 08:09

Dagg Nabbit