FabricJS - How to prevent leaving object from the given object? - javascript

I was able to prevent leaving objects from the canvas but I was not able to prevent leaving child objects from the given object.
I tried to do it in this way (see code example below) but this locks the object leaving in the same size (width, height) of the locked object but positioned absolutely (top, left). I need to prevent leaving in the same coords there the parent object is positioned (see please the attached image).
Any ideas are welcome.
preventLeavingInsideBox(e: IEvent) {
let activeObject = e.target;
if ((activeObject.get('left') - (activeObject.get('width') * activeObject.get('scaleX') / 2) < 0))
activeObject.set('left', activeObject.get('width') * activeObject.get('scaleX') / 2);
if ((activeObject.get('top') - (activeObject.get('height') * activeObject.get('scaleY') / 2) < 0))
activeObject.set('top', activeObject.get('height') * activeObject.get('scaleY') / 2);
if (activeObject.get('left') + (activeObject.get('width') * activeObject.get('scaleX') / 2) > this.rect.width)
{
var positionX = this.rect.width - (activeObject.get('width') * activeObject.get('scaleX')) / 2;
activeObject.set('left', positionX > this.rect.width / 2 ? positionX : this.rect.width / 2);
}
if (activeObject.get('top') + (activeObject.get('height') * activeObject.get('scaleY') / 2) > this.rect.height)
{
var positionY = this.rect.height - (activeObject.get('height') * activeObject.get('scaleY') / 2);
activeObject.set('top', positionY > this.rect.height / 2 ? positionY : this.rect.height / 2);
}
//below just prevention for object from getting width or height greater than canvas width and height
if (activeObject.get('width') * activeObject.get('scaleX') > this.rect.width)
{
activeObject.set('scaleX', this.rect.width / activeObject.get('width'));
}
if (activeObject.get('height') * activeObject.get('scaleY') > this.rect.height)
{
activeObject.set('scaleY', this.rect.height / activeObject.get('height'));
}
}

I was trying to achieve the same result. I had an object with an image inside that I didn't want to be able to be moved outside of it and I also did the resize too.
So I had to get the object that frame which held the image originally and then adds the image after.
What you need to take into account is the top & left of the holding object and the movable object.
So I have a ID on my object of feature image so I get access to it from that. and get the original top, left, height & width.
const featureImageBoxDetails = {
top: featureImageBox.top,
left: featureImageBox.left,
right: featureImageBox.left + featureImageBox.width * featureImageBox.scaleX,
bottom: featureImageBox.top + featureImageBox.height * featureImageBox.scaleY,
width: featureImageBox.width * featureImageBox.scaleX,
height: featureImageBox.height * featureImageBox.scaleY,
};
Then I have my preventLeavingInsideBox function:
const preventLeavingInsideBox = (e: IEvent) => {
const activeObject = e.target;
const activeObjectDetails = {
top: activeObject.top,
left: activeObject.left,
right: activeObject.left + activeObject.width * activeObject.scaleX,
bottom: activeObject.top + activeObject.height * activeObject.scaleY,
width: activeObject.width * activeObject.scaleX,
height: activeObject.height * activeObject.scaleY,
};
// right
if (activeObjectDetails.right <= featureImageBoxDetails.right) {
activeObject.set({
left: featureImageBoxDetails.right - activeObjectDetails.width,
});
}
// left
if (activeObjectDetails.left >= featureImageBoxDetails.left) {
activeObject.set({
left: featureImageBoxDetails.left,
});
}
// bottom
if (activeObjectDetails.bottom <= featureImageBoxDetails.bottom) {
activeObject.set({
top: featureImageBoxDetails.top + featureImageBoxDetails.height - activeObjectDetails.height,
});
}
// Top
if (activeObjectDetails.top >= featureImageBoxDetails.top) {
activeObject.set({
top: featureImageBoxDetails.top,
});
}
};
Then you just need to add in the:
canvas.on('object:moving', preventLeavingInsideBox);

I have same need but need more
i just need this same feature. and its working fine but when we do the same with rotated object its works strange. can you please

Related

Scaling elements based on another element

