如何在 Kineticjs 中获得 textBaseline="alphabetic" 行为?

How can I get textBaseline="alphabetic" behavior in Kineticjs?

我正在尝试以 canvas context textBaseline 属性 设置为 "alphabetic" 的方式对齐文本。我无法使用 kineticjs 获得完全相同的效果。

var letters = [
    { symbol: "A", x: 3.0, size: 20 },
    { symbol: "B", x: 36.3, size: 30 },
    { symbol: "C", x: 86.3, size: 40 },
    { symbol: "D", x: 158.6, size: 50 },
    { symbol: "E", x: 248.9, size: 40 },
    { symbol: "F", x: 315.5, size: 30 },
    { symbol: "G", x: 361.3, size: 20 } ];
// How kineticjs renders the text
(function actual() {
    var stage = new Kinetic.Stage({ container: "mycontainer",  width: 400,  height: 100 }),
  layer = new Kinetic.Layer(),
        baseline = 60;
 letters.forEach(function(letter) { 
     layer.add(new Kinetic.Text({
         x: letter.x,
         y: baseline - letter.size,
         text: letter.symbol,
         fontSize: letter.size,         
          fill: 'black',
      }));     
  });
    // Baseline visualization
  layer.add(new Kinetic.Line({
     points: [0, baseline, 400, baseline ],
     stroke: "red"
  }));
  stage.add(layer);
})();
// How I would like it to render the text
(function expected() {
    var c = document.getElementById("mycanvas"),
     ctx = c.getContext("2d"),
        baseline = 60;
   ctx.textBaseline = "alphabetic"; // redundant as it's actually default behaviour  
  letters.forEach(function(letter) {
        ctx.font = letter.size + "px Arial";
        ctx.fillText(letter.symbol, letter.x, baseline); 
    });    
    // Baseline visualization
    ctx.strokeStyle = "red";
  ctx.moveTo(0, baseline);
  ctx.lineTo(400, baseline);
  ctx.stroke();
})();
<script src="https://cdn.lukej.me/kineticjs/5.1.0/kinetic.min.js"></script>

<div id="mycontainer"></div>

<canvas id="mycanvas" width="400" height="150" style="position: absolute; left: 10px; top: 100px">

jsfiddle 上的相同代码。

我知道 this question 但是我还没有找到计算字母基线偏移量的正确方法。

正确的"y"位置可以计算如下:

y = baseline - fontSize + descent;

其中 "descent" 可以使用来自 this answer

的 "getTextHeight" 函数获得

下面的代码在 Firefox 中给出了像素完美的结果,在 Chrome 中给出了不错的结果。

var letters = [
    { symbol: "A", x: 3.0, size: 20 },
    { symbol: "B", x: 36.3, size: 30 },
    { symbol: "C", x: 86.3, size: 40 },
    { symbol: "D", x: 158.6, size: 50 },
    { symbol: "E", x: 248.9, size: 40 },
    { symbol: "F", x: 315.5, size: 30 },
    { symbol: "G", x: 361.3, size: 20 } ];
// How kineticjs renders the text
(function actual() {
    var stage = new Kinetic.Stage({ container: "mycontainer",  width: 400,  height: 100 }),
  layer = new Kinetic.Layer(),
        baseline = 60;
 letters.forEach(function(letter) { 
     layer.add(new Kinetic.Text({
         x: letter.x,
         y: baseline - letter.size + getMetrics("Arial", letter.size).descent,
         text: letter.symbol,
         fontSize: letter.size,         
          fill: 'black',
      }));     
  });
    // Baseline visualization
  layer.add(new Kinetic.Line({
     points: [0, baseline, 400, baseline ],
     stroke: "red"
  }));
  stage.add(layer);
    function getMetrics(fontFamily, fontSize) { 
        var $text = $("<span>Hg</span>").css({  
            "fontFamily": fontFamily,  
            "font-size": fontSize + "px", 
            "line-height": "normal" 
        }); 
        var $block = $("<div></div>").css({ 
            "display": "inline-block", 
            "width": "1px", 
            "height": "0px" 
        }); 
        var $div = $("<div></div>"); 
        $div.append($text, $block); 
        $("body").append($div); 
        try { 
            var result = {}; 
            $block.css({ verticalAlign: "baseline" }); 
            result.ascent = $block.offset().top - $text.offset().top; 
            $block.css({ verticalAlign: "bottom" }); 
            result.height = $block.offset().top - $text.offset().top; 
            result.descent = result.height - result.ascent; 
        } finally { 
            $div.remove(); 
        } 
        return result; 
 }; 
})();
// How I would like it to render the text
(function expected() {
    var c = document.getElementById("mycanvas"),
     ctx = c.getContext("2d"),
        baseline = 60;
   ctx.textBaseline = "alphabetic"; // redundant as it's actually default behaviour  
  letters.forEach(function(letter) {
        ctx.font = letter.size + "px Arial";
        ctx.fillText(letter.symbol, letter.x, baseline); 
    });    
    // Baseline visualization
    ctx.strokeStyle = "red";
  ctx.moveTo(0, baseline);
  ctx.lineTo(400, baseline);
  ctx.stroke();
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.lukej.me/kineticjs/5.1.0/kinetic.min.js"></script>

<div id="mycontainer"></div>

<canvas id="mycanvas" width="400" height="150" style="position: absolute; left: 10px; top: 100px">