Fabric.js元素中的曲线文本

3

如何在Fabric.js元素的上下文中应用“HTML5 Canvas Text Along Arc Path”?

http://www.html5canvastutorials.com/labs/html5-canvas-text-along-arc-path/ 使用上述链接,我们可以显示弯曲的文本,我该如何在Fabric.js中实现?

JS fiddle :: http://jsfiddle.net/E5vnU/

      function drawTextAlongArc(context, str, centerX, centerY, radius, angle) {
    var len = str.length, s;
    context.save();
    context.translate(centerX, centerY);
    context.rotate(-1 * angle / 2);
    context.rotate(-1 * (angle / len) / 2);
    for(var n = 0; n < len; n++) {
      context.rotate(angle / len);
      context.save();
      context.translate(0, -1 * radius);
      s = str[n];
      context.fillText(s, 0, 0);
      context.restore();
    }
    context.restore();
  }
  var canvas = document.getElementById('myCanvas'), 
    context = canvas.getContext('2d'),
    centerX = canvas.width / 2,
    centerY = canvas.height - 30,
    angle = Math.PI * 0.8,
    radius = 150;

  context.font = '30pt Calibri';
  context.textAlign = 'center';
  context.fillStyle = 'blue';
  context.strokeStyle = 'blue';
  context.lineWidth = 4;
  drawTextAlongArc(context, 'Text along arc path', centerX, centerY, radius, angle);

  // draw circle underneath text
  context.arc(centerX, centerY, radius - 10, 0, 2 * Math.PI, false);
  context.stroke();

1
这可能是你正在寻找的 http://jsfiddle.net/NHs8t/ 这不是我的代码,我从Github讨论页面得到的,看看吧.. - Prasanna Aarthi
1
@PrasannaAarthi 我正在尝试更新 fiddle http://jsfiddle.net/NHs8t/7/,以添加多个文本,并且如果选择了对象,则更改所选对象的文本,否则添加新文本。但是在此方面需要一些帮助。 - anam
3个回答

9

这是我实现的弧形文本(沿着圆形路径绘制的文本)。 https://jsfiddle.net/av01d/4p0syzw3/

