Text on a Path

by Steve Eberhardt

HTML

<script src="https://unpkg.com/[email protected]/dist/fabric.min.js"></script>
<canvas id="c" width="600" height="300"></canvas>
<div>
  <div>
    <input type="text" id="textInput" value="" placeholder="Enter text here">
  </div>
  <div>
    <span>Start Offset: </span>
    <input type="range" id="startOffsetInput" step="0.25" min="-50" max="50">
    <h2 id="offsetAmount">0</h2>
  </div>
  <div>
    <span>Character Spacing: </span>
    <input type="range" id="charSpacingInput" step="1" min="-100" max="200">
    <h2 id="spacingAmount">0</h2>
  </div>
  <div>
    <span>Font Size: </span>
    <input type="range" id="fontSizeInput" step="1" min="10" max="100">
    <h2 id="fontSize">0</h2>
  </div>
  <div>
    <span>Text Align: </span>
    <button onclick="setProp('textAlign','left')">
      Left
    </button>
    <button onclick="setProp('textAlign','center')">
      Center
    </button>
    <button onclick="setProp('textAlign','right')">
      Right
    </button>
  </div>
  <div>
    <span>Side: </span>
    <button onclick="setProp('pathSide','left')">
      Left
    </button>
    <button onclick="setProp('pathSide','right')">
      Right
    </button>
  </div>
</div>

CSS

canvas {
    border: 1px solid #999;
}

div > * {
  display: inline-block;
}

h2 {
  width: 30px;
  margin: 0 20px 0 0;
}

div > div {
  display: block;
  padding: 4px;
}

input {
  margin-top: 10px;
}

JavaScript

var canvas = new fabric.Canvas('c');

fabric.Object.prototype.noScaleCache = false;

var pathString = "M 75 0 A 10 10 0 0 1 75 -150 A 1 1 0 0 1 75 0 Z";
var path = new fabric.Path(pathString, {
  strokeWidth: 2,
  stroke: 'red',
  left: 100,
  top: 50,
  fill: 'transparent',
  scaleX: 1,
  strokeUniform: true
});

var path2 = new fabric.Path('M 0 0 Q 180 0 180 -101.25 Q 180 -180 90 -180 Q 0 -180 0 -112.5 Q 0 -45 78.75 -45 Q 135 -45 146.25 -90', {
  strokeWidth: 2,
  stroke: 'red',
  left: 300,
  top: 50,
  fill: 'transparent'
});

var text = new fabric.Text('Text on a Path', {
  path: path,
  fontSize: 30,
  left: 50,
  top: 50,
  textAlign: 'center',
  pathStartOffset: 0,
  pathSide: 'left'
});

var text2 = new fabric.Text('Text on a Path', {
  path: path2,
  fontSize: 30,
  left: 300,
  top: 50,
  pathStartOffset: 0,
  textAlign: 'center',
  pathSide: 'left'
});

canvas.add(text);
canvas.add(text2);

document.getElementById('startOffsetInput').addEventListener("input", function() {
  setProp('pathStartOffset', parseInt(this.value));
  document.getElementById('offsetAmount').innerHTML = this.value;
});

document.getElementById('charSpacingInput').addEventListener("input", function() {
  setProp('charSpacing', parseInt(this.value));
  document.getElementById('spacingAmount').innerHTML = this.value;
});

document.getElementById('fontSizeInput').addEventListener("input", function() {
  setProp('fontSize', parseInt(this.value));
  document.getElementById('fontSize').innerHTML = this.value;
});

document.getElementById('textInput').addEventListener("input", function() {
  setProp('text', this.value);
});

function setProp(prop, value) {
  var objects = canvas.getObjects();
  objects.forEach(function(obj) {
    obj.set(prop, value);
    canvas.renderAll();
  });
}

canvas.on({
  'object:scaling': function(e) {
    applyScaling(e.target);
  }
})

function applyScaling(obj) {
  if (obj.type === "text" && obj.path) {
    path = obj.path;
    path.set({
      scaleX:...