I have elements positioned on top of am img in the following way:
<div style={{ display: "inline-block", position: "relative" }} >
<img />
<div style={{ position: "absolute", left: 0, right: 0, top: 0, bottom: 0, width: "100%", height: "100%" }} >
<child elements I'm trying to position />
</div>
</div>
The child elements use the top, left, width, height properties to position themselves on top of the image.
I'm trying to make them scale with the image (- when the image's width and height changes I want them to stay in the same place and scale to the image):
width: Math.round(((box.x2 - box.x1) / imgWidth) * 100) + "%",
height: Math.round(((box.y2 - box.y1) / imgHeight) * 100) + "%",
left: Math.round((box.x1 / imgWidth) * 100) + "%",
top: Math.round((box.y1 / imgHeight) * 100) + "%"
But the results when using percentage are a little off than just using the coordinates.
Is there a better way to position the elements (boxes) so they scale with the image? Is there something wrong with the formula I'm using?
Edit:
For example:
The squares are the child elements I mentioned and when the user zooms in or out of the page or if the image's size changes I want the squares position on the image to be preserved and their scale match the image's scale
Ok, so this may not fit into your project straight off the bat but you should be able to get it working with a little bit of tweaking.
Use some JavaScript to calculate the absolute positioning of the child elements to a percentage.
var imgWrapper = document.getElementById("wrapper");
var children = document.getElementsByClassName("child");
window.onresize = function(event) {
var imgWidth = imgWrapper.offsetWidth;
var imgHeight = imgWrapper.offsetHeight;
for (var i = 0; i < children.length; i++)
{
// - Current child element
var child = children.item(i);
// - Child positions
var currentTop = child.offsetTop;
var currentLeft = child.offsetLeft;
var currentBottom = imgHeight - (currentTop + child.offsetHeight);
var currentRight = imgWidth - (currentLeft + child.offsetWidth);
var newTop = (100 * currentTop / imgHeight);
var newLeft = (100 * currentLeft / imgWidth);
var newBottom = (100 * currentBottom / imgHeight);
var newRight = (100 * currentRight / imgWidth);
child.style.top = newTop + "%";
child.style.left = newLeft + "%";
child.style.bottom = newBottom + "%";
child.style.right = newRight + "%";
}
};
http://next.plnkr.co/edit/E8RbqFTClYklW4w7

How to create a "Simple Harmonic Oscillation" on scroll event using Javascript

Currently I have a element on the page. I would like that object to oscillate left and right similar to the one in the example give below. However, I would like to bind the oscillation with the browser window scroll event.
https://www.khanacademy.org/computer-programming/simple-harmonic-motion/4920589909229568
I tried this but it's not smooth and accurate:
Javascript
let butterfly_y_position_log = [];
let scroll_times = 0;
let scroll_direction;
$(window).scroll(function(){
let butterfly_y_position = $('.floating-butterfly').offset().top;
let butterfly_x_position;
butterfly_y_position_log.push(butterfly_y_position);
if (butterfly_y_position_log.length > 2){
butterfly_y_position_log = butterfly_y_position_log.slice(1, 4);
}
if (butterfly_y_position_log[0] < butterfly_y_position_log[1] || butterfly_y_position_log[1] == undefined){
scroll_direction = 'down';
scroll_times++;
butterfly_x_position = ($(window).width() / 2) + (100 * Math.sin(2* Math.PI + scroll_times));
} else {
scroll_direction = 'up';
scroll_times--;
butterfly_x_position = ($(window).width() / 2) + (100 * Math.cos(2* Math.PI + scroll_times));
}
console.log(scroll_times);
console.log(butterfly_x_position);
$('.floating-butterfly').css({left:butterfly_x_position});
});
HTML
<img src="..." class="floating-butterfly">
CSS
.floating-butterfly {
position: fixed;
top: 40px;
z-index: 2;
}
The Y-position will remain fixed, only X-position will change on scroll.

How can I make my div spawn within 400 px of either side using jquery?

