Has anyone ever tried to create a custom shape class for Fabric.js that is a combination of shapes? I want to create something like a ruler, where the left and right ends are vertical lines and is attached to the middle, |---| . I'd also like to have a text box follow along in the center of this ruler. The ends should also have the ability to reposition. I've done this without a class, but it won't save or load, since I made my version from multiple shapes. Anyone have any thoughts if it's possible to create a custom shape like this?
Below is an example of some code that I pulled together from another example I found a while ago. 3 lines are created to make up my ruler. If I export the canvas data I'll get data to recreate as 3 lines, so if I clear the canvas and reload with this data, I'll get my 3 lines, but they will be independent 3 lines. I guess I need to figure out how to save their relationship so they can retain their ruler behavior when reloaded. I was thinking the best approach would be to create a custom shape with a type of "ruler".
window.onload = function() {
var canvas;
var started = false;
var mode = 0;
var lastX = 0;
var lastY = 0;
canvas = new fabric.Canvas('c');
canvas.isDrawingMode = false;
canvas.isTouchSupported = true;
canvas.selection = false;
canvas.on("mouse:down", function(option) {
if (mode === 1) {
var mouse = canvas.getPointer(option.e);
lastX = mouse.x;
lastY = mouse.y;
started = true;
var ln = new fabric.Line([lastX, lastY, lastX, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
var rAnchor = new fabric.Line([lastX - 20, lastY, lastX + 20, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
var tip = new fabric.Line([lastX - 20, lastY, lastX + 20, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
tip.line = ln;
rAnchor.line = ln;
ln.tip = tip;
ln.rAnchor = rAnchor;
canvas.add(ln);
canvas.add(rAnchor);
canvas.add(tip);
canvas.setActiveObject(ln);
canvas.renderAll();
}
});
canvas.on("mouse:move", function(e) {
if (!started) {
return false;
}
var mouse = canvas.getPointer(e.e);
var line = canvas.getActiveObject();
line.set('x2', mouse.x).set('y2', mouse.y);
var tip = line.get('tip');
tip.set('left', mouse.x).set('top', mouse.y);
var rAnchor = line.get('rAnchor');
// Get the angle for pointing the tip at the mouse location.
var rad = Math.atan2((line.y1 - line.y2), (line.x1 - line.x2));
var ang = rad * (180 / Math.PI);
tip.set('angle', (ang - 90));
rAnchor.set('angle', (ang - 90));
line.setCoords();
tip.setCoords();
rAnchor.setCoords();
canvas.renderAll();
});
canvas.on("mouse:up", function() {
if (started) {
started = false;
}
canvas.discardActiveObject();
canvas.renderAll();
});
// Event Handler for Drawn Object move
canvas.on('object:moving', function(e) {
var p = e.target;
// move line
if (p.tip) {
var oldCenterX = (p.x1 + p.x2) / 2;
var oldCenterY = (p.y1 + p.y2) / 2;
var deltaX = p.left - oldCenterX;
var deltaY = p.top - oldCenterY;
p.tip.set({
'left': p.x2 + deltaX,
'top': p.y2 + deltaY
}).setCoords();
p.rAnchor.set({
'left': p.x1 + deltaX,
'top': p.y1 + deltaY
});
p.set({
'x1': p.x1 + deltaX,
'y1': p.y1 + deltaY
});
p.set({
'x2': p.x2 + deltaX,
'y2': p.y2 + deltaY
});
p.set({
'left': (p.x1 + p.x2) / 2,
'top': (p.y1 + p.y2) / 2
});
}
// move tip
if (p.line) {
var tip = p.line.tip;
var rAnchor = p.line.rAnchor;
p.line.set({
'x2': tip.left,
'y2': tip.top,
'x1': rAnchor.left,
'y1': rAnchor.top
});
// Get the angle for pointing the tip at the mouse location.
var rad = Math.atan2((p.line.y1 - p.line.y2), (p.line.x1 - p.line.x2));
var ang = rad * (180 / Math.PI);
tip.set('angle', (ang - 90));
rAnchor.set('angle', (ang - 90));
tip.setCoords();
rAnchor.setCoords();
p.line.setCoords();
}
canvas.renderAll();
});
// Button Event Handlers
document.getElementById("btnEdit").addEventListener("click", function(e) {
mode = 0;
});
document.getElementById("btnArrow").addEventListener("click", function(e) {
mode = 1;
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas style="border: 2px solid; " height="500" width="600" id="c"></canvas>
<button id="btnEdit">Edit</button>
<button id="btnArrow">Ruler</button>