I have this problem. I would like the set to and left position of the element be implemented , which I haven't been able to work it out. Anyone with suggestion of a working solution thanks.
$(document).ready(function (){
//lets assign a value to a variable, but in my case the value is obtained from different part but that has no problem.
//Note in real case this value changes.
var number = 4;
//we create a function to count the top and left dimensions
//Note the values given are exactly as I want. the param given is number of current rect and second is total rect to be drawn.
function dim(i,a)
{
var top = '';
if((i%2)==0){top = 0;}
else if ((i%2)==1){top = ((document.documentElement.clientHeight)*1/3)/2; top = Math.floor(top);}
//We have gotten the top value if odd 0px if even halfway our container.
//Now for the left.
var y = (((a/2)+1)-i);
y = y*(-1);
var W = document.documentElement.clientWidth;
var left = Math.floor(((W/2)+(30*y)));
//alert(top+' , '+left);
return ([top,left]);
}
function draw()
{
//This is the main function.
for (i = 1; i <= number; i++ )
{
var cd = document.createElement('div');
var node = document.createTextNode("This is new.");
cd.appendChild(node);
cd.style.width = '30px';
cd.style.background = 'rgb('+10*i+','+20*i+','+15*i+')';
cd.setAttribute ('position','relative');
var x = Math.floor(((document.documentElement.clientHeight)*1/3)/2);
cd.style.height = x+'px';
cd.className = 'rect'+i;
var di = dim(i,number);
cd.style.top = di[0]+'px';
cd.style.left = di[1]+'px';
document.body.appendChild (cd);
}
}
draw();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
I do not want it to appear on the same line with no alignment of top and left. thanks.
Related
Hello I am trying to insert an img several times into a div, each time my img will have different properties. My Html code will have a div with an example id called ID_DIV.
I will have a style section into the html code like this img position absolute.
I will preset the variable NumberOfTimes according to the number of img I need into the div.
But I can't manage to make the loop work. Every time I refresh my browser my img appears in a different position, but it wont appear a "NumberOfTImes" times.
I have edited my code into this. But the loop wont work anyways. Still without knowing what it is.
function generateImg(){
var numberOfTimes=prompt("how many times?");
var div_id1=document.getElementById("div_id1");
do{
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 's0me_s0urce'); // sets the source for img file
div_id1.appendChild(oImg); // append the img to #id Div.
document.querySelector('img').style.top = Math.floor(Math.random()*401)+'px';
document.querySelector('img').style.left = Math.floor(Math.random()*401)+'px';
numberOfTimes--;
}while(numberOfTimes>0);
}
Please Help. I can't find where is the mistake in this logic.
you need to declare "i" outside of the loop otherwise it will not decrement and will only ever be "NumberOfTimes - 1" and therefore your loop will loop repeadeteldy with the image in the same location. This is also assuming that "NumberOfTimes" is in in the same scope to be used in the first place. Note that I have moved the location of "i" and added a decrement to it in the loop.
function RandomPositionImgs(){
var i=NumberOfTimes;
do{
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 'some_source'); // sets the source for img file
document.getElementById("ID_DIV").appendChild(oImg); // append the img to leftSide Div.
document.querySelector('img').style.top = Math.floor(Math.random()*401)+'px'; // random positioning
document.querySelector('img').style.left = Math.floor(Math.random()*401)+'px'; // random positioning
i--;
}while(i>0);
}
Thank you all for your answers you made me realize what I was doing wrong. This is the correct code I did after your comments, its working perfect.
function generateImgs(){
var numberOfTimes=prompt("how many times?");
for(i=0;i<numberOfFaces;i++){
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 'valid source'); // sets the source for img file
document.getElementById("div id").appendChild(oImg); // append the img to Div.
oImg.style.top = Math.floor(Math.random()*401)+'px';
oImg.style.left = Math.floor(Math.random()*401)+'px';
}
}
My answer is obsolete now. Anyway, here is a way to deal with overlapping images:
var img, i, x, y;
var size = 48, w = 5, h = 3;
var grid = new Array(w * h).map(x => false);
var n = 5; // n > grid.length => infinite loop
var src = 'https://i.stack.imgur.com/6gwfY.gif?s=48&g=1';
var ct = document.getElementById('ct');
ct.style.width = size * w + 'px';
ct.style.height = size * h + 'px';
while (n-- > 0) {
i = next(), x = i % w, y = (i - x) / w;
img = document.createElement('img');
img.setAttribute('src', src);
img.style.left = size * x + 'px';
img.style.top = size * y + 'px';
ct.appendChild(img);
}
function next () {
var l = grid.length;
var i = Math.floor(Math.random() * l);
// as long as this position is not free, try the next one
while (grid[i]) i = (i + 1) % l;
return grid[i] = true, i;
}
img{position:absolute}
#ct{background:#eee}
#ct{position:relative}
<div id="ct"></div>
I'm building a website which uses jQuery to allow users to add widgets to a page, drag them around and resize them (the page is fixed width and infinite height.) The issue that I'm having is that when adding a new widget to the page I have to find a free space for it (the widgets cannot overlap and I'd like to favour spaces at the top of the page.)
I've been looking at various packing algorithms and none of them seem to be suitable. The reason why is that they are designed for packing all of the objects in to the container, this means that all of the previous rectangles are laid out in a uniform way. They often line up an edge of the rectangle so that they form rows/columns, this simplifies working out what will fit where in the next row/column. When the user can move/resize widgets at will these algorithms don't work well.
I thought that I had a partial solution but after writing some pseudo code in here I’ve realized that it won’t work. A brute force based approach would work, but I'd prefer something more efficient if possible. Can anyone suggest a suitable algorithm? Is it a packing algorithm that I'm looking for or would something else work better?
Thanks
Ok, I've worked out a solution. I didn't like the idea of a brute force based approach because I thought it would be inefficient, what I realized though is if you can look at which existing widgets are in the way of placing the widget then you can skip large portions of the grid.
Here is an example: (the widget being placed is 20x20 and page width is 100px in this example.)
This diagram is 0.1 scale and got messed up so I've had to add an extra column
*123456789A*
1+---+ +--+1
2| | | |2
3| | +--+3
4| | 4
5+---+ 5
*123456789A*
We attempt to place a widget at 0x0 but it doesn't fit because there is a 50x50 widget at that coordinate.
So we then advance the current x coordinate being scanned to 51 and check again.
We then find a 40x30 widget at 0x61.
So we then advance the x coordinate to 90 but this doesn't leave enough room for the widget being placed so we increment the y coordinate and reset x back to 0.
We know from the previous attempts that the widgets on the previous line are at least 30px high so we increase the y coordinate to 31.
We encounter the same 50x50 widget at 0x31.
So we increase x to 51 and find that we can place a widget at 51x31
Here is the javascript:
function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself
for (var y = 0; y < heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x < widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}
var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever
var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
}
}
y += heightOfShortestInRow - 1;
}
//TODO: Add the widget to the bottom
}
Here is the longer and more less elegant version that also adjusts the height of the container (I've just hacked it together for now but will clean it up later and edit)
function findSpace(width, height,
yStart, avoidIds // These are used if the function calls itself - see bellow
) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself
var bottomOfShortestInRow;
var idOfShortestInRow;
for (var y = yStart ? yStart : 0; y <= heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x <= widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}
var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever
colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
var widgetId = $collider.attr('data-widget-id');
if (!avoidIds || !$.inArray(widgetId, avoidIds)) { // If this is true then we are calling ourselves and we used this as the shortest widget before and it didnt work
bottomOfShortestInRow = colliderBottom;
idOfShortestInRow = widgetId;
}
}
}
y += heightOfShortestInRow - 1;
}
if (!yStart) {
// No space was found so create some
var idsToAvoid = [];
for (var attempts = 0; attempts < widthOfContainer; attempts++) { // As a worse case scenario we have lots of 1px wide colliders
idsToAvoid.push(idOfShortestInRow);
heightOfContainer = $ul.height();
var maxAvailableRoom = heightOfContainer - bottomOfShortestInRow;
var extraHeightRequired = height - maxAvailableRoom;
if (extraHeightRequired < 0) { extraHeightRequired = 0;}
$ul.height(heightOfContainer + extraHeightRequired);
var result = findSpace(width, height, bottomOfShortestInRow, idsToAvoid);
if (result.top) {
// Found a space
return result;
}
// Got a different collider so lets try that next time
bottomOfShortestInRow = result.bottom;
idOfShortestInRow = result.id;
if (!bottomOfShortestInRow) {
// If this is undefined then its broken (because the widgets are bigger then their contianer which is hardcoded atm and resets on f5)
break;
}
}
debugger;
// Something has gone wrong so we just stick it on the bottom left
$ul.height($ul.height() + height);
return { 'left': 0, 'top': $ul.height() - height };
} else {
// The function is calling itself and we shouldnt recurse any further, just return the data required to continue searching
return { 'bottom': bottomOfShortestInRow, 'id': idOfShortestInRow };
}
}
function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
// Existing element passed in
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
// Coordinates and dimensions passed in
t_x = [tAxis.left, tAxis.left + width];
t_y = [tAxis.top, tAxis.top + height];
}
var overlap = false;
$obsticles.each(function () {
var $this = $(this);
var thisPos = $this.position();
var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];
if (t_x[0] < i_x[1] && t_x[1] > i_x[0] &&
t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
overlap = this;
return false;
}
});
return overlap;
}
I'm building a little script to manipulate elements according to vertical page-scrolling.
This solution basically works fine, but I'm wondering if there is a more performant way to do this. I would love to keep everything smooth and fluent.
http://jsfiddle.net/d4NCE/1/
$(document).ready(function(){
var element = $("#element1");
var elementOffset = element.offset().top;
var windowHeight = $(window).height();
var distanceEntry = windowHeight*0.8; // distance from element
var distanceExit = windowHeight*0.2; // distance from element
var entry = elementOffset-distanceEntry; // entry point in px from pagetop
var exit = elementOffset-distanceExit; // exit point in px from pagetop
var stepping = (exit-entry)/100; // px to percentage
$("#entry").css("top", entry); // placing entry-marker
$("#exit").css("top", exit); // placing exit-marker
$(window).scroll(function(){
var scroll = $(window).scrollTop();
var value = (scroll-entry)/stepping; // CALCULATION PROGRESS OF ANIMATION
if(value>=100) {value=100;} // cutting top and bottom
if(value<=0) {value=0;} // cutting top and bottom
value = Math.round(value); // to integer
$("#element1").css("left", value); // WRITING VALUE TO ELEMENT
$("#head").html("Element-Movement in Percent: " + value);
});
});
Thank you!
Since the scroll event fires so often, you might see some slight improvements in performance if you move as much logic out of the scroll event as possible.
In your example all of the element selection could be moved out of the listener, and the if statements don't need >= and <=. < and > will achieve the same thing.
e.g.
var $win = $(window);
var $element1 = $("#element1");
var $head = $("#head");
$win.scroll(function(){
var scroll = $win.scrollTop();
var value = (scroll-entry)/stepping; // CALCULATION PROGRESS OF ANIMATION
if(value>100) {value=100;} // cutting top and bottom
if(value<0) {value=0;} // cutting top and bottom
value = Math.round(value); // to integer
$element1.css("left", value); // WRITING VALUE TO ELEMENT
$head.html("Element-Movement in Percent: " + value);
});
Not sure it this will give you noticable perf gains, but it shouldn't hurt.
Ok, by now I finalized my little script.
Again I would love to have some Input if there are ways to improve performance, or if there is a better approach to achieve my goal.
Already tried to store all important information in variables, so there are as few dom-scans as possible. Thank you guys!
Have a look at the script: http://jsfiddle.net/sVeXT/1/
$(document).ready(function(){
(function parallax() {
var win = $(window);
var windowHeight = win.height();
var distanceEntry = windowHeight*0.85; // distance from element
var distanceExit = windowHeight*0.20; // distance from element
var elements = [];
function elementsSetup() { // scans DOM for Elements and forwards their specs to "Element"-Constructor
elements = [];
$(".element").each(function(i, el){ // iteration through elements
var height = $(el).height();
var offset = $(el).offset().top;
elements.push(new Element(i, el, height, offset)); // link to constructor and adding to array
});
};
elementsSetup();
function Element(i, el, height, offset) { // constructor
this.el = el;
this.height = height;
this.i = i;
this.offset = offset;
this.entry = offset-distanceEntry+height/2; // calculations to center element
this.exit = offset-windowHeight/2+height/2; // calculations to center element
this.stepping = (this.exit-this.entry)/100; // to percentage
}
$(window).scroll( function(){ // when scrolling writing to CSS of elements
var scroll = win.scrollTop();
var x = elements.length-1;
for (var i = x; i>=0; i--) {
var value = valueCap( (scroll-elements[i].entry)/elements[i].stepping );
$(elements[i].el).css("left", value);
}
});
$(window).resize(function() { // in case of window-resize values are redefined
windowHeight = win.height();
elementsSetup();
});
function valueCap(val) { // caps value 0-100
if(val>100) {val=100;}
if(val<0) {val=0;}
return val;
}
})();
});
I want to add the vertical lines when I draw rectangle. The no of lines is dependent on the user and can be read from the text box.
I know the logic but somehow I am not able to get the answer.
I am calculating the width of the rectangle and then diving the width on the basis of no of vertical lines.
Click the checkbox near rectangle and draw using mouse down events
Please let me know where I am going wrong.
function PlotPitch()
{
var iPatches = document.getElementById('txtPatchCount').value;
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
canvas = document.getElementById('canvas2');
context = canvas.getContext('2d');
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX = iLeft + iPatch*mySel.w/iPatches;
context.moveTo(iX, iTop);
context.lineTo(iX, iBottom);
}
context.lineWidth=0.25;
context.stroke();
}
http://jsfiddle.net/K5wcs/4/
If I am adding this the code is breaking and I am not able to draw anything.
What you should do if you have 'strange' behaviour is to separate concerns, so in this case that could be by creating a function that you test separately, which draws the lines, then once it's tested ok, plug it in code by just calling the function. You should find quickly.
So begin by testing this :
function drawLines(Context, mySel, iPatches) {
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
var colWidth = mySel.w/iPatches ;
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX += colWidth;
Context.moveTo(iX, iTop);
Context.lineTo(iX, iBottom);
}
Context.lineWidth=0.25;
Context.stroke();
}
Good luck.
I am trying to perform some jquery animation on images, their positions and dimensions. What I'm trying to do is move the image clicked to the position of the Biggest Image(Position 1,p1, image).
What I have been able to do so far is rotate every image to the next forward position.
You can see in this fiddle.
What I have tried to do is to place the function movement inside a loop like so
for(var x = 0; x < 3; x++){
movement(checker);
}
At first thought I expected it to jus move every element 3 positions forward but that wasn't the case. Nothing noticeable happened. NB: checker is id number of the clicked image.
I've also thought that making the movement function go on more than the number of element(16) would also cause it to somewhat solve the problem. I change it to 32 expecting each element to move 2 positions.
function movement(checker){
var count = 1;
var first, last, positions, dimensions, leftPos, topPos, imgWidth, imgHeight;
while(count<=32){//Increasing the loops to 32 from 16
if(checker == 0){
checker = 16;
}
first = d.e("p"+checker);
if(checker == 1){
last = d.e("p"+16);
}else{
last = d.e("p"+(checker-1));
}
//console.log(checker);
positions = findPos(last);
dimensions = getCanvas(last);
leftPos = positions[0]; topPos = positions[1];
imgWidth = dimensions[0]; imgHeight = dimensions[1];
$("#p"+checker).animate({"left":leftPos, "top":topPos, "width":imgWidth, "height":imgHeight}, "fast");
checker--; count++;
}
I am at a lost of what to do now. Ideally what I want to do is put it in a loop that would have the parameters "continue until checker left and top positions == left and top positions of p1(initial)".
So my problem is getting the elements to move more than one position on the click. I'm not sure if I'm taking the right approach at this but any help would be appreciated.
Thank you in advance.
//move object
// current status: index of largest picture
var status = 1;
function movement(checker){
var count = 1;
var first, last, positions, dimensions, leftPos, topPos, imgWidth, imgHeight;
while(count<=16){
if(checker == 0){
checker = 16;
}
first = d.e("p"+checker);
if(checker == 1){
last = d.e("p"+16);
}else{
last = d.e("p"+(checker-1));
}
//console.log(checker);
positions = findPos(last);
dimensions = getCanvas(last);
leftPos = positions[0]; topPos = positions[1];
imgWidth = dimensions[0]; imgHeight = dimensions[1];
var animateCount = 0;
$("#p"+checker).animate({"left":leftPos, "top":topPos, "width":imgWidth, "height":imgHeight}, "fast", function() {
animateCount++;
// check if all 16 picture's animation was completed.
if (16 == animateCount) {
console.log('finished');
// update the index of largest picture
status = (status % 16) + 1;
// rotate all again if status is not equal to checker
if (status != checker)
movement(checker);
}
});
checker--; count++;
}
}