Reputation: 13
var canvas = new fabric.Canvas('canvas', {width: 400, height: 400});
canvas.on('object:scaled', function (e) {
let type = e.target.get('type');
if (type == 'group') {
var triangleCount = 0, triangles = [];
//console.log(e.target);
e.target._objects.forEach(function (object) {
if (object.get('type') == 'triangle') {
triangles[triangleCount] = object;
triangleCount++;
let ratio = 1 / e.target.get('scaleY');
object.set('scaleY', ratio);
}
});
canvas.requestRenderAll();
}
});
function addArrow() {
var line = new fabric.Line([10, 10, 10, (10 + 50)], {
strokeUniform: true,
lockScalingX: true,
borderColor: 'transparent',
left: 10,
top: 10,
strokeWidth: 2,
stroke: '#000'
});
var triangleBottom = new fabric.Triangle({
width: 16, height: 16, fill: '#000',
scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, lockUniScaling: true, lockSkewingX: true, lockSkewingY: true, left: (10 - 8), top: (10 + 36)
});
triangleBottom.rotate(180);
var triangleTop = new fabric.Triangle({
opacity: 1, width: 16, height: 16, fill: '#000', scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, left: (10 - 8), top: (10 - 0)
});
//groupItems.push(triangleTop);
let groupItems = [line, triangleBottom, triangleTop];
var group = new fabric.Group(groupItems, {
hasControls: true,
left: 10,
top: 10,
strokeUniform: true,
lockScalingX: true
}).setControlsVisibility({
tl: false,
tr: false,
mt: true,
mb: true,
ml: false,
mr: false,
bl: false,
br: false
});
canvas.add(group);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.6/fabric.min.js"></script>
<button onclick="addArrow()">Add arrow</button>
<canvas id="canvas" class="canvas" style="border:1px solid #000;"></canvas>
I have a problem with fabric.js. I created a "group" containing line and 2 triangles. So it can be single our double headed arrow. Now what I want is to able to rotate and scale this. But here's the problem. I want those arrow heads to maintain same size and they are unfortunately also increasing/decreasing with scaling of the line.
I really have no idea how to fix it. Here are my attempts at it. Tried using static ratio since by default arrow is 50 height and triangle 16, tried some calculations. but they never same to make right heads. Also did attempt to delete and re-enter those triangles but this failed while rotating and resizing group.
Am I missing something obvious? Or it's that hard to prevent one element I group from scaling. Another thing I want to do is prevent scaling of text in rectangle, but I'm worried it will be same nightmare again
canvas.on('object:scaled', function (e) {
let type = e.target.get('type');
if (type == 'group') {
var triangleCount = 0, triangles = [];
e.target._objects.forEach(function (object) {
if (object.get('type') == 'triangle') {
triangles[triangleCount] = object;
triangleCount++;
let currentGroupHeight = e.target.get('scaleY') * e.target.get('height');
let ratio = object.get('height') * e.target.get('scaleY') / e.target.get('height') * e.target.get('scaleY');
//object.set('scaleY', 50 / 16);
object.set('scaleY', ratio);
} else if (object.get('type') == 'i-text') {
//canvas.remove(object);
object.set({ scaleX: 1, scaleY: 1 });
}
});
canvas.requestRenderAll();
}
});
function addArrow(options, doubleHeads) {
var line = new fabric.Line([options.e.layerX, options.e.layerY, options.e.layerX, (options.e.layerY + 50)], {
strokeUniform: true,
lockScalingX: true,
borderColor: 'transparent',
left: options.e.layerX,
top: options.e.layerY,
});
var triangleBottom = new fabric.Triangle({
width: 16, height: 16, fill: '#000',
scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, lockUniScaling: true, lockSkewingX: true, lockSkewingY: true, left: (options.e.layerX - 8), top: (options.e.layerY + 36)
});
triangleBottom.rotate(180);
let display = 0;
if (doubleHeads) {
display = 1;
}
var triangleTop = new fabric.Triangle({
opacity: display, width: 16, height: 16, fill: '#000', scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, left: (options.e.layerX - 8), top: (options.e.layerY - 0)
});
//groupItems.push(triangleTop);
let groupItems = [line, triangleBottom, triangleTop];
var group = new fabric.Group(groupItems, {
hasControls: true,
left: options.e.layerX,
top: options.e.layerY,
strokeUniform: true,
lockScalingX: true
}).setControlsVisibility({
tl: false,
tr: false,
mt: true,
mb: true,
ml: false,
mr: false,
bl: false,
br: false
});
canvas.add(group);
}
Upvotes: 1
Views: 474
Reputation: 1088
The only way I can think of is calling the arrows adjusting function during the object:scaling
event. I wonder if there is another better solution to that.
var canvas = new fabric.Canvas('canvas', {width: 400, height: 400});
canvas.on('object:scaling', function (e) {
let type = e.target.get('type');
if (type == 'group') {
adjustArrowsSize(e.target._objects, e.target.get('scaleY'))
}
});
canvas.on('object:scaled', function (e) {
let type = e.target.get('type');
if (type == 'group') {
adjustArrowsSize(e.target._objects, e.target.get('scaleY'))
}
});
function adjustArrowsSize(arrowObjects, objectScaleY) {
var triangleCount = 0, triangles = [];
arrowObjects.forEach(function (object) {
if (object.get('type') == 'triangle') {
triangles[triangleCount] = object;
triangleCount++;
let ratio = 1 / objectScaleY;
object.set('scaleY', ratio);
}
});
canvas.requestRenderAll();
}
function addArrow() {
var line = new fabric.Line([10, 10, 10, (10 + 50)], {
strokeUniform: true,
lockScalingX: true,
borderColor: 'transparent',
left: 10,
top: 10,
strokeWidth: 2,
stroke: '#000'
});
var triangleBottom = new fabric.Triangle({
width: 16, height: 16, fill: '#000',
scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, lockUniScaling: true, lockSkewingX: true, lockSkewingY: true, left: (10 - 8), top: (10 + 36)
});
triangleBottom.rotate(180);
var triangleTop = new fabric.Triangle({
opacity: 1, width: 16, height: 16, fill: '#000', scaleX: 1, scaleY: 1, strokeUniform: true, lockScalingX: true, lockScalingY: true, left: (10 - 8), top: (10 - 0)
});
//groupItems.push(triangleTop);
let groupItems = [line, triangleBottom, triangleTop];
var group = new fabric.Group(groupItems, {
hasControls: true,
left: 10,
top: 10,
strokeUniform: true,
lockScalingX: true
}).setControlsVisibility({
tl: false,
tr: false,
mt: true,
mb: true,
ml: false,
mr: false,
bl: false,
br: false
});
canvas.add(group);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.6/fabric.min.js"></script>
<button onclick="addArrow()">Add arrow</button>
<canvas id="canvas" class="canvas" style="border:1px solid #000;"></canvas>
Upvotes: 1