I'm making an animated background for a webpage that spawn div elements to look like bubbles and animates their transparency. I want the bubbles to only spawn within the left 400px and the right 400px of the screen. I can get them to spawn in the middle, but when I try to make it spawn on the edges it breaks the code. (maybe an infinite loop?)
function bubble() {
var rightOffset = $(window).width() - 400; //where i want them on the right
do {
var leftPx = Math.floor(Math.random() * $(window).width() - 100);
} while (leftPx >= 400 || rightOffset <= leftPx);
var topPx = Math.floor(Math.random() * $(window).height() - 100); //not even going to worry about the top yet
if(bubbleCount <= 30){
bubbleCount ++;
$('html').append("<div class=bubble style=' left: " + leftPx + "px; top: " + topPx + "px;'></div>");
$('.bubble').animate({
opacity: 0.5,
}, 3000, 'swing')
.animate({
opacity: 0,
}, 3000, 'swing', function(){
$(this).remove();
bubbleCount -= 1;
});
}
}
Why not simply randomize the side as well? This way you don't have to check and discard potential positions. Something like this.
function randomBetween(min,max) {
return Math.floor(Math.random()*(max-min+1)+min);
}
var width = $(window).width();
var leftPixels = Math.round(Math.random()) ? randomBetween(0, 400) :
randomBetween(width - 400, width);
What happens here is that it first randomly generates 0 or 1, and then if it's 1 it generates a LEFT side value and if 0 generates a RIGHT side value. No extra iterations that slow down your program.
I reproduced your bubble effect and didn't end up with a break in code!
I think what you are missing is that for an absolute-positioned bubble to be positioned from right, you need to give it a right CSS offset value; likewise, for a left absolute-positioned bubble, stick to the left property:
var rightOffset = $(window).width() - 400; //where i want them on the right
do {
var rightPx = Math.floor(Math.random() * $(window).width() - 100);
} while (rightPx >= 400 || rightOffset <= rightPx);
/*
* ...
*/
$('html').append("<div class='bubble' style=' right: " + rightPx + "px; top: " + topPx + "px;'></div>");

mousemove parallax only move slightly instead of mouse position

