FabricJS circular text
by Arjan Haverkamp
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<table>
<tr>
<td>Text</td>
<td><input type="text" id="text" value="Lorem ipsum di samet"></td>
</tr>
<tr>
<td>Diameter</td>
<td><input type="range" id="diameter" min="100" max="2500" value="250" step="1"></td></tr>
<tr>
<td>Font size</td>
<td><input type="range" id="fontSize" min="8" max="100" value="24" step="1"></td>
</tr>
<tr>
<td>Kerning</td>
<td><input type="range" id="kerning" min="-10" max="10" value="0" step="1"></td>
</tr>
<tr>
<td>Flip</td>
<td><input type="checkbox" id="flip" value="1"></td>
</tr>
<tr>
<td></td>
<td><button type="button" id="newObject">Add to Canvas</button></td>
</tr>
</table>
<hr>
<div id="canvasContainer">
<canvas id="c" width="600" height="600"></canvas>
</div>
CSS
body, input, button {
font: 12pt Arial, Helvetica;
}
#canvasContainer {
background-position: 0 0, 10px 10px;
background-size: 20px 20px;
background-image: linear-gradient(45deg, #e4ebf5 25%, transparent 25%, transparent 75%, #e4ebf5 75%, #e4ebf5 100%),linear-gradient(45deg, #e4ebf5 25%, white 25%, white 75%, #e4ebf5 75%, #e4ebf5 100%);
border: 2px solid #ccc;
display: inline-block;
}
input[type=range], input[type=text] {
width: 200px;
}
JavaScript
//
// Fabric Addons
//
/**
* Trim a canvas.
*
* @param {canvas} canvas A canvas element to trim. This element will be trimmed (reference)
* @param {int} threshold Alpha threshold. Allows for trimming semi-opaque pixels too. Range: 0 - 255
* @returns {Object} Width and height of trimmed canvcas and left-top coordinate of trimmed area. Example: {width:400, height:300, x:65, y:104}
* @see https://stackoverflow.com/a/69993651/3360038
*/
fabric.util.trimCanvas = (canvas, threshold) => {
threshold = threshold || 0;
const ctx = canvas.getContext('2d'),
w = canvas.width, h = canvas.height,
imageData = ctx.getImageData(0, 0, w, h),
tlCorner = { x:w+1, y:h+1 },
brCorner = { x:-1, y:-1 }
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
if (imageData.data[((y * w + x) * 4) + 3] > threshold) {
tlCorner.x = Math.min(x, tlCorner.x)
tlCorner.y = Math.min(y, tlCorner.y)
brCorner.x = Math.max(x, brCorner.x)
brCorner.y = Math.max(y, brCorner.y)
}
}
}
const cut = ctx.getImageData(tlCorner.x, tlCorner.y, brCorner.x - tlCorner.x, brCorner.y - tlCorner.y)
canvas.width = brCorner.x - tlCorner.x
canvas.height = brCorner.y - tlCorner.y
ctx.putImageData(cut, 0, 0)
return {width:canvas.width, height:canvas.height, x:tlCorner.x, y:tlCorner.y}
}
//
// Textcurved object
//
fabric.Textcurved = fabric.util.createClass(fabric.IText, {
type: 'textcurved',
cacheProperties: fabric.IText.prototype.cacheProperties.concat(
'diameter', 'kerning', 'flipped'
),
initialize: function(text, options) {
this.diameter = options?.diameter ?? 250
this.kerning = options?.kerning ?? 0
this.flipped = options?.flipped ?? false
this.set('lockScalingFlip',true)
this.setControlsVisibility({
mt: false, // middle top disable
mb: false, // middle bottom
ml: false, // middle left
mr: false, //...