Safari3 に HTML5::Canvas の Text API を実装した

WebKit530未満(Safari3.x)には、Text API(fillText, strokeText, measureText) が実装されていません。
また、window.CanvasRenderingContext2D が存在しないため、prototype ベースでの機能拡張ができません。

uuCanvas.js では、getContext() をラップすることで Safari3.x に Text API を実装しました。


uuCanvas.js により拡張される機能

  • Text API(fillText(), strokeText(), measureText()) をサポートします。
  • Text Shadow プロパティ(shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY) をサポートします。
  • Text プロパティ(font, textAlign, textBaseline) をサポートします。

制限事項

  • Firefox2, Opera9.2 と同じく DOM による擬似的な実装です。ピクセル操作やCanvasのコピーには対応しません。
  • maxLength は非サポートです。
  • Matrix変換による移動,回転,変形は非サポートです。
  • textBaseline には "top" のみ指定可能です。
  • canvas.width, canvas.height を動的に変更するとサーフェイスの初期化を行う必要があるのですが、こちらはフックできないので、width, height を変更した場合は、clearRect を呼び出す必要があります。

これで

Safari3.x に Text API を実装できたので、全てのモダンブラウザ(IE6+, Safari3.1+, Opera9.2+, Firefox2+, Google Chrome2+)で Canvas Text API がそれなりに利用可能になります。

追加したコード

追加実装したコード片はこんな感じになります。

// 初期化処理
          if (_mm.enginever < 530) { // Safari3.x
            var nodes = _doc.getElementsByTagName("canvas"), v, i = 0;
            while ( (v = nodes[i++]) ) {
              initOldWebKitCanvas(v);
            }
          }

// getContext のラッパー

function initOldWebKitCanvas(node, type) {
  if (!node._getContext) {
    node._getContext = node.getContext; // keep original method
    // wrapper
    node.getContext = function(type) {
      var ctx = node._getContext(type || "2d");
      return ctx._stack ? ctx
                        : _canvas.extend.textAPI(ctx);
    };
  }
  return node;
}

// 動的に生成した canvas オブジェクトの初期化

  // uuCanvas.init - initialize a canvas made dynamically
  init: function(canvas, // @param Node: canvas element
                 vml) {  // @param Boolean(= false): true = force VML
                         // @return Node: new canvas
    if (_mm.webkit && _mm.enginever < 530) {
      return initOldWebKitCanvas(canvas);
    }
    return canvas.getContext ? canvas // already initialized
                             : (vml || !_mm.slver) ? VMLInit(canvas)
                                                   : SLInit(canvas);
  },

// 実体

// === Extend Text API for old WebKit ======================
_extend.textAPI =
    function(ctx) { // @param CanvasRenderingContext2D:
                    // @return CanvasRenderingContext2D:
  ctx._stack = [];
  ctx._save = ctx.save;
  ctx._restore = ctx.restore;
  ctx._clearRect = ctx.clearRect;
  ctx.font = "10px sans-serif";
  ctx.textAlign = "start";
  ctx.textBaseline = "top"; // spec: "alphabetic"
  ctx.xMissColor = "#000";
  ctx.xTextMarginTop = 1.3;

  ctx.save = function() {
    this._stack.push([ctx[FONT], ctx[TEXT_ALIGN], ctx[TEXT_BASELINE]]);
    this._save();
  };
  ctx.restore = function() {
    ctx._restore();
    if (ctx._stack.length) {
      var last = ctx._stack.pop();
      ctx[FONT] = last[0];
      ctx[TEXT_ALIGN] = last[1];
      ctx[TEXT_BASELINE] = last[2];
    }
  };
  ctx.clearRect = function(x, y, w, h) {
    clearRectDOM(ctx, x, y, w, h);
  };
  ctx.fillText = function(text, x, y, maxWidth, wire) {
    fillTextDOM(ctx, text, x, y, maxWidth, wire);
  };
  ctx.strokeText = function(text, x, y, maxWidth) {
    fillTextDOM(ctx, text, x, y, maxWidth, 1);
  };
  ctx.measureText = function(text) {
    var metric = _extend.getTextMetric(text, ctx[FONT]);
    return new _extend.TextMetrics(metric.w, metric.h);
  }
  return ctx;
};