I'm wanting the plane and rocket to only move approx 5% from their original place when the mouse hits the hero-panel area.
this current code makes both images follow and offset where the mouse position is.
Please assist.
$(document).ready(function () {
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 1);
parallax(e, document.getElementById('rocket'), 2);
});
});
function parallax(e, target, layer) {
var layer_coeff = 10 / layer;
var x = ($(window).width() - target.offsetWidth) / 4 - (e.pageX - ($(window).width() / 4)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 4 - (e.pageY - ($(window).height() / 4)) / layer_coeff;
$(target).offset({ top: y ,left : x });
};
https://jsfiddle.net/jc0807/c5yke2on/
Thanks
Ok, I think I understand what you're looking for:
fiddle
$(document).ready(function () {
var plane = document.getElementById('plane');
var rocket = document.getElementById('rocket');
plane.homePos = { x: plane.offsetLeft, y: plane.offsetTop };
rocket.homePos = { x: rocket.offsetLeft, y: rocket.offsetTop };
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 10);
parallax(e, document.getElementById('rocket'), 20);
});
});
function parallax(e, target, layer) {
var x = target.homePos.x - (e.pageX - target.homePos.x) / layer;
var y = target.homePos.y - (e.pageY - target.homePos.y) / layer;
$(target).offset({ top: y ,left : x });
};
What we're doing here is recording the starting position of the plane and the rocket as a new property 'homePos' on the plane and rocket objects. This makes it easy to apply the parallax effect as an offset from the original positions based on the mouse distance from the object homePos.
If you modify the layer value passed to parallax, the amount of movement will change (we're dividing the mouse offset from the middle of the object's starting position by it, to calculate the new object offset amount).
I guess my question is somehow related to the one above and I didn't want to create a duplicate.
I am using the code bellow to "navigate" into an image on mousemove. The problem is that I cannot manage to make the image fill all the viewable screen area. I've added a red background to the container to show what I mean. The desired result would be to no red background visible.
HTML
<div class="m-scene" id="main">
<div class="scene_element">
<img class="body" src="http://www.planwallpaper.com/static/images/nature-background-images2.jpg" />
</div>
</div>
JS
$(document).ready(function() {
$('img.body').mousemove(function(e) {
parallax(e, this, 1);
});
});
function parallax(e, target, layer) {
var layer_coeff = 20 / layer;
var x = ($(window).width() - target.offsetWidth) / 2 - (e.pageX - ($(window).width() / 2)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 2 - (e.pageY - ($(window).height() / 2)) / layer_coeff;
$(target).offset({
top: y,
left: x
});
};
CSS
.m-scene {
background-color: red;
overflow: hidden;
width: 100%;
}
.scene_element {
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
.scene_element img {
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
}
My jsFiddle is: fiddle
Thank you!

DIVs to randomly fadeIn on page

Okay, I've seen a few things that sort of * answer my question, but none of them quite do what I want to do / I'd like to understand how to do this myself from start to finish as a learning exercise. I'm a novice at all this, so bear with me!
What I'm Trying to Do:
I have a black page and I'd like 20-30 small, white div boxes to fadeIn at random positions on the page (like stars is sort of the vibe I'm going for).
Ideally, they wouldn't overlap and they would be randomly sized between 5px and 10px, but I recognize that this might be getting a little too complicated.
Here's what I have so far
I've been working off this jsfiddle and well as this one. This is what I've come up with (that doesn't work, they all fade in equally spaced in a line and don't stay confined from to the site)
Here's my jsfiddle, code below
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
$(document).ready(function() {
var newPosition = randomPosition();
$('.star').css( {
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px'
}).each(function(index) { $(this).delay(1500*index).fadeIn('slow');
})
});
CSS
body {
background-color: black;
}
.star {
height: 10px;
width: 10px;
background-color: white;
display: none;
}
HTML (is there a way to do this with just a for loop or something similar?)
<div class="star"> </div>
<div class="star"> </div>
<div class="star"> </div>
<div class="star"></div>
The sizing and positioning isn't too hard. The thing is to do it all in the each loop - currently you get 1 position and use it for everything. Also you will want to make them position:absolute so they don't go off the page.
I've updated your fiddle to set the random position and a size between 5 and 10px:
The overlapping is a bit harder. You need to keep track of the sizes and positions you have generated and in the same .each function compare the current generated size+position to the previous ones to check for overlapping.
http://jsfiddle.net/5ocb5aww/3/
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
function randomSize() {
return Math.round(Math.random() * 5) + 5;
}
$(document).ready(function() {
// stores generated star positions
var stars = [];
$('.star').each(function(index) {
var newPosition, newSize;
// check for overlap
var isOverlap = true;
while(isOverlap)
{
newPosition = randomPosition();
newSize = randomSize();
// check previous stars to see if an edge of this one overlaps
isOverlap = $.grep(stars, function(s) {
return (
(newPosition[1] >= s.x1 && newPosition[1] <= s.x2)
|| (newPosition[1]+newSize >= s.x1 && newPosition[1]+newSize <= s.x2)
)
&& (
(newPosition[0] >= s.y1 && newPosition[0] <= s.y2)
|| (newPosition[0]+newSize >= s.y1 && newPosition[0]+newSize <= s.y2)
);
}).length > 0;
}
// store to check later stars against it
stars.push({
x1: newPosition[1],
x2: newPosition[1] + newSize,
y1: newPosition[0],
y2: newPosition[0] + newSize,
size: newSize});
$(this).css({
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px',
'width':newSize + 'px',
'height':newSize + 'px'
});
$(this).delay(800*index).fadeIn('slow');
})
});
Here is my approach to your exercise ... the overlapping position would require a little bit more effort ... I'll leave you that to sort for yourself (may require restructuring the code I'm handing here)
jsFiddle Demo
JS
function starDust(wdt, hgt, tSt, tAp){
var timer = tAp * 1000;
var defInt = tSt,
starInt = setInterval(function(){
var posX = Math.floor((Math.random() * wdt) + 1),
posY = Math.floor((Math.random() * hgt) + 1),
size = Math.floor((Math.random() * 10) + 1);
$('body').append('<div class="star"></div>');
$('.star:last').css({'width':size,'height':size,'left':posX,'top':posY}).hide().fadeIn('slow');
var totalStars = $('.star').length;
if(totalStars == defInt){
clearInterval(starInt);
}
}, timer);
}
$(function(){
// Function arguments: starDust(max X position in px, max Y position in px, total number of stars, time in seconds between stars show);
starDust(600,300,25,1);
});
CSS
body{
background-color:#000;
}
.star{
position: absolute;
background-color:#fff;
min-width:5px;
min-height:5px;
}

Categories