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

1

10

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?

Weinshienk answered 3/5, 2012 at 19:55 Comment(8)
What happens if you remove the line with the mobile UA sniffing?Transfix
@GGG The trails become more pronounced (i.e., a little thicker)Weinshienk
This looks like a bug to me. As if the browsers optimize a little too much when trying to avoid unneccessary redraws. I would suggest having talkback to any of the browser developers. Interesting thought that it happens to all of them.Irmairme
@JonasWielicki - Is there a reference somewhere that all of these mobile browsers share some common code? It's a smell to me that the behavior is exactly the same across every device on which I've gotten my paws.Weinshienk
@Chris, random workaround idea... what happens if you pad the outside edges of the image with a few transparent (or nearly transparent) pixels?Transfix
@Chris I don't think so, but I am no expert on mobile devices. But at least on desktop machines the rendering engines (Mozilla, WebKit and this Opera-engine) have a quite different history and do not have a lot in common (even quite different licenses, MPL vs. GPL etc.).Irmairme
@GGG - It seems like chasing the "why" of this any farther is going to require a second posting. So, having come up with a viable workaround for the "what," if you'll post your transparency-padding suggestion in the form of an answer, I'll gladly accept.Weinshienk
@Chris I'm interested—did you file the bugreport and if, can you provide a link?Irmairme
T
3

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.

Transfix answered 8/5, 2012 at 21:0 Comment(1)
Thanks! Great advice, and clever fix. Now I just have to find the appropriate project to which I should file the bug report!Weinshienk

© 2022 - 2024 — McMap. All rights reserved.