1

I'm working on an HTML5 canvas app that contains shapes that I fill with images. It's a framing tool, for building and previewing custom frames.

Everything works great on desktops/laptops, but when I switch to iOS devices, I'm getting the following strange behaviour.

Sometimes, a thin white line appears at the border where the image that I'm using to fill the shape repeats.

See the following JSFiddle: http://jsfiddle.net/3TVSy/3/

HTML:

<canvas id="canvas2" width="100" height="500" style="vertical-align:top"></canvas>
<canvas id="canvas1" width="300" height="2000" style="vertical-align:top"></canvas>

​ Javascript:

jQuery(function($){
    var $canvas = $('#canvas1'),
        $canvas2 = $('#canvas2'),
        scale = {scale: 0.21688837478311163, x: 0, y: 0},
        side = {
            fillStyle: '#222',
            x1: 0,
            x2: 266,
            x3: 266,
            x4: 0,
            y1: -4.610666666666667,
            y2: 261.38933333333335,
            y3: 1538.898666666667,
            y4: 1804.898666666667,
        }
    ;

    $canvas.createPattern({
        source: "http://vps5960.inmotionhosting.com/~romamo5/wp-content/uploads/2012/12/286-ft.jpg",
        repeat: "repeat-y",
        // Draw frame when pattern loads
        load: function(ptrn){
            side.fillStyle = ptrn;
            $canvas.clearCanvas()
                .drawLine(side)
            ;
            $canvas2.clearCanvas()
                .scaleCanvas(scale)
                .drawLine(side)
            ;
    }
});    

});​

The fiddle has two versions of the canvas. The larger one is not scaled, and the smaller one IS scaled. The artifact only appears on the smaller one (and only on actual iOS devices, not the simulator). You can see the thin white line on the smaller canvas about 90% of the way down.

I have to scale the image, so not scaling is not an option. I've also noticed that if I round the scale factor in the fiddle to just three decimal places, then the artifact disappears. Problem is, if I do that rounding, my frame tool really starts to mess things up on subsequent sides, so rounding also really isn't an option.

I'm using jCanvas to do the drawing, but I don't suspect it's the problem. It's just a wrapper to all of the native canvas methods. (Edit: confirmed, the artifact appears even if not using jCanvas - see http://jsfiddle.net/3TVSy/5/)

Has anyone else seen/solved this problem? Is this a bug in the iOS browser?

4

1 に答える 1

1

iOS6 のサファリでも同じ問題が発生しました。繰り返される画像の間にギャップが表示されます。この問題は、パターン画像の幅/高さ (元の画像サイズではなく、スケールを適用した後のサイズ) が 10 進数で、その最初の小数点以下の桁数が 5 未満、つまり [X. 0 < n < X.5]

解決策はありませんが、次のロジックで問題を検出できます。

var scaledWidth = img.width * scale,
    scaledHeight = img.height * scale,
    decimalPartOfWidth = scaledWidth - (scaledWidth | 0),
    decimalPartOfHeight = scaledHeight - (scaledHeight | 0);

if ((decimalPartOfWidth > 0 && decimalPartOfWidth < 0.5)
    ||
    (decimalPartOfHeight > 0 && decimalPartOfHeight < 0.5)) {
  ; // The issue will occur.
} else {
  ; // The issue will not occur.
}

以下に貼り付けたのは、問題を再現するために使用したコードです。

<!DOCTYPE html>
<html>
  <body>
    <div id="status"></div>
    <div>
      <canvas width="480px" height="400px"></canvas>
    </div>
    <script>
      var canvas = document.getElementsByTagName('canvas')[0],
          ctx = canvas.getContext('2d'),
          stats = document.getElementById('status'),
          img = new Image(),
          doDrawImage = function (pScale) {
            var txt = 'scale = ' + pScale + ', w = ' + (img.width * pScale);
            stats.innerHTML = txt;
            ctx.save();
            ctx.transform(pScale, 0, 0, pScale, 0, 0);
            ctx.fillStyle = ctx.createPattern(img, 'repeat');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.beginPath();
            ctx.rect(0, 0, canvas.width, canvas.height);
            ctx.fill();
            ctx.restore();
            console.log(txt + ' : ' + canvas.toDataURL());
          };

      img.src = 'pattern.png';
      img.onload = function () {
        var scale = 1.00,
            onTimeout = function () {
              if (scale < 2.00) {
                doDrawImage(scale);
                scale += 0.01;
                setTimeout(onTimeout, 1000);
              }
            };
        onTimeout();
      };
    </script>
  </body>
</html>

IMO 最善の解決策は、背景色をパターン画像と同じ色に設定することです;p

于 2013-12-05T03:55:14.717 に答える