I have a fabric ellipse object which is scaled and skewed. I want to recreate the same ellipse with no scaling and skewing. I assume this could be achieved by calculating new values for rx,ry and angle by offseting the scaling and skewing values. I managed to offset the scaling values, but I am not able to figure out the calculations to offset the skew values.
Thanks in advance for any help.
Jsfiddle - https://jsfiddle.net/jwp2yysh/
Please see the sample code. I want the blue ellipse to alligned inline with the red ellipse.
var canvas = new fabric.Canvas('c');
var redEllipse = new fabric.Ellipse({
rx: 80,
ry: 50,
originX: 'center',
originY: 'center',
fill: '',
stroke: 'red',
strokeWidth: 1,
top: 180,
left: 240,
angle: 30,
scaleX: 1.23,
scaleY: 1.4,
skewX: 25,
skewY: 0
});
canvas.add(redEllipse);
var blueEllipse = new fabric.Ellipse({
rx: redEllipse.rx * redEllipse.scaleX,
ry: redEllipse.ry * redEllipse.scaleY,
originX: 'center',
originY: 'center',
fill: '',
stroke: 'blue',
strokeWidth: 1,
top: redEllipse.top,
left: redEllipse.left,
angle: redEllipse.angle * redEllipse.scaleY / redEllipse.scaleX,
scaleX: 1,
scaleY: 1,
skewX: 0,
skewY: 0
});
canvas.add(blueEllipse);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
I assume you are looking for something like this JSFiddle.
var canvas = new fabric.Canvas('c');
var redEllipse = new fabric.Ellipse({
rx: 80,
ry: 50,
originX: 'center',
originY: 'center',
fill: '',
stroke: 'red',
strokeWidth: 1,
top: 180,
left: 240,
angle: 30,
scaleX: 1.23,
scaleY: 1.4,
skewX: 0,
skewY: 0
});
canvas.add(redEllipse);
var blueEllipse = new fabric.Ellipse({
rx: redEllipse.rx * redEllipse.scaleX - 10,
ry: redEllipse.ry * redEllipse.scaleY - 10,
// rx:80*1.23,
//ry:50*1.4,
originX: 'center',
originY: 'center',
fill: '',
stroke: 'blue',
strokeWidth: 1,
top: redEllipse.top,
left: redEllipse.left,
angle: redEllipse.angle,
//angle:30,
scaleX: 1,
scaleY: 1,
skewX: 0,
skewY: 0
});
canvas.add(blueEllipse);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
From what I could understand, your calculation of rx and ry is correct. The problem with calculating the angle using the formula you have mentioned in the code is probably not taking into account the skew-ness of the original ellipse.
A simpler way would be to replicate the angle redEllipse.angle to make the new ellipse inline.
To make it inside the original one, you just need to reduce the rx and ry values by some common factor (I have used 10).
Related
I want to assign text or label using OpenLayers to Icons. I am able to assign the label to the icon but it places text on the icon .but I want to display label at offset so icon and label both display properly. I have tried using offsetY but it is not working and does not change the placement.
var styleToponimiFunction = function(feature,resolution) {
console.log(feature);
var iconName;
if (feature.get("remark")=='Daerah Istimewa') {
iconName='daerah_istimewa2.png';
}
else if (feature.get("remark")=='Kecamatan'){
iconName='kecamatan.png';
}
iconStyle = [new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 46],
anchorXUnits: "fraction",
anchorYUnits: "pixel",
scale:0.03,
opacity:1,
src: "icons/" + iconName,
})),
text: new ol.style.Text({
text: feature.get('namobj'),
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({ color: 'black' }),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2,
textAlign: 'center',
Baseline: 'hanging',
offsetX : 0,
offsetY : -12,
}),
declutter: true,
overflow: true
}),
})
]
return iconStyle;
}
The text options should not be inside the Stroke constructor, also Baseline should be textBaseline
stroke: new ol.style.Stroke({
color: '#fff',
width: 2,
}),
textAlign: 'center',
textBaseline: 'hanging',
offsetX : 0,
offsetY : -12,
How to increase touch area of control points on mobile device with FabricJS? Because on mobile they are too small for smooth interactive with. But not change size of control points ( for nice view ).
Here is my code:
How to increase interactive of control points = view control points size x 2 ?
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 12,
cornerStyle: 'circle',
});
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Please show me the way to resolve this problem.
By the way: ( on mobile device ) is there any way to allow drag object only object is selected before?
Thank you very much!
You need to override _setCornerCoords method of object and change the value of cornerHypotenuse.
DEMO
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 5,
cornerStyle: 'circle'
});
rect._setCornerCoords = function() {
var coords = this.oCoords,
newTheta = fabric.util.degreesToRadians(45 - this.angle),
cornerHypotenuse = this.cornerSize * 2 * 0.707106,
cosHalfOffset = cornerHypotenuse * fabric.util.cos(newTheta),
sinHalfOffset = cornerHypotenuse * fabric.util.sin(newTheta),
x, y;
for (var point in coords) {
x = coords[point].x;
y = coords[point].y;
coords[point].corner = {
tl: {
x: x - sinHalfOffset,
y: y - cosHalfOffset
},
tr: {
x: x + cosHalfOffset,
y: y - sinHalfOffset
},
bl: {
x: x - cosHalfOffset,
y: y + sinHalfOffset
},
br: {
x: x + sinHalfOffset,
y: y + cosHalfOffset
}
};
}
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
I've been trying to cut a picture into several parts for hours now.
I thought it would work with ImageEditor.cropImage. Apparently I'm using the function wrong, or it doesn't do what I expect it to do.
Can someone please tell me what I'm doing wrong, or recommend a package I can use. Thank you.
My attempt:
const cropData = {
offset: {x: 25, y: 25},
size: {width: 250, height: 250},
displaySize: {width: 250, height: 250},
resizeMode: 'contain',
};
later..
ImageEditor.cropImage(require('../resources/images/pictures/categoryOne/c1b1.jpg'),
cropData);
I solved my issue not with ImageEditor.cropImage but with CSS
<View style={styles.topLeftView}>
<Image
style={styles.topLeftImage}
source={require('../resources/images/pictures/categoryOne/c1b1.jpg')}
resizeMode="contain"
/>
</View>
Style
topLeftView: {
width: 100,
height: 100,
overflow: 'hidden',
},
topLeftImage: {
right: 0,
bottom: 0,
},
In my code I created method to clip images using fabric shapes. I have used stackoverflow answer for achieving this. Somehow after using this method I cannot render the canvas using default fabric canvas render method.
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
document.getElementById('download').addEventListener('click', function() {
canvas.renderAll();
this.href = canvas.toDataURL({
format: 'png',
multiplier: 2
});
this.download = "test.png";
}, false);
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
angle: 90,
left: 195,
top: 0,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
angle: 5,
stroke: 'red'
});
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 245,
top: 35,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clipRect1.render(ctx);
ctx.restore();
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
pugImg.setAttribute('crossOrigin', 'anonymous');
#c {
border:0px solid #ccc;
}
<script src="//cdn.bootcss.com/fabric.js/1.5.0/fabric.js"></script>
<canvas id="c" width="500" height="400"></canvas>
<a id="download">Download as image</a>
Your problem is the multiplier.
When using that kind of clipTo functions, you are setting the transform of canvas to plain. When rendering with canvas with multiplier, the base canvas transformation is scaled by the multiplier.
Your rect will be drawn without this transform ( because of setTransform(1,0,0,1,0,0) and will clip out the image.
Instead of setting the transform to [1,0,0,1,0,0] you should set to the invers of the current transformation of the object you are clipping.
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
document.getElementById('download').addEventListener('click', function() {
var data = canvas.toDataURL({
format: 'png',
multiplier: 2
});
console.log(data);
//var img = document.getElementById('export');
//img.src = data;
}, false);
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
angle: 90,
left: 195,
top: 0,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
stroke: 'red',
angle: 5
});
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 245,
top: 35,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
ctx.save();
var myMatrix = this.calcTransformMatrix();
myMatrix = fabric.util.invertTransform(myMatrix);
ctx.transform.apply(ctx, myMatrix);
clipRect1.render(ctx);
ctx.restore();
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
#c {
border:0px solid #ccc;
}
<script src="//www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas id="c" width="500" height="400"></canvas>
<a id="download">Download as image</a>
<img id="export" >
The vertical alignment of text seems to be different in different browsers when working with KineticJS. I created two JSFiddles that illustrate the problem: In the first, the text is rendered to a raw HTML5 canvas without the use of KineticJS (see Raw HTML5 Example). In this case, the text is aligned the same way in Chrome and FF. Here's the JS-code for raw HTML5 canvas:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
xOrigin = 10;
context.beginPath();
context.rect(xOrigin, 90, 107, 12);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'black';
context.stroke();
context.font = '11px Calibri';
context.fillStyle = 'black';
context.fillText('Hello World! Calibri', xOrigin + 9, 100);
xOrigin = 141;
context.beginPath();
context.rect(xOrigin, 90, 103, 12);
context.fillStyle = 'cyan';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'black';
context.stroke();
context.font = '11px Arial';
context.fillStyle = 'black';
context.fillText('Hello World! Arial', xOrigin + 9, 100);
However, when KineticJS is used, the text in Arial is placed one pixel higher in FF than in Chrome (see KineticJS Example). Here's the code for the KineticJS example:
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var xOrigin = 10;
var rect = new Kinetic.Rect({
x: xOrigin,
y: 90,
width: 107,
height: 12,
fill: 'yellow',
stroke: 'black',
strokeWidth: 2
});
var simpleText = new Kinetic.Text({
x: xOrigin + 9,
y: 90,
text: 'Hello World! Calibri',
fontSize: 11,
fontFamily: 'Calibri',
fill: 'black'
});
layer.add(rect);
layer.add(simpleText);
xOrigin = 141
var rect = new Kinetic.Rect({
x: xOrigin,
y: 90,
width: 103,
height: 12,
fill: 'cyan',
stroke: 'black',
strokeWidth: 2
});
var simpleText = new Kinetic.Text({
x: xOrigin + 9,
y: 90,
text: 'Hello World! Arial',
fontSize: 11,
fontFamily: 'Arial',
fill: 'black'
});
layer.add(rect);
layer.add(simpleText);
stage.add(layer);
Is there a way to render text consistently across FF and Chrome with KineticJS? Unfortunately, there seems to be no text-baseline property in KineticJS (see here). And why is it rendered correctly when working on the raw HTML5 canvas? Is this a KineticJS bug or did I miss something?