(function(fabric) {

/*
 * CurvedText object for fabric.js
 * @author Arjan Haverkamp (av01d)
 * @date January 2018
 */

fabric.CurvedText = fabric.util.createClass(fabric.Object, {
   type: 'curved-text',
   diameter: 250,
   kerning: 0,
   text: '',
   flipped: false,
   fill: '#000',
   fontFamily: 'Times New Roman',
   fontSize: 24, // in px
   fontWeight: 'normal',
   fontStyle: '', // "normal", "italic" or "oblique".
    cacheProperties: fabric.Object.prototype.cacheProperties.concat('diameter', 'kerning', 'flipped', 'fill', 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'strokeStyle', 'strokeWidth'),
   strokeStyle: null,
   strokeWidth: 0,

   initialize: function(text, options) {
      options || (options = {});
      this.text = text;

      this.callSuper('initialize', options);
      this.set('lockUniScaling', true);

      // Draw curved text here initially too, while we need to know the width and height.
      var canvas = this.getCircularText();
      this.cropCanvas(canvas);
      this.set('width', canvas.width);
      this.set('height', canvas.height);
   },

   _getFontDeclaration: function()
   {
      return [
         // node-canvas needs "weight style", while browsers need "style weight"
         (fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
         (fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
         this.fontSize + 'px',
         (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
      ].join(' ');
   },

   cropCanvas: function(canvas)
   {
      var ctx = canvas.getContext('2d'),
         w = canvas.width,
         h = canvas.height,
         pix = {x:[], y:[]}, n,
         imageData = ctx.getImageData(0,0,w,h),
         fn = function(a,b) { return a-b };

      for (var y = 0; y < h; y++) {
         for (var x = 0; x < w; x++) {
            if (imageData.data[((y * w + x) * 4)+3] > 0) {
               pix.x.push(x);
               pix.y.push(y);
            }
         }
      }
      pix.x.sort(fn);
      pix.y.sort(fn);
      n = pix.x.length-1;

      w = pix.x[n] - pix.x[0];
      h = pix.y[n] - pix.y[0];
      var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);

      canvas.width = w;
      canvas.height = h;
      ctx.putImageData(cut, 0, 0);
   },

   // Source: http://jsfiddle.net/rbdszxjv/
   getCircularText: function()
   {
      var text = this.text,
         diameter = this.diameter,
         flipped = this.flipped,
         kerning = this.kerning,
         fill = this.fill,
         inwardFacing = true,
         startAngle = 0,
         canvas = fabric.util.createCanvasElement(),
         ctx = canvas.getContext('2d'),
         cw, // character-width
         x, // iterator
         clockwise = -1; // draw clockwise for aligned right. Else Anticlockwise

      if (flipped) {
         startAngle = 180;
         inwardFacing = false;
      }

      startAngle *= Math.PI / 180; // convert to radians

      // Calc heigt of text in selected font:
      var d = document.createElement('div');
      d.style.fontFamily = this.fontFamily;
      d.style.fontSize = this.fontSize + 'px';
      d.style.fontWeight = this.fontWeight;
      d.style.fontStyle = this.fontStyle;
      d.textContent = text;
      document.body.appendChild(d);
      var textHeight = d.offsetHeight;
      document.body.removeChild(d);

      canvas.width = canvas.height = diameter;
      ctx.font = this._getFontDeclaration();

      // Reverse letters for center inward.
      if (inwardFacing) { text = text.split('').reverse().join('') };

      // Setup letters and positioning
      ctx.translate(diameter / 2, diameter / 2); // Move to center
      startAngle += (Math.PI * !inwardFacing); // Rotate 180 if outward
      ctx.textBaseline = 'middle'; // Ensure we draw in exact center
      ctx.textAlign = 'center'; // Ensure we draw in exact center

      // rotate 50% of total angle for center alignment
      for (x = 0; x < text.length; x++) {
         cw = ctx.measureText(text[x]).width;
         startAngle += ((cw + (x == text.length-1 ? 0 : kerning)) / (diameter / 2 - textHeight)) / 2 * -clockwise;
      }

      // Phew... now rotate into final start position
      ctx.rotate(startAngle);

      // Now for the fun bit: draw, rotate, and repeat
      for (x = 0; x < text.length; x++) {
         cw = ctx.measureText(text[x]).width; // half letter
         // rotate half letter
         ctx.rotate((cw/2) / (diameter / 2 - textHeight) * clockwise);
         // draw the character at "top" or "bottom"
         // depending on inward or outward facing

         // Stroke
         if (this.strokeStyle && this.strokeWidth) {
            ctx.strokeStyle = this.strokeStyle;
            ctx.lineWidth = this.strokeWidth;
            ctx.miterLimit = 2;
            ctx.strokeText(text[x], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));
         }

         // Actual text
         ctx.fillStyle = fill;
         ctx.fillText(text[x], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));

         ctx.rotate((cw/2 + kerning) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
      }
      return canvas;
   },

   _set: function(key, value) {
      switch(key) {
         case 'scaleX':
            this.fontSize *= value;
            this.diameter *= value;
            this.width *= value;
            this.scaleX = 1;
            if (this.width < 1) { this.width = 1; }
            break;

         case 'scaleY':
            this.height *= value;
            this.scaleY = 1;
            if (this.height < 1) { this.height = 1; }
            break;

         default:
            this.callSuper('_set', key, value);
            break;
      }
   },

   _render: function(ctx)
   {
      var canvas = this.getCircularText();
      this.cropCanvas(canvas);

      this.set('width', canvas.width);
      this.set('height', canvas.height);

      ctx.drawImage(canvas, -this.width / 2, -this.height / 2, this.width, this.height);

      this.setCoords();
   },

   toObject: function(propertiesToInclude) {
      return this.callSuper('toObject', ['text', 'diameter', 'kerning', 'flipped', 'fill', 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'strokeStyle', 'strokeWidth'].concat(propertiesToInclude));
   }
});

fabric.CurvedText.fromObject = function(object, callback, forceAsync) {
   return fabric.Object._fromObject('CurvedText', object, callback, forceAsync, 'curved-text');
};

})(typeof fabric !== 'undefined' ? fabric : require('fabric').fabric);

请问您如何使用tosvg()函数来处理这个curvedText?因为它给我返回了未定义的函数。 - sanjay rathod
还没有实现。我对SVG不够熟悉,无法实现这个功能。 - av01d
嘿@av01d,你能告诉我如何将图像模式填充到这个曲线文本元素中吗?下面的代码我已经在你的jsfiddle上检查过了,但它不起作用。提前致谢。 fabric.util.loadImage(url, function(img) { const objects = canvas.getObjects(); objects.forEach(obj=>{ if(!obj.elemType){ obj.set('fill', new fabric.Pattern({ source: img, repeat: 'repeat' })); } }) canvas.renderAll(); }); - Jitendra Mohanta
从JSON加载后,我得到了这个错误->“Uncaught TypeError:text.split不是函数,位于i.getCircularText”,如何解决?@av01d - Rohan Ashik

8

我正在尝试使用spectrum插件更改曲线文本的颜色。只有在我更改半径和间距时,颜色才会反映出来。可能是什么问题,在fiddle中附加了spectrum更改的代码 http://jsfiddle.net/qpJTz/18/ - anam
目前只适用于SET,我正在研究问题所在,如果您将代码发布到GitHub网站上,我可以更深入地了解。 - Fabrizio
我检查了你的插件,它被设计用来设置半径、间距等等。但是你能否更新它以设置填充颜色? - anam
你想要一个这样的例子吗?实际上,只要使用 set,您可以应用于文本的所有属性也将适用于曲线文本。在您的情况下,要使用填充颜色,请使用 curvedText.setFill('#EFEFEF'); - Fabrizio
如果我尝试改变颜色,只有在改变半径和间距后才会反映出来。在您的插件中添加一些更改 _dimensionAffectingProps['fill'] = true; 后,它会立即反映出来,我很快会在 GitHub 上提供代码片段。 - anam
演示不起作用,这个曲线文本在SVG中支持还是只支持画布? - Umashankar

4

是否有计划支持更新的fabric.js版本,如1.7.x或2.x? - Andres

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接