I have elements which are overlapping and I would like to prevent this. Here is a picture: http://grab.by/cB7t
Also, here is the CSS for those elements:
.navigationItem {
background: #808080;
-webkit-border-radius: 360px;
padding: 1.0em;
text-decoration: none;
color: #fff;
position: absolute;
-webkit-box-shadow: 0px 2px 5px #909090;
font-weight: bold;
text-shadow: 1px 1px 2px #707070;
font-size: 1.0em;
}
And here they are in the HTML:
play
register
our blog
contact us
about us
our rules`
As you can see, I am using them as simple styled links using the HTML a tag. The reason that their positions are absolute is because I am moving them using jQuery:
function moveAll() {
for(var i = 0; i < AMOUNT; i++) {
var random = Math.random() * 500;
$("#nav" + i).animate({"left": random + i + "px"}, "slow");
$("#nav" + i).animate({"top": random + i + "px"}, "slow");
}
}
When they move, though, they sometimes overlap which is annoying. How can I prevent them from overlapping? Thank you for your efforts.
Removing position:absolute would render them side by side.
JSFiddle
But if the whole point is to scatter them around randomly, then you will have to keep track of positioned elements and take that into account when calculating their position. You should save each link's position and calculate every next link's position according to previous already positioned links. There's simply no other way when you want random positions and non overlapping.
Final non-overlapping solution
This is a working example of non-overlapping functionality. If you'd want your links to not even touch, you should change < to <= and > to >= in the if statement condition.
Relevant code
var positions = [];
$(".navigationItem").each(function(){
var ctx = $(this);
var dim = {
width: ctx.outerWidth(),
height: ctx.outerHeight()
};
var success = false;
// repeat positioning until free space is found
while (!success)
{
dim.left = parseInt(Math.random() * 300);
dim.top = parseInt(Math.random() * 300);
var success = true;
// check overlapping with all previously positioned links
$.each(positions, function(){
if (dim.left < this.left + this.width &&
dim.left + dim.width > this.left &&
dim.top < this.top + this.height &&
dim.top + dim.height > this.top)
{
success = false;
}
});
}
positions.push(dim);
ctx.animate({
left: dim.left,
top: dim.top
}, "slow");
});
You can change the position value to relative.
See my example : http://jsfiddle.net/NmmX6/2/
I changed your loop so that it isn't id dependent :
function moveAll() {
$('.navigationItem').each(function(i,e){
var rdm = Math.random() * 500;
$(e).animate({"left":rdm + "px"}, "slow");
$(e).animate({"top": rdm + "px"}, "slow");
});
}
I tested it and did not find one case where it actually overlaps, but check it out.
Related
Sorry, this is going to be a little long...
A bit of context
As part of a larger project I'm trying to make a project timeline to include in a webpage used to remote control a music making program (called Reaper for those who are interested). I'm trying to get it to display the current play position, the project markers, and project regions. These are all served straight from the program's API, no problem getting the info. For starters I'm just trying to display the project markers, however I've been pulling my hair out for over a week now trying to get it to work.
Here's a quick screencap from inside the software to illustrate what I'm trying to emulate: Reaper ruler screencap
Normally I would use a progress bar for something like this, or one of the thousands of examples from the web, however I have no way of knowing the length of the project as the software doesn't limit it. As a result I fell back onto using a fixed scale of 10px per bar. Kind of arbitrary, I chose that as it's the optimal for a 5 minute song at 120 bpm. Not too worried about looks for the moment, I just want to get it to work haha.
The problem I have (code is included at the bottom) is that because I'm using absolute positioning for the markers in order to align them all from the left of the screen, they are pulled from the document flow and so I can't wrap them in the parent div. In the end I intend setting the parent div to an 80% width with a scrollbar to see the rest of the markers, so obviously I'm doing it wrong. However I can't seem to find any coded snippets of anything similar to what I'm trying to achieve.
So here is the actual question:
What sort of display/position/float CSS should I be using to do this instead of position: absolute and float: left? If I need JS to do it, then how to I go about it?
Thanks for any help you can bring me, be it actual code or just a nudge in the right direction!
Here's my (relevant) code:
index.html
<html>
<body>
<div id="timeline">
<div id="labels"></div>
<div id="markers"></div>
</div>
</body>
</html>
script.js:
// hardcoded for debugging purposes
// See section below about the API for info about how I get this data
var markers = [
{label: "Start", pos: "20.00000000000000"},
{label: "First", pos: "50.00000000000000"},
{label: "Second", pos: "200.00000000000000"},
{label: "Last", pos: "576.845412000000000"}
];
function draw_markers(marker_list) {
var label_html = "";
var marker_html = "";
$.each(marker_list, function(index, obj) {
var label = obj.label;
var offset = parseFloat(obj.pos) * 7; // obj.pos is mesured in bars, not px
label_html = label_html +
"<span class='label' style='margin-left:"+offset+"px'>" +
label + "</span>";
marker_html = marker_html +
"<span class='marker' style='margin-left:"+offset+"px'>|</span>";
});
document.getElementById("labels").innerHTML = label_html;
document.getElementById("markers").innerHTML = marker_html;
}
draw_markers(markers);
style.css:
html, body {
background: #eeeeee;
}
#timeline {
height: 4em;
width: 100%;
background-color: #9E9E9E;
}
#labels {
border-bottom: 1px solid black;
height: 50%;
width: 100%;
}
#markers {
height: 50%;
width: 100%;
}
.label {
position: absolute;
float: left;
}
.marker {
position: absolute;
float: left;
}
About the API
We're given a bunch of functions that poll the server at regular intervals and parse the (cleartext) responses. A typical response looks something like this:
MARKERLIST_BEGINMARKER_LIST
MARKER \t label \t ID \t position
...
MARKER_LIST_END
TRANSPORT \t playstate \t position_seconds \t isRepeatOn \t position_string \t position_string_beats
...
Using JS I split each line and use a switch statement to figure out what to do with each line. I then build up a global array containing all the marker in the project with just the info I need.
Alternatively you could use <canvas> to draw the timeline from Javascript (this is what I use for Song Switcher's web interface). Also you can get the project length using the GetProjectLength API function (for example, by calling a script to put the length into a temporary extstate then reading it from the web interface).
function Timeline(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.length = 0;
this.markers = [];
}
Timeline.prototype.resize = function() {
this.canvas.width = this.canvas.clientWidth;
this.canvas.height = this.canvas.clientHeight;
this.scale = this.length / this.canvas.width;
}
Timeline.prototype.update = function() {
this.resize();
this.ctx.fillStyle = '#414141';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.textBaseline = 'hanging';
for(var marker of this.markers)
this.drawMarker(marker);
}
Timeline.prototype.drawMarker = function(marker) {
const MARKER_WIDTH = 2;
const FONT_SIZE = 14;
const PADDING = MARKER_WIDTH * 2;
var xpos = this.timeToPx(marker.pos);
this.ctx.strokeStyle = this.ctx.fillStyle = 'red';
this.ctx.lineWidth = MARKER_WIDTH;
this.ctx.beginPath();
this.ctx.moveTo(xpos, 0);
this.ctx.lineTo(xpos, this.canvas.height);
this.ctx.stroke();
if(marker.name.length > 0) {
this.ctx.font = `bold ${FONT_SIZE}px sans-serif`;
var boxWidth = this.ctx.measureText(marker.name).width + PADDING;
this.ctx.fillRect(xpos, 0, boxWidth, FONT_SIZE + PADDING);
this.ctx.fillStyle = 'white';
this.ctx.fillText(marker.name, xpos + MARKER_WIDTH, PADDING);
}
}
Timeline.prototype.timeToPx = function(time) {
return time / this.scale;
}
var timeline = new Timeline(document.getElementById('timeline'));
timeline.length = 30; // TODO: Fetch using GetProjectLength
timeline.markers = [
{pos: 3, name: "Hello"},
{pos: 15, name: "World!"},
{pos: 29, name: ""},
];
timeline.update();
window.addEventListener('resize', timeline.update.bind(timeline));
#timeline {
height: 50px;
line-height: 50px;
image-rendering: pixelated;
width: 100%;
}
<canvas id="timeline"></canvas>
you can use divs as display: inline-block and set their width as percentages of deltas of the overlaping absolute timeline positions:
function draw_markers(marker_list) {
var label_html = "";
var marker_html = "";
var total = null;
var prev = 0;
$.each(marker_list, function(index, obj) {
var delta = parseFloat(obj.pos) - prev;
obj.delta = delta;
prev += parseFloat(obj.pos);
total += delta;
})
$.each(marker_list, function(index, obj) {
var label = obj.label;
var offset = parseFloat(obj.delta) / (total / 100); // obj.pos is mesured in bars, not px
label_html = label_html +
"<div class='label' style='width:"+offset+"%'>" +
label + "</div>";
marker_html = marker_html +
"<div class='marker' style='width:"+offset+"%'>|</div>";
});
document.getElementById("labels").innerHTML = label_html;
document.getElementById("markers").innerHTML = marker_html;
}
draw_markers(markers);
css:
html, body {
background: #eeeeee;
}
#timeline {
height: 4em;
width: 100%;
background-color: #9E9E9E;
}
#labels {
border-bottom: 1px solid black;
height: 50%;
width: 100%;
}
#markers {
height: 50%;
width: 100%;
}
.label {
position: relative;
display: inline-block;
}
.marker {
position: relative;
display: inline-block;
}
$(this).css({
position: 'absolute',
left: Math.random() * ($('.parentcontainer').width() - $(this).width()),
top: Math.random() * ($('.parentcontainer').height() - $(this).height())
});
I got this each loop that will randomly place elements within a div. The problem with this is that the elements will overlap each other sometimes because they are absolute positioned. Is there anyway to go around this in js to check position? or maybe rewrite this with margin values? Thanks a ton!
There's a few different ways you can do to achieve this. I find it easiest to try to define the problem in one sentence:
New square's position must be at least X distance from current square positions
Using this sentence, we can make some simple theories as to how the code will work.
Assuming all squares are 50x50 pixels, we can write some checks.
Here are some pseudo code steps we could follow:
Generate a random position for newSquare
Compare the x and y positions of newSquare to all existing squares
If either of the x and y positions of newSquare are further away from the other squares, newSquare can be placed
Otherwise, try again
var container = $('#container');
var squareSize = 50;
var containerSize = 500;
for (var i = 0; i < 20; i++) {
var foundSpace = false;
while (!foundSpace) {
// Generate random X and Y
var randX = Math.floor(Math.random() * (containerSize - squareSize));
var randY = Math.floor(Math.random() * (containerSize - squareSize));
var hitsSquare = false;
var squares = container.children();
squares.each(function(index, square) {
var square = $(square);
// parseInt() because .css() returns a string
var left = parseInt(square.css('left'));
var top = parseInt(square.css('top'));
// Check boundaries
var hitsSquareX = Math.abs(left - randX) < squareSize;
var hitsSquareY = Math.abs(top - randY) < squareSize;
// Will overlap a square
if (hitsSquareX && hitsSquareY) {
hitsSquare = true;
// jQuery break .each()
return false;
}
});
// If doesn't overlap any square
if (!hitsSquare) {
foundSpace = true;
var newSquare = $('<div class="square">');
newSquare.offset({
left: randX,
top: randY
});
container.append(newSquare);
}
}
}
#container {
position: relative;
}
.square {
position: absolute;
background-color: red;
width: 48px;
/* border adds 2px */
height: 48px;
/* border adds 2px */
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
</div>
You should look for collision detection.
In my opinion this is a great tutorial: https://www.youtube.com/watch?v=XYzA_kPWyJ8, but there are several other great ones out there.
Good luck :)
I am trying to slide image from left to right and after a set point it should again slide in reverse direction. This is my code somehow its not working as i am going wrong somewhere in the if statement.
(function($) {
var x = 0;
var y = 0;
//cache a reference to the banner
var banner = $("#banner");
// set initial banner background position
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
x++;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
}, 90);
if ($(banner.offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
})(jQuery);
div#banner {
width: 960px;
height: 200px;
margin: auto;
background: url(http://cdn-careers.sstatic.net/careers/gethired/img/companypageadfallback-leaderboard-2.png?v=59b591051ad7) no-repeat 0 0;
}
div#banner p {
font: 15px Arial, Helvetica, sans-serif;
color: white;
position: relative;
left: 20px;
top: 120px;
width: 305px;
padding: 20px;
background: black;
text-align: center;
text-transform: uppercase;
letter-spacing: 20px;
zoom: 1;
filter: alpha(opacity=50);
opacity: 0.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="banner"></div>
Firstly, you are using a IIFE (Immediately Invoked Function Expression) instead of a DOM ready handler. This code will only work if placed after the elements it references.
Use this shortcut for DOM ready that also provides a locally scoped $
jQuery(function ($) {...});
You also have a missing closing paren (or really a redundant $( as it is already a jQuery object):
JSFiddle: http://jsfiddle.net/g0gn4osy/7/
You also need to have a delta value that changes the direction when you hit a bound value. I sped up your timing to show this:
jQuery(function ($) {
var delta = 1;
var y = 0;
//cache a reference to the banner
var $banner = $("#banner");
// set initial banner background position
$banner.css('background-position', '0px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
window.setInterval(function () {
var position = parseInt($banner.css('background-position'));
if (position >= 40 || position < 0) {
delta = -delta;
}
position += delta;
$banner.css("background-position", position + 'px' + ' ' + y + 'px');
}, 10);
});
Notes:
You also had backgroundPosition instead of background-position for the CSS property. I prefer to use the values that match the css properties (personal choice only for maintenance).
To avoid the redundant $() issue, I recommend you prefix jQuery variables with $. e.g. $banner in this case. Then it becomes obvious you are dealing with a jQuery object.
I tend to use the current position of an element, rather than keep a global var running. This allows for external influences to change the position and still work. Have removed x and just use position.
Inspired and modelled on Gone Coding's answer.
I have expanded his example to take into account the image width and the view pane DIV width.
It now scrolls to image end and then back. You never scroll off the canvas or past a visible part of the image. It doesn't jerk or rock, just switches direction.
With awareness of the viewing box width you can easily adjust the width of div#banner to fit the display space and the code adjusts. Just remember to set the background image width imgW var.
I have also added:
Visual indicator for testing with a current position and scroll direction. (With -1 is scrolling left, +1 is scrolling right),
Image start position in px. (A minus number or Zero. With 0 is start image at left, Minus number is start image part way through i.e image pre-scrolled left)
Image start vertical position in px (to vertically pull image up/down. Useful if view pane height shorter than image height and you want to tweak what is seen)
Things to do:
Change image URL (background: url(IMAGENAMEHERE) no-repeat 0 0;)
Insert image width (var imgW = #PIXELWIDTH#;)
Play with WIDTH: and HEIGHT: of view pane (div#banner)
Enjoy.
Fiddle
Have a play http://jsfiddle.net/Lm5yk46h/
Image credit Mark Higgins | Dreamstime.com Image source for purchase
Javascript
jQuery(function ($) {
var delta = 1;
var imgW = 3000;//width of image px
var imgY = 0;//to shift image view vertically px (Minus or zero)
//cache ref to #banner
var $banner = $("#banner");
var viewpaneW = $banner.width();
var endpos = (imgW - viewpaneW);
var startpos = 0;//0 or negative number
// set initial banner background position
$banner.css('background-position', startpos + 'px' + ' ' + imgY + 'px');
// scroll background position every 20ms
window.setInterval(function () {
var position = parseInt($banner.css('background-position'));
// minus is left, plus is right
if (position >= 0 ) delta = -delta;//go left
if (position < (-1*endpos)) delta = (-1*delta);//go right
position += delta;//increment posn
$banner.css("background-position", position + 'px' + ' ' + imgY + 'px');
$("#indicator").text('Posn:' + position + ' | direction: ' + delta);
}, 20);
});
CSS
div#canvas {
background-color: #999;
width: 100%;
height: 400px;
margin:0;padding:10px;
}
div#banner {
width: 460px;
height: 300px;
margin: 10px;
background: url(https://digido.net/eg/newcastle-beach-3000x300.jpg) no-repeat 0 0;
}
div#banner p {
font: 13px Arial, Helvetica, sans-serif;
color: white;
position: relative;
left: 0;
top: 310px;
width: 99%;
padding: 10px;
background: black;
text-align: center;
text-transform: uppercase;
letter-spacing: 8px;
zoom: 1;
filter: alpha(opacity=50);
opacity: 0.5;
}
HTML
<div id="canvas">
<div id="banner">
<p id="indicator">Hit run</p>
</div>
</div>
Just put the if condition inside the setInterval. And check the syntax error. The if doesn't have a closing }:
// scroll up background position every 90 milliseconds
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
x++;
//x--;
if (banner.offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
}, 90);
Your "if" should be like this:
if ($(banner).offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
https://jsfiddle.net/wc4b2g97/
your if should be inserted inside your setInterval handler, so it would get evaluated every 90 milliseconds (thank you for correcting me).
Actually, your if is evaluted only the first time, when your javascript file is parse.
Add it into your setInterval and it should work as expected
I have this following animation:
<!DOCTYPE HTML>
<html>
<head>
<style>
.example_path {
position: relative;
overflow: hidden;
width: 530px;
height: 30px;
border: 3px solid #000;
}
.example_path .example_block {
position: absolute;
background-color: blue;
width: 30px;
height: 20px;
padding-top: 10px;
text-align: center;
color: #fff;
font-size: 10px;
white-space: nowrap;
}
</style>
<script>
function move(elem) {
var left = 0
function frame() {
left+=10 // update parameters
elem.style.left = left + 'mm' // show frame
if (left == 10000) // check finish condition
clearInterval(id)
}
var id = setInterval(frame, 1) // draw every 1ms
}
</script>
</head>
<body>
<div onclick="move(this.children[0])" class="example_path">
<div class="example_block"></div>
</div>
</body>
</html>
as you see, the blue block moves out of the rectangle if it crosses it. how do i have the blue block oscillate about the rectangular border to and fro keeping the speed constant throughout ...
(in my case the speed is 10 m/s aka 10 mm/ms)
You need to update code as: Here is working JSfiddle
function move(elem) {
var left = 0
var fwdMove = true;
function frame() {
if (left < 0) {
fwdMove = true;
} else if (left > 520) {
fwdMove = false;
}
fwdMove?left += 10:left -= 10
elem.style.left = left + 'px' // show frame
}
var id = setInterval(frame, 1) // draw every 1ms
}
We begin by adding a variable to track the direction that we're heading in. We don't want to modify how fast you're moving, so we use a positive or negative 1 to affect the position.
var direction = 1; // 1=Right, -1=Left
var left = 0
function frame() {
left+=(10 * direction); // update parameters
Because mm are a print-unit, and we're working in the browser, we'll change it to use px. If you really need to use mm, you'll have to find a way of converting between them for the box to stop at the appropriate spot.
elem.style.left = left + 'px' // show frame
Finally, we check whether we've gone past the bounds of the box, and if so, we put it back in the box and reverse the direction;
if (left <= 0) {
direction = 1; // Begin moving to the left
left = 0; // Put the box back in the path
} else if (left >= (530 - 20)) {
direction = -1; // Begin moving to the right
left = (530 - 20); // Put the box back in the path
}
JSFiddle.
I want to create a rectangle on mousedown that drags across a grid and remains there on mouseup, snapping to the gridlines and outputting the coordinates for top left and bottom right of the it's position (x1,x2,y1,y2). Any help on starting to build this would be much appreciated.
I have a 500x500 grid with squares of 10x10 (example - jsFiddle).
Grid Code:
function creategrid(size){
var standardW = Math.floor((500) / size),
standardH = Math.floor((500) / size);
var standard = document.createElement('div');
standard.className = 'grid';
standard.style.width = (standardW * size) + 'px';
standard.style.height = (standardH * size) + 'px';
for (var i = 0; i < standardH; i++) {
for (var p = 0; p < standardW; p++) {
var cell = document.createElement('div');
cell.style.height = (size - 1) + 'px';
cell.style.width = (size - 1) + 'px';
cell.style.position = 'relative'
cell.style.zIndex= '2';
standard.appendChild(cell);
}
}
document.body.appendChild(standard);
}
creategrid(10);
CSS for grid:
.grid {
margin: 0px auto auto;
border: 1px solid #000;
border-width: 0 1px 1px 0;
background-color: #CCC;
}
.grid div {
border: 1px solid #000;
border-width: 1px 0 0 1px;
float: left;
}
#tooltip {
text-align:center;
background:black;
color:white;
padding:3px 0;
width:150px;
position:fixed;
display:none;
white-space:nowrap;
z-index:3;
}
I've found some snapping code through google http://jqueryui.com/draggable/#snap-to but I am literally stuck (I'm a complete beginner at JQuery).
Alternatively if anyone has a better idea of how to do this then that would be more than welcome.
Some background if you want to suggest a different way to do it: This is for a website running off of an SQL server built in python and django. The data it outputs are jSON objects but otherwise I'm just using html, css and javacript/jQuery for the front end. -- Not sure if that info is useful or not.
EDIT added code for mouseover grid coordinates in jQuery
$(window).load(function() {
var tooltip = $('<div id="tooltip">').appendTo('body')[0];
$('.coords').
each(function() {
var pos = $(this).offset(),
top = pos.top,
left = pos.left,
width = $(this).width(),
height = $(this).height();
$(this).
mousemove(function(e) {
var x = ((e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft) - left).toFixed(0),
y = (((e.clientY + document.body.scrollTop + document.documentElement.scrollTop) - top)).toFixed(0);
$(tooltip).text( x + ', ' + y).css({
left: e.clientX + 20,
top: e.clientY + 10
}).show();
}).
mouseleave(function() {
$(tooltip).hide();
});
});
});
If i understood your question correctly, you don't really need jQueryUI for that.
You need to find mouse position snapped to the cell of the grid on mousemove and resize your selection rectangle.
function getMousePos (e) {
return {
'left': Math.floor((e.pageX - gridOffset.left) / cellSpacing) * cellSpacing,
'top': Math.floor((e.pageY - gridOffset.top) / cellSpacing) * cellSpacing
}
}
Here is an example - http://jsfiddle.net/4efTV/
I recommend you to use that plugin, jQuery UI, its really simple to use take a look at this fiddle: http://jsfiddle.net/promatik/hBQxb/
HTML
<div class="snap-box">snap box</div>
Javascript:
$( ".snap-box" ).draggable({ grid: [ 10,10 ] });
CSS:
.snap-box {
width: 50px;
height: 50px;
position: absolute;
z-index: 10;
}