Hatena::ブログ(Diary)

latest log このページをアンテナに追加 RSSフィード

2009-07-01

Firefox3.5 の Canvas の描画結果が他のブラウザと異なる

ついったーだと長すぎたので、こっちに書きます。

http://uupaa-js-spinoff.googlecode.com/svn/trunk/uuCanvas.js/demo1/5_1_canvas_savestate+shadow.html

の結果が他のブラウザと異なります。

  1. ctx.globalCompositeOperation = "destination-over""source-out"
  2. ctx.fillText()

で既に描画されている領域がクリアされてしまうようです。

f:id:uupaa:20090701170045p:image

      <canvas id="canvas" width="300" height="300"></canvas>
      <canvas id="vmlcanvas" class="vml" width="300" height="300"></canvas>

    <script>
      function boot() {
        draw(document.getElementById('canvas').getContext('2d'));
        draw(document.getElementById('vmlcanvas').getContext('2d'));
      }
      function draw(ctx) {
        ctx.textBaseline = "top";
        ctx.font = "16pt Arial";
        ctx.shadowColor = "red";
        ctx.shadowOffsetX = 10;
        ctx.shadowOffsetY = 10;
        ctx.shadowBlur = 10;
        ctx.fillText("red shadow", 80, 20);
        ctx.save();
        ctx.textAlign = "center";
        ctx.font = "20pt 'Arial Black'";
        ctx.fillStyle = "#09F";
        ctx.shadowColor = "green";
        ctx.shadowOffsetX = 5;
        ctx.shadowOffsetY = 5;
        ctx.shadowBlur = 5;
        ctx.fillText("green shadow", 120, 60);

        ctx.save();
        ctx.textAlign = "end";
        ctx.font = "24pt 'Times New Roman'";
        ctx.globalAlpha = 0.5;
        ctx.globalCompositeOperation = "source-out";
        ctx.fillStyle = "#eee";
        ctx.shadowColor = "blue";
        ctx.shadowOffsetX = 3;
        ctx.shadowOffsetY = -1;
        ctx.shadowBlur =  0;
        ctx.fillText("blue shadow", 220, 100); // ここで消える
        ctx.restore();

        ctx.fillText("green shadow", 120, 140);
        ctx.restore();

        ctx.fillText("red shadow", 80, 180);
      }
    </script>

他にもまだあるかもしれません。

の描画結果がおかしくなっているようでしたら教えてください。 @uupaa

関数の初期化とCallオブジェクト

大事なことなので、記憶を裏打ちしてみる。

<!doctype html><html><head><title></title></head><body><script>

function fn() {
  alert(typeof a + ", " + a); // undefined, undefined
  alert(typeof b + ", " + b); // function, function() { alert("b2"); }
  b(); // alert("b2");
  try {
    alert(typeof c + ", " + c);
  } catch(err) { alert("c was not found"); }

  function b() {
    alert("b3");
  }

  function b() { // duplicity
    alert("b2");
  }

  var a = 3,
      b = function() { alert("b1"); }; // overwrite b

  alert(typeof a + ", " + a); // number, 3
  alert(typeof b + ", " + b); // function, function() { alert("b1"); }
  b(); // alert("b1");

  try {
    delete b;
  } catch(err) { alert("delete b; fail"); }

  return b;
}

alert("return " + fn()); // return function() { alert("b1"); }

</script></body></html>
  1. fn() を実行すると、Callオブジェクトが作成される
  2. 関数内の関数を全て検索しリストを作成する。リストには function b() { alert("b2)"; } が登録される。function b() { alert("b3"); } は b2 により存在が上書される。
  3. 関数内のvar文を全て検索しリストを作成する。リストには a, b が登録される
  4. Callオブジェクトに a と b が登録される。this や arguments も登録される
  5. Callオブジェクトの各プロパティが以下の値で初期化される
  6. var a = 3; の行に到達すると a は 3 で初期化される。それまでは undefined で初期化されている
  7. var b = function() { alert("b1"); } の行に到達すると、Call.b が 上書される
  8. Call.c は宣言されておらず、Globalオブジェクトにも存在しないため、参照エラーが発生する
  9. delete b; で Call.b は消せない。例外も発生しない(暗黙)
  10. return b; が返すのは function() { alert("b1") } オブジェクト関数を toString() した値が最後に表示される。

同じ関数が存在した場合は上書されてしまうから…

<!doctype html><html><head><title></title></head><body><script>

function fn(a) {
  if (a === 1) {
    function b() { alert("a === 1"); }
  } else {
    function b() { alert("a !== 1"); }
  }
  return b;
}

fn(1)();

</script></body></html>

これは、 alert("a !== 1") が実行される。



記憶を頼りにイメージで書いてるから、初期化処理の正確な手順は ECMAScript仕様書を見たほうがいいよ(Execution Contexts のとこ)

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/ (ECMAScript3 日本語訳)