Adding a "bleed area" to canvases with fabricjs? - javascript

I'm using fabricjs (1.7.20) and would like to create a sort of "bleed area" where some space around the canvas isn't usable by the user; a sort of "wall" if you will, to stop objects from being moved to the sides of the canvas walls. How might I accomplish this?
var canvas = new fabric.Canvas("c");
canvas.setHeight(350);
canvas.setWidth(350);
canvas.add(new fabric.IText("Some text", {
top: 25,
}));
var circle = new fabric.Circle({
radius: 20, fill: 'green', left: 100, top: 100
});
var triangle = new fabric.Triangle({
width: 20, height: 30, fill: 'blue', left: 150, top: 150
});
canvas.add(circle, triangle);
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<canvas id="c"></canvas>
This was suggested to me and it is the closest I've gotten. I'm looking to do this, but 10px, give or take, from the border of the canvas.
canvas.on('object:moving', function (e) {
var obj = e.target;
// if object is too big ignore
if(obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width){
return;
}
obj.setCoords();
// top-left corner
if(obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0){
obj.top = Math.max(obj.top, obj.top-obj.getBoundingRect().top);
obj.left = Math.max(obj.left, obj.left-obj.getBoundingRect().left);
}
// bot-right corner
if(obj.getBoundingRect().top+obj.getBoundingRect().height > obj.canvas.height || obj.getBoundingRect().left+obj.getBoundingRect().width > obj.canvas.width){
obj.top = Math.min(obj.top, obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top);
obj.left = Math.min(obj.left, obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left);
}
});

You can add a 10 pixel bleed area by adding / subtracting the value 10 from the conditions that determine if an object is being moved out of the “bleed” area (I'd actually prefer to call it “padding”), and adding / subtracting from the calculation that repositions the object inside the padding boundaries.
Here's an updated example that works:
var padding = 10;
var canvas = new fabric.Canvas("c");
canvas.setHeight(350);
canvas.setWidth(350);
canvas.add(new fabric.IText("Some text", {
top: 25,
}));
var circle = new fabric.Circle({
radius: 20,
fill: 'green',
left: 100,
top: 100
});
var triangle = new fabric.Triangle({
width: 20,
height: 30,
fill: 'blue',
left: 150,
top: 150
});
canvas.add(circle, triangle);
canvas.on('object:moving', function(e) {
var obj = e.target;
// if object is too big ignore
if (obj.currentHeight > obj.canvas.height - padding * 2 ||
obj.currentWidth > obj.canvas.width - padding * 2) {
return;
}
obj.setCoords();
// top-left corner
if (obj.getBoundingRect().top < padding ||
obj.getBoundingRect().left < padding) {
obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top + padding);
obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left + padding);
}
// bot-right corner
if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height - padding ||
obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width - padding) {
obj.top = Math.min(
obj.top,
obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top - padding);
obj.left = Math.min(
obj.left,
obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left - padding);
}
});
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<canvas id="c"></canvas>
In the first line, I've defined a variable “padding” that refers to the desired padding size. This way, if you want to change the padding later on, you don't have to change it in eight different places.
The padding variable used instead of 0 in the condition for the top left corner.
If the “top left” condition is true, the padding is added to the calculation for repositioning the object.
In the “bottom right” part, we are doing the opposite – we subtract the padding from the condition and subtract from the repositioning calculation.
You can also try the code in this fiddle: https://jsfiddle.net/pahund/hz7jLnme/

Related

How to update Javascript Variables from HTML input in real time?

I'm making a program whereby rectangles can be modified in realtime (depending on what the user keyboard input is) using fabric.js.
I'm having an awful time doing this.
I'm trying to get the input id=width and input id=height to be able to edit the rect.width and the rect.height and PREFERABLY have it change the shape of the rectangle as I'm doing it.
I've included my code below, I've just removed all my attempts at document.getElementById('height').value; because they have all been absolutely useless, and when I include them it breaks the whole script (which I didn't think was very useful for a jsfiddle).
I think I'm in a bit over my head.
Any help would be much appreciated.
jsfiddle
HTML
<body>
<input id="width" type="number" value="100"> <br>
<input id="height" type="number" value="100"> <br>
</body>
<canvas id="canvas" width="800" height="800"></canvas>
Javascript
var canvas = new fabric.Canvas('canvas');
var grid = 20;
for (var i = 0; i < (800 / grid); i++) {
canvas.add(new fabric.Line([ i * grid, 0, i * grid, 800],
{strokeDashArray: [3, 17], strokeWidth : 3, stroke: 'black', selectable: false }));
canvas.add(new fabric.Line([ 0, i * grid, 800, i * grid],
{strokeDashArray: [3, 17], strokeWidth :3, stroke: 'black', selectable: false }))
}
var rect = new fabric.Rect({
top : 50,
left : 50,
width : 100,
height : 100,
fill : 'red',
hasControls : false
});
canvas.add(rect);
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) *
grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas.on('object:moving', function (e) {
var obj = e.target;
// if object is too big ignore
if(obj.currentHeight > obj.canvas.height || obj.currentWidth
> obj.canvas.width){
return;
}
obj.setCoords();
// top-left corner
if(obj.getBoundingRect().top < 0 ||
obj.getBoundingRect().left < 0){
obj.top = Math.max(obj.top, obj.top-
obj.getBoundingRect().top);
obj.left = Math.max(obj.left, obj.left-
obj.getBoundingRect().left);
}
// bot-right corner
if(obj.getBoundingRect().top+obj.getBoundingRect().height >
obj.canvas.height ||
obj.getBoundingRect().left+obj.getBoundingRect().width >
obj.canvas.width){
obj.top = Math.min(obj.top, obj.canvas.height-
obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top);
obj.left = Math.min(obj.left, obj.canvas.width-
obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left);
}
});
So you are really asking two questions:
1. How do I know when the value of a text box changes?
You can find the input using getElementById and attach and event listener to the change event.
let widthInput = document.getElementById('width');
widthInput.addEventListener('change', (event) => {
});
2. How do I resize an object in fabric.js
You use the set method on the Fabric.js object. Then call renderAll on the canvas.
rect.set('width', parseInt(widthInput.value));
canvas.renderAll();
All Together:
Note: You have to click out of the text box for the change event to fire.
https://jsfiddle.net/9v5zogju/
HTML
I removed in the invalid type attributes, gave each input a unique id, and removed the extra body tag.
<input id="width" value="100"><br>
<input id="height" value="100"><br>
<canvas id="canvas" width="800" height="800"></canvas>
Javascript
I left your code as is. My code is at the bottom.
var canvas = new fabric.Canvas('canvas');
var grid = 20;
for (var i = 0; i < (800 / grid); i++) {
canvas.add(new fabric.Line([ i * grid, 0, i * grid, 800],
{strokeDashArray: [3, 17], strokeWidth : 3, stroke: 'black', selectable: false }));
canvas.add(new fabric.Line([ 0, i * grid, 800, i * grid],
{strokeDashArray: [3, 17], strokeWidth :3, stroke: 'black', selectable: false }))
}
var rect = new fabric.Rect({
top : 50,
left : 50,
width : 100,
height : 100,
fill : 'red',
hasControls : false
});
canvas.add(rect);
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas.on('object:moving', function (e) {
var obj = e.target;
// if object is too big ignore
if(obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width){
return;
}
obj.setCoords();
// top-left corner
if(obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0){
obj.top = Math.max(obj.top, obj.top-obj.getBoundingRect().top);
obj.left = Math.max(obj.left, obj.left-obj.getBoundingRect().left);
}
// bot-right corner
if(obj.getBoundingRect().top+obj.getBoundingRect().height > obj.canvas.height || obj.getBoundingRect().left+obj.getBoundingRect().width > obj.canvas.width){
obj.top = Math.min(obj.top, obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top);
obj.left = Math.min(obj.left, obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left);
}
});
//new code below this line
let widthInput = document.getElementById('width');
widthInput.addEventListener('change', (event) => {
rect.set('width', parseInt(widthInput.value));
canvas.renderAll();
});
let heightInput = document.getElementById('height');
heightInput.addEventListener('change', (event) =>
rect.set('height', parseInt(heightInput.value));
canvas.renderAll();
});
https://jsfiddle.net/andrwrbnsn/doynL163/30/
After a quick look into it I hope this helps somewhat — fabric has setWidth and setHeight as can be explained here:
So essentially you use document.GetElementById to get the inputs and add an eventlistener which on keyup updates the width/height and refreshes the canvas.
How do I change a value for a Fabric.js object?

Line-by-line text background color padding in Fabric JS / Canvas

I am using the library FabricJS to overlay text on canvas. I need to add padding (ideally just left & right) to a text element that includes the property textBackgroundColor.
Here is what I've tried so far:
let textObject = new fabric.Text('Black & White',{
fontFamily: this.theme.font,
fontSize: this.theme.size,
textBaseline: 'bottom',
textBackgroundColor: '#000000',
left: 0,
top: 0,
width: 100,
height: 40,
padding: 20,
fill: 'white',
});
The padding doesn't work as I anticipated. I have attempted to use the backgroundColor property but that adds background to the whole group block and not just the text.
I could add a a non-breaking space to achieve the same effect, but this doesn't seem like a reliable solution and I was hoping Fabric JS allowed this out-of-the-box. Any ideas how to achieve this?
Required solution (version on the right, with additional padding is what I would like):
I give you 2 answers for two subtly different cases.
Case 1 - padding around the bounding box of single of multi-line text.
The code follows the CSS approach where margin is outside of the box, as depicted by the red line, and padding is inside, as shown by the gold background. On the left hand image, black text background is what you get from the built-in 'textBackgroundColor'. The yellow area shows the padding currently applied. The right hand image shows the additional benefit when you harmonise the padding colour, an also that you can reduce opacity on the background whilst keeping the text full-opaque.
BTW the built-in 'padding' attribute for text pads in relation to the controlling border, but the background color fill does not cover the white-space created. In other words, it operates like CSS margin rather than CSS padding.
Therefore it is necessary to ignore this padding attribute, and instead introduce a coloured rect to give the background color required, grouping this with the text element and positioning accordingly.
Example snippet below.
var canvas = window._canvas = new fabric.Canvas('c');
// function to do the drawing. Could easily be accomodated into a class (excluding the canvas reset!)
function reset(pos)
{
canvas.clear();
// Create the text node - note the position is (0, 0)
var text = new fabric.Text(pos.text, {
fontFamily: 'Arial',
left: 0,
top: 0,
fill: "#ffffff",
stroke: "",
textBackgroundColor: '#000000'
});
// create the outer 'margin' rect, note the position is negatively offset for padding & margin
// and the width is sized from the dimensions of the text node plus 2 x (padding + margin).
var rectMargin = new fabric.Rect({
left: -1 * (pos.padding.left + pos.margin.left),
top: -1 * (pos.padding.top + pos.margin.top),
width: text.width + ((pos.padding.left + pos.padding.right) + (pos.margin.left + pos.margin.right)),
height: text.height + ((pos.padding.top + pos.padding.bottom) + (pos.margin.top + pos.margin.bottom)),
strokeWidth: pos.border,
stroke: 'red',
fill: 'transparent'
})
// create the inner 'padding' rect, note the position is offset for padding only
// and the width is sized from the dimensions of the text node plus 2 x padding.
var rectPadding = new fabric.Rect({
width: text.width + (pos.padding.left + pos.padding.right),
height: text.height + (pos.padding.top + pos.padding.bottom),
left: -1 * pos.padding.left, top: -1 * pos.padding.top,
fill: 'gold'
})
// create group and add shapes to group, rect first so it is below text.
// note that as the group is oversized, we position it at pos - padding.
var group = new fabric.Group([ rectMargin, rectPadding, text ], {
left: pos.x - (pos.padding.left - pos.margin.left),
top: pos.y - (pos.padding.top - pos.margin.top),
angle: pos.angle,
});
canvas.add(group);
}
// function to grab values from user inputs
function go()
{
var m = $('#margin').val().split(',');
var p = $('#padding').val().split(',');
for (var i = 0 ; i < 4; i = i + 1)
{
p[i] = parseInt(p[i], 10); // ensure we have numbers and not strings !
m[i] = parseInt(m[i], 10);
}
// Object holding position and content info
var pos = {x: 50, y : 10, text: 'Text with padding\nand another line',
padding: {top:p[0], right:p[1], bottom: p[2], left: p[3]}, margin: {top:m[0], right:m[1], bottom: m[2], left: m[3]}, border: 1, angle: 10};
reset(pos);
}
// click handler for go button
$('#go').on('click', function(e){
go();
})
// call go once to show on load
go();
div
{
background-color: silver;
width: 600px;
height: 300px;
}
.ipt
{
margin-right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<p>
<span class='ipt'> Margin: <input id='margin' value = '12,10,12,10' /></span>
<span class='ipt'> Padding: <input id='padding' value = '0,5,0,5' /></span>
<span class='ipt'><button id='go' />Go</button></span>
<div>
<canvas id="c" width="600" height="300"></canvas>
</div>
Case 2: padding boxing the individual text lines and not the full bounding box.
In this case you can see the difference in how the padded background tracks each line of text instead of applying to the outer bounding box of the text. The solution is more complex, involving creating a dummy text node which then provides line splitting and sizing information. We then loop thru the line data, outputting individual text lines and padding rects into a group which means we can position and handle the text as a single object, as illustrated by the applied angle.
var textIn = 'Text goat\nMillenium jam\nplumb\nBlack & White'
var canvas = window._canvas = new fabric.Canvas('c');
// function to do the drawing. Could easily be accomodated into a class (excluding the canvas reset!)
function reset(pos)
{
canvas.clear();
// Create the text measuring node - not added to the canvas !
var textMeasure = new fabric.IText(pos.text, {
fontFamily: 'Arial',
left: 0,
top: 0,
fill: "#ffffff",
stroke: "",
textBackgroundColor: '#000000'
});
// loop round the lines in the text creating a margin/pad scenario for each line
var theText, text, textHeight, rectPadding, rectMargin, top = 0, shapes = [];
for (var i = 0; i < textMeasure._textLines.length; i = i + 1){
theText = textMeasure._textLines[i].join('');
textHeight = Math.floor(textMeasure.lineHeight * textMeasure.fontSize) //textMeasure.getHeightOfLine(i)
// Make the text node for line i
text = new fabric.IText(theText, {
fontFamily: 'Arial',
left: 0,
top: top,
fill: "#ffffff",
stroke: ""
});
// create the outer 'margin' rect, note the position is negatively offset for padding & margin
// and the width is sized from the dimensions of the text node plus 2 x (padding + margin).
rectMargin = new fabric.Rect({
left: -1 * (pos.padding.left + pos.margin.left),
top: top - (pos.padding.top + pos.margin.top),
width: text.width + ((pos.padding.left + pos.padding.right) + (pos.margin.left + pos.margin.right)),
height: textHeight + ((pos.padding.top + pos.padding.bottom) + (pos.margin.top + pos.margin.bottom)),
fill: 'transparent'
})
shapes.push(rectMargin);
// create the inner 'padding' rect, note the position is offset for padding only
// and the width is sized from the dimensions of the text node plus 2 x padding.
rectPadding = new fabric.Rect({
width: text.width + (pos.padding.left + pos.padding.right),
height: textHeight + (pos.padding.top + pos.padding.bottom),
left: -1 * pos.padding.left,
top: top - pos.padding.top,
fill: '#000000ff'
})
shapes.push(rectPadding);
shapes.push(text);
// move the insert point down by the height of the line
var gap = 0; // text.lineHeight - textHeight;
top = top - 1 + textHeight + pos.padding.top + pos.margin.top + pos.padding.bottom + pos.margin.bottom;
}
// At this point we have a list of shapes to output in the shapes[] array.
// Create group and add the shapes to group.
// note that group is positioned so that the topleft of the first text line is where
// it would fall if it were a standard text node.
var group = new fabric.Group(shapes, {
left: pos.x - (pos.padding.left - pos.margin.left),
top: pos.y - (pos.padding.top - pos.margin.top),
angle: pos.angle,
});
canvas.add(group);
}
// function to grab values from user inputs
function go()
{
var m = $('#margin').val().split(',');
var p = $('#padding').val().split(',');
for (var i = 0 ; i < 4; i = i + 1)
{
p[i] = parseInt(p[i], 10); // ensure we have numbers and not strings !
m[i] = parseInt(m[i], 10);
}
// Object holding position and content info
var pos = {x: 70, y : 10, text: textIn,
padding: {top:p[0], right:p[1], bottom: p[2], left: p[3]}, margin: {top:m[0], right:m[1], bottom: m[2], left: m[3]}, border: 1, angle: 10};
reset(pos);
}
// click handler for go button
$('#go').on('click', function(e){
go();
})
// call go once to show on load
go();
div
{
background-color: silver;
width: 600px;
height: 100px;
}
.ipt
{
margin-right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<p>
<span class='ipt'> Margin: <input id='margin' value = '0,0,0,0' /></span>
<span class='ipt'> Padding: <input id='padding' value = '5,15,5,15' /></span>
<span class='ipt'><button id='go' />Go</button></span>
<div>
<canvas id="c" width="600" height="300"></canvas>
</div>
If anyone is looking for a solution with Textbox,
Here's another solution you can try: https://github.com/fabricjs/fabric.js/issues/3731
var TextboxWithPadding = fabric.util.createClass(fabric.Textbox, {
_renderBackground: function(ctx) {
if (!this.backgroundColor) {
return;
}
var dim = this._getNonTransformedDimensions();
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(
-dim.x / 2 - this.padding,
-dim.y / 2 - this.padding,
dim.x + this.padding * 2,
dim.y + this.padding * 2
);
// if there is background color no other shadows
// should be casted
this._removeShadow(ctx);
}
});
Extend the base class and overwrite the _renderBackground function.
Very simple and works well for me!

Konva - Using Rotate and keeping within bounds

I am using Konva to create an area on a webpage. I create a dragBoundFunc function for my rectangle checking pos.x and pos.y and width of canvas and rect and this works fine - the rectangle only stays in this area as I want it to when dragged.
My problem comes when I rotate the rectangle 90 degrees. Now width is less than height when I check getClientRect().width and getClientRect().height which is expected but it now won't drag all the way to the end of my canvas unless I detect end of rotation and change my shapes offsetY value to 65 and then it works fine.
Is changing offsetY and offsetX after rotation the right approach to fix this problem and how do I calculate what to set them to if yes?
dragBoundFunc: function(pos) {
var iw = 600 - (my_shape.getClientRect().width);
var ih = 400 - my_shape.getClientRect().height;
var newX = pos.x > iw ? iw : pos.x;
var newY = pos.y > ih ? ih : pos.y;
return {
x: newX > 0 ? newX : 0,
y: newY > 0 ? newY : 0
};
}
Here is a working solution. Note that this works only for fixed rotations of 0, 90, 180 and 270 degrees or multiples. A solution for 'any-angle-of-rotation; would require trig or matrix maths to compute rotated values of the point, etc.
/*
This is the drag bounds func
*/
function theDragFunc(pos) {
var thisRect = {x: this.x(), y: this.y(), width: this.width(), height: this.height()};
// copy the boundary rect into a testRect which defines the extent of the dragbounds
// without accounting for the width and height of dragging rectangle.
// This is changed below depending on rotation.
var testRect={
left: boundary.x,
top: boundary.y,
right: boundary.x + boundary.width,
bottom: boundary.y + boundary.height
};
// the userRotation value is calculated in the rotation button onclick
// to be one of 0, 90, 180, 270
switch (userRotation){
case 0: // for 0 degrees compute as per a normal bounds rect
testRect.right = testRect.right - thisRect.width;
testRect.bottom = testRect.bottom - thisRect.height;
break;
case 90: // for 90 degs we have to modify the test boundary left and bottom
testRect.left = testRect.left + thisRect.height;
testRect.bottom = testRect.bottom - thisRect.width;
break;
case 180: // for 180 degs we have to modify the test boundary left and top
testRect.left = testRect.left + thisRect.width;
testRect.top = testRect.top + thisRect.height;
break;
case 270: // for 270 degs we have to modify the test boundary right and top
testRect.right = testRect.right - thisRect.height;
testRect.top = testRect.top + thisRect.width;
break;
}
// get new pos as: if pos inside bounday ranges then use it, otherwise user boundary
// left edge check
var newX = (pos.x < testRect.left ? testRect.left : pos.x);
// right edge check
newX = (newX > testRect.right ? testRect.right : newX);
// top edge check
var newY = (pos.y < testRect.top ? testRect.top : pos.y);
// bottom edge check
newY = (newY > testRect.bottom ? testRect.bottom : newY);
// return the point we calculated
return {
x: newX,
y: newY
}
}
// From here on is just the canvas setup etc.
// set ub the main rect - the one we drag and rotate
var target = {x: 70, y: 70, width: 70, height: 40};
// set ub the boundary rect - used in the rectfunc later
var boundary = {x: 20, y: 20, width: 460, height: 160};
// Set up the stage
var s1 = new Konva.Stage({container: 'container1', width: 500, height: 200});
// add a layer.
var layer1 = new Konva.Layer({draggable: false});
s1.add(layer1);
// show the extent of the boundary
var funcRect = new Konva.Rect({
x:boundary.x,
y: boundary.y,
width: boundary.width,
height: boundary.height,
stroke: 'red'})
layer1.add(funcRect)
// Make some easy-to-grok values for the boundary func.
boundary.minX = boundary.x;
boundary.maxX = boundary.x + boundary.width;
boundary.minY = boundary.y;
boundary.maxY = boundary.y + boundary.height;
// show the target rect
var targetRect = new Konva.Rect({
x:target.x,
y: target.y,
width: target.width,
height: target.height,
stroke: 'green',
draggable: true,
// Apply a linear graient fill to give a sense of rotation.
fillLinearGradientStartPoint: { x : -50, y : -50},
fillLinearGradientEndPoint: { x : 50, y : 50},
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
dragBoundFunc: theDragFunc // the function is at the bottom top of the code
})
layer1.add(targetRect)
// Draw the stage
s1.draw();
var userRotation = 0;
$('#btnRotate').on('click', function(e){
targetRect.rotate(90)
s1.draw();
var rectRotation = targetRect.rotation();
// user can rotate > 360 so we will nomalise the rotation down to range 0 - 270
userRotation = (rectRotation / 90);
userRotation = (userRotation % 4) * 90;
$('#info').html("Rect rotation " + rectRotation + " same as " + userRotation);
})
p
{
padding: 4px;
}
#container1
{
display: inline-block;
width: 500px;
height: 200px;
background-color: silver;
overflow: hidden;
}
#pallette
{
height: 52px; width: 500px;
border: 1px solid #666;
margin-bottom: 10px;
z-index: 10;
}
.draggable
{
width:50px;
height: 50px;
display: inline-block;
border: 1px solid #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Drag bounds function on rotated rect. Red rectangle is the extent of the bounds function. Drag the rect to the boundary and notice it is captured. Now click to rotate by 90%. A simple rect-based dragFunc would fail because the origin of the rect is not top-right. The solution function solves this.
</p>
<p>
<button id='btnRotate'>Rotate by +90 degrees</button> <span id='info'>0</span> degrees.
</p>
<div id='container1'></div>

Fabricjs - Get objects in group to be based on left wall

My fiddle: http://jsfiddle.net/yeftc60v/1/
So I'm trying to get the obj.left to be based on the left side(wall) of the group instead of the center. So right now, when I try to get the left of an object, if it's on the left side, it will return a negative number.
I've tried both setOriginY and setOriginX but they don't have any effect.
I'm working around this by doing some maths based on the group's width.
Right now if you hit the "clone" button, the left values read negative values (-100.5 and -0.5). Instead they should be 0 and 99.5(I think), respectively.
Unfortunately, any active group­'s origin x and y will always be at center. This won't take originX and originY property into consideration, hence setting setOriginX and setOriginY won't have any effect.
To get around this, you need to manually calculate the object­'s top and left position respective to the selected group, like so ...
let objLeft = obj.left + (active.width / 2);
let objTop = obj.top + (active.height / 2);
ᴅᴇᴍᴏ
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 50,
top: 50,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 1,
opacity: .8
});
var rect1 = new fabric.Rect({
left: 150,
top: 150,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 1,
opacity: .8
});
canvas.add(rect);
canvas.add(rect1);
function clone() {
let active = canvas.getActiveGroup();
if (active) {
active.forEachObject(obj => {
let clone = obj.clone();
// get object's left & top relative to the group
let objLeft = obj.left + (active.width / 2);
let objTop = obj.top + (active.height / 2);
clone.setLeft(objLeft + active.left);
clone.setTop(objTop + active.top);
canvas.add(clone);
});
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<canvas id="c" width="300" height="300" style="border: 1px solid black;"></canvas>
<button onclick="clone()">
Clone
</button>

Background position animation on hover

I have a div1 which animates background position on hover direction of mouse by jquery.
But it's working properly. it's going not right direction and I want it to work on every single mouse hover on the div.
Find jsfiddle
code:
$(function() {
$(".mainCont").hover(function(e) {
// $(this).addClass("hoverOnce");
var edge = closestEdge(e.pageX, e.pageY, $(this).width(), $(this).height());
}, function(){
$(this).removeClass('top right bottom left');
// $(this).removeClass("hoverOnce");
});
});
function closestEdge(x,y,w,h) {
var topEdgeDist = distMetric(x,y,w/2,0);
var bottomEdgeDist = distMetric(x,y,w/2,h);
var leftEdgeDist = distMetric(x,y,0,h/2);
var rightEdgeDist = distMetric(x,y,w,h/2);
var min = Math.min(topEdgeDist,bottomEdgeDist,leftEdgeDist,rightEdgeDist);
switch (min) {
case leftEdgeDist:
$(".hoverOnce").addClass("left");
case rightEdgeDist:
$(".hoverOnce").addClass("right");
case topEdgeDist:
$(".hoverOnce").addClass("top");
case bottomEdgeDist:
$(".hoverOnce").addClass("bottom");
}
}
function distMetric(x,y,x2,y2) {
var xDiff = x - x2;
var yDiff = y - y2;
return (xDiff * xDiff) + (yDiff * yDiff);
}
The size of this image that you use in the background is 700x500:
http://thesis2010.micadesign.org/kropp/images/research/bird_icon.png
I think that if you add these settings to .mainCont that this will get you the desired result:
width: 700px;
height: 500px;
position: absolute;
For example:
.mainCont {
width: 700px;
height: 500px;
background: url(http://thesis2010.micadesign.org/kropp/images/research/bird_icon.png) no-repeat center center;
transition: all 0.5s ease-in-out;
margin: 100px auto;
position: absolute;
}
Fiddle
Finally, It got solved.
find fiddle demo
$('.mainCont').hover(function(e){
var dir = determineDirection($(this), {x: e.pageX, y: e.pageY});
$(this).addClass('direction_'+dir);
}, function() {
$(this).removeClass('direction_3 direction_1 direction_2 direction_0');
});
function determineDirection($el, pos){
var w = $el.width(),
h = $el.height(),
x = (pos.x - $el.offset().left - (w/2)) * (w > h ? (h/w) : 1),
y = (pos.y - $el.offset().top - (h/2)) * (h > w ? (w/h) : 1);
return Math.round((((Math.atan2(y,x) * (180/Math.PI)) + 180)) / 90 + 3) % 4;
}

Categories

Resources