I'm trying to draw a line from an element [.identifier] to the clicked element [ A, B, C series ]. I'm able to display the line but in the other direction, not sure why it is displaying in such a direction.
Here is my fiddle: https://jsfiddle.net/SampathPerOxide/u2afymxs/11/
Can someone help me to display a line between ".identifier" and the respective series element?
Expected result on clicking A series:
on clicking B series:
$('.seriesli').click(function() {
function adjustLine(from, to, line) {
var fT = from.offsetTop + from.offsetHeight / 2;
var tT = to.offsetTop + to.offsetHeight / 2;
var fL = from.offsetLeft + from.offsetWidth / 2;
var tL = to.offsetLeft + to.offsetWidth / 2;
var CA = Math.abs(tT - fT);
var CO = Math.abs(tL - fL);
var H = Math.sqrt(CA * CA + CO * CO);
var ANG = 180 / Math.PI * Math.acos(CA / H);
if (tT > fT) {
var top = (tT - fT) / 2 + fT;
} else {
var top = (fT - tT) / 2 + tT;
}
if (tL > fL) {
var left = (tL - fL) / 2 + fL;
} else {
var left = (fL - tL) / 2 + tL;
}
if ((fT < tT && fL < tL) || (tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)) {
ANG *= -1;
}
top -= H / 2;
line.style["-webkit-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-moz-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-ms-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-o-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-transform"] = 'rotate(' + ANG + 'deg)';
line.style.top = top + 'px';
line.style.left = left + 'px';
line.style.height = H + 'px';
}
adjustLine(
document.getElementById('div1'),
document.getElementById('div2'),
document.getElementById('line')
);
});
.identifier {
width: 10px;
height: 10px;
background-color: red;
position: absolute;
right: 45%;
top: 50%;
}
.series-div {
position: absolute;
right: 5%;
bottom: 30%;
}
.series-ul li {
list-style: none;
color: grey;
font-size: 1em;
font-weight: 600;
border: 2px solid grey;
display: table;
padding: 0.3em 0.1em;
text-align: center;
margin: 0.5em;
cursor: pointer;
}
#line {
position: absolute;
width: 2px;
margin-top: -1px;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="position;relative;">
<div class="identifier" id="div2"></div>
<div class="series-div">
<ul class="series-ul">
<li class="seriesli" id="div1">A series</li>
<li class="seriesli">B series</li>
<li class="seriesli">C series</li>
</ul>
</div>
<div id="line"></div>
<img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive firstcar-detail" style="width: 100%;">
</div>
I fixed your fiddle: https://jsfiddle.net/c4ju6a0p/
Code changes:
// Get actual position relative to viewport.
// See https://stackoverflow.com/a/11396681/117030
fromBCR = from.getBoundingClientRect();
toBCR = to.getBoundingClientRect();
var fT = fromBCR.top + from.offsetHeight / 2;
var tT = toBCR.top + to.offsetHeight / 2;
// Don't add offsetWidth. This connects to the middle, not the left edge.
var fL = fromBCR.left //+ from.offsetWidth / 2;
var tL = toBCR.left + to.offsetWidth / 2;
The problem was the line was being calculated with the incorrect position due to relative positioning. This can be seen more clearly when the relative CSS is commented out: https://jsfiddle.net/vust5nxf/
Also, don't add the offsetWidth if you want the line to go to the left edge.
update: didn't notice the code snippet... applied changes there, too. I also made one more change:
You need to pass the element that was clicked to adjustLine(), otherwise currently the line is drawn between the same two elements every time because the elements are hardcoded with ids.
As a style note: I would move the definition of function adjustLine() outside the click handler. This will make the code easier to read, and the function will only be created once, instead of every time a click is handled.
adjustLine(
this, // Element that was clicked.
document.getElementById('div2'),
document.getElementById('line')
);
$('.seriesli').click(function() {
function adjustLine(from, to, line) {
// Get actual position relative to viewport.
// See https://stackoverflow.com/a/11396681/117030
fromBCR = from.getBoundingClientRect();
toBCR = to.getBoundingClientRect();
var fT = fromBCR.top + from.offsetHeight / 2;
var tT = toBCR.top + to.offsetHeight / 2;
// Don't add offsetWidth. This connects to the middle, not the left edge.
var fL = fromBCR.left //+ from.offsetWidth / 2;
var tL = toBCR.left + to.offsetWidth / 2;
var CA = Math.abs(tT - fT);
var CO = Math.abs(tL - fL);
var H = Math.sqrt(CA * CA + CO * CO);
var ANG = 180 / Math.PI * Math.acos(CA / H);
if (tT > fT) {
var top = (tT - fT) / 2 + fT;
} else {
var top = (fT - tT) / 2 + tT;
}
if (tL > fL) {
var left = (tL - fL) / 2 + fL;
} else {
var left = (fL - tL) / 2 + tL;
}
if ((fT < tT && fL < tL) || (tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)) {
ANG *= -1;
}
top -= H / 2;
line.style["-webkit-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-moz-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-ms-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-o-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-transform"] = 'rotate(' + ANG + 'deg)';
line.style.top = top + 'px';
line.style.left = left + 'px';
line.style.height = H + 'px';
}
adjustLine(
this, // Element that was clicked.
document.getElementById('div2'),
document.getElementById('line')
);
});
.identifier {
width: 10px;
height: 10px;
background-color: red;
position: absolute;
right: 45%;
top: 50%;
}
.series-div {
position: absolute;
right: 5%;
bottom: 30%;
}
.series-ul li {
list-style: none;
color: grey;
font-size: 1em;
font-weight: 600;
border: 2px solid grey;
display: table;
padding: 0.3em 0.1em;
text-align: center;
margin: 0.5em;
cursor: pointer;
}
#line {
position: absolute;
width: 2px;
margin-top: -1px;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="position;relative;">
<div class="identifier" id="div2"></div>
<div class="series-div">
<ul class="series-ul">
<li class="seriesli" id="div1">A series</li>
<li class="seriesli">B series</li>
<li class="seriesli">C series</li>
</ul>
</div>
<div id="line"></div>
<img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive firstcar-detail" style="width: 100%;">
</div>
I am trying to make a walking animation, the program does that the character goes to the mouse click. but I can't make it when it's moving to change the image and when it's stopping return to the first image.
here is the code for the js file and the css file:
import "./styles.css";
var theThing = document.querySelector("#thing");
var cont = document.querySelector("#contentCont");
cont.addEventListener("click", getClickPosition, false);
function getClickPosition(e) {
theThing.src = "ddd2.gif";
var parentPosition = getPosition(cont);
var xPosition = e.clientX - parentPosition.x - theThing.offsetWidth / 2;
var yPosition = e.clientY - parentPosition.y - theThing.offsetHeight / 2;
var translate3dValue =
"translate3d(" + xPosition + "px," + yPosition + "px,0)";
theThing.style.transform = translate3dValue;
}
function getPosition(element) {
var xPos = 0;
var yPos = 0;
while (element) {
xPos += element.offsetLeft - element.scrollLeft + element.clientLeft;
yPos += element.offsetTop - element.scrollTop + element.clientTop;
element = element.offsetParent;
}
return {
x: xPos,
y: yPos
};
}
body {
background-color: #fff;
padding: 50px;
margin: 0;
}
#contentCont {
width: 600px;
height: 600px;
border: 15px #ededed solid;
overflow: hidden;
background-color: #f2f2f2;
cursor: pointer;
}
#thing {
width: 80px;
height: 80px;
transition: transform 0.7s ease-in;
}
right now its start at image 1 and change to image 2 when walking but doesnt return to image 1 when stopping.
The Goal :
The idea is to create an element grid (image gallery for exemple) that would infinitely loop on itself scrolling on two axes.
There should be no holes nor too much randomness (avoid having the same element randomly falling aside from itself). And this no matter how many element there is in the first place (it seems easy to infinite loop through a grid of 16 (4*4) elements, not that much over 17 (17*1). (My guess is that any prime number of elements is by definition a pain to make a grid of).
So I actually found a wonderful working exemple :
http://www.benstockley.com/
It's actually really close (probably better) than what I was imagining. Now it's using canvas and i tried looking at the javascript and it's a 30000 minified lines long script so I really can't read any core logic behind it.
Math side / Problem solving :
This is the logic and theory behind the problem, the math involved and the mindset.
How the program should process the list of elements so we have no holes, infinite grid, best repartion of the elements over all the axes.
My guess is that it somehow has to be procedural. I'm not sure if we should create grids or loop through the list on every axes (kind of like sudoku ? i don't know);
Pratical side / UI / UX :
Any advice on the technologies involved, pieces of code. I'm guessing it classic DOM is out of the way and that somehow canvas or 2D webgl will be mandatory. But I would love to hear any advice on this side.
Besides all the elements grid processing. The UI and UX involved in exploring a 2D infinite or vast layout in DOM or renderer is somehow not classical. The best technologies or advice on doing this are welcome.
Exemples :
I would welcome any working exemple that somewhat share an aspect of this problem.
I've got a fiddle that's set up to arrange your 2d grid.
It functions by using horizontal and vertical "step sizes". So, moving one step right in the grid advances the horizontal step size in the list. Moving one step down advances the vertical step size in the list (and they accumulate).
We allow the advances in the list to loop back to zero when the end is reached.
It likely makes sense to use a horizontal step size of 1 (so a row of your grid will maintain your list order). For the vertical step size, you want an integer that shares no common divisors with the list length. Though it's no guarantee, I used the (rounded) square root of the list length as something that will work in lots of cases.
I'll reproduce the fiddle here:
var list = ['red','green','blue','cyan','orange','yellow','pink'];
var hstep = 1;
var vstep = Math.ceil(Math.sqrt(list.length));
function getListItem(x,y) {
var index = x * hstep + y * vstep;
return list[index % list.length];
}
var elementSize = 30;
var gutterSize = 10;
function getOffset(x,y) {
return [10 + (elementSize + gutterSize) * x, 10 + (elementSize + gutterSize) * y];
}
var frame = $('.frame');
function drawElement(x,y) {
var listItem = getListItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': listItem
});
frame.append(element);
}
function drawElements() {
var x = 0, y = 0;
while (10 + (elementSize + gutterSize) * x < frame.width()) {
while (10 + (elementSize + gutterSize) * y < frame.height()) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
drawElements();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 300px;
position: relative;
overflow: hidden;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
.buttons {
position: absolute;
top: 0px;
width: 100%;
}
.buttons button {
position: absolute;
width: 30px;
height: 30px;
padding: 5px;
}
button.up {top: 0px; left: 46%;}
button.down {top: 355px; left: 46%;}
button.left {top: 160px; left: 15px;}
button.right {top: 160px; right: 15px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
</div>
<div class="buttons">
<button class="up">↑</button>
<button class="down">↓</button>
<button class="left">←</button>
<button class="right">→</button>
</div>
You can see I've left some simple buttons to implement movement, but they are not functional yet. If you wanted to continue implementation along the lines of what I've done here, you could render your elements to a certain range beyond the visible frame, then implement some sort of animated repositioning. The renderElements function here only renders what is visible, so you can use something like that and not get stuck in rendering infinite elements, even though there's no theoretical limit to how far you can "scroll".
#arbuthnott I edited your code to implement the exploration via decrementing relativeX and relativeY variables. Also I inserted an "origin" div (1x1 px, overflow visible). This DOM element will represent the X and Y origin. I'm not sure it's essential but it's really convenient.
Now my function currently remove all elements and reinsert all elements on each update (every 500ms for now).
The idear would be to find a way to compare which elements I need versus which one already exists.
Maybe storing existing elements into an array, and compare the array with the "query" array. Than see just the elements that are missing.
This is the idear, not sure about the implementation (I suck at handling arrays).
https://jsfiddle.net/bnv6mumd/64/
var sources = ['red','green','blue','cyan','orange','yellow','pink','purple'];
var frame = $('.frame'),
origin = $('.origin');
var fWidth = 600,
fHeight = 300,
srcTotal = sources.length,
srcSquare = Math.ceil(Math.sqrt(srcTotal)),
rX = 0,
rY = 0;
var gridSize = 30,
gutterSize = 5,
elementSize = gridSize - gutterSize;
function getSourceItem(x,y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x,y) {
return [gridSize * x,gridSize * y];
}
function drawElement(x,y) {
var sourceItem = getSourceItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': sourceItem,
});
origin.append(element);
}
function init() {
var x = 0, y = 0;
while ( gridSize * x < fWidth) {
while ( gridSize * y < fHeight) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
function updateElements() {
origin.empty();
var x = -Math.trunc(rX / gridSize) -1, y = - Math.trunc(rY / gridSize) -1;
while ( gridSize * x + rX < fWidth) {
while ( gridSize * y + rY < fHeight) {
drawElement(x,y);
y++;
}
y = -Math.ceil(rY / gridSize);
x++;
}
}
function animate() {
rX -= 5;
rY -= 5;
origin.css({left: rX, top: rY})
updateElements();
console.log("relative X : " + rX + " | relative Y : " + rY);
}
setInterval(animate, 500)
init();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 600px;
position: relative;
overflow: hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 25px;
height: 25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
<div class="origin" style="top:0;left:0;"></div>
</div>
This is my final snippet version (i will start to work on real implementation specific to my case now).
I think I optimized in a decent way DOM operations, code structure etc (I am very well open to suggestions though).
I now only update the elements that needs to be updated (click near the frame to show overflow)
https://jsfiddle.net/bnv6mumd/81/
var sources = ['red', 'green', 'blue', 'cyan', 'orange', 'yellow', 'pink', 'purple'];
var frame = $('.frame'),
origin = $('.origin');
var srcTotal = sources.length,
srcSquare = Math.round(Math.sqrt(srcTotal)),
fWidth = 200,
fHeight = 200,
cellSize = 50,
gutterSize = 20,
gridSize = [Math.floor(fWidth / cellSize) + 1, Math.floor(fHeight / cellSize) + 1],
aX = 0, // Absolute/Applied Coordinates
aY = 0,
rX = 0, // Relative/frame Coordinates
rY = 0;
function getSrcItem(x, y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x, y) {
return [cellSize * x, cellSize * y];
}
function getY() {
return Math.floor(-rY / cellSize);
}
function getX() {
return Math.floor(-rX / cellSize);
}
function drawElement(x, y) {
var srcItem = getSrcItem(x, y),
offsets = getOffset(x, y),
element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': srcItem,
}).attr({
"X": x,
"Y": y
});
origin.append(element);
}
function drawCol(x, y) {
var maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawElement(x - 1, y - 1);
y++;
}
}
function drawLign(x, y) {
var maxX = x + gridSize[0];
while (x <= maxX + 1) {
drawElement(x - 1, y - 1);
x++;
}
}
function drawGrid() {
origin.empty();
var x = getX(),
y = getY(),
maxX = x + gridSize[0],
maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawLign(x, y);
x = getX();
y++;
}
}
function updateX(x, y, diffX, diffY) {
if (Math.sign(diffX) == -1) {
drawCol(aX - 1, y);
$('[x=' + (aX + gridSize[0]) + ']').remove();
aX--;
} else if (Math.sign(diffY) == 1) {
drawCol(aX + gridSize[0] + 2, y);
$('[x=' + (aX - 1) + ']').remove();
aX++;
}
}
function updateY(x, y, diffX, diffY) {
if (Math.sign(diffY) == -1) {
drawLign(x, aY - 1);
$('[y=' + (aY + gridSize[0]) + ']').remove();
aY--;
} else if (Math.sign(diffY) == 1) {
drawLign(x, aY + gridSize[0] + 2);
$('[y=' + (aY - 1) + ']').remove();
aY++;
}
}
function animate() {
rX += 1;
rY += 1;
origin.css({
left: rX,
top: rY
});
var x = getX(),
y = getY(),
diffX = x - aX,
diffY = y - aY;
if (diffX) {
updateX(x, y, diffX, diffY)
};
if (diffY) {
updateY(x, y, diffX, diffY)
};
requestAnimationFrame(animate);
}
$('body').click(function() {
$(frame).toggleClass("overflow");
})
drawGrid();
animate();
.frame {
border: 2px solid black;
margin: 100px auto;
height: 200px;
width: 200px;
position: relative;
}
.overflow{
overflow:hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame overflow">
<div class="origin" style="top:0;left:0;"></div>
</div>
I have a square, when clicked it appears in a random location and it also change size (so for example if I have a 30px box but it is 10px from the left border I still get 10px outside the gamespace).
sometimes the square exceed his container border
How can I make sure that the square will never exceed his container?
function position() {
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
var position = "position:relative;top:" + positionTop + "%;left:" + positionLeft + "%;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin: 0;
padding-top: 30px;
}
#playSpace {
width: 90%;
height: 90%;
margin: 0 auto;
margin-top: 10px;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
You need to set position: relative; to the parent element and position: absolute; to the shape element. Then use max min value for random where max is the parent width/height and subtract the shape width/height ...
This is snippet before update
function position() {
var playSpace = document.querySelector('#playSpace');
var shape = document.getElementById("shape");
var maxHeight = playSpace.offsetHeight - shape.offsetHeight;
var maxWidth = playSpace.offsetWidth - shape.offsetWidth;
var positionTop = Math.random() * (maxHeight - 0) + 0;
var positionLeft = Math.random() * (maxWidth - 0) + 0;
// think of this like so:
// var positionTop = Math.random() * (max - min) + min;
// more information about it https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
var position = "position:absolute;top:" + positionTop + "px;left:" + positionLeft + "px;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin:0;
padding-top: 30px;
}
#playSpace {
position: relative; /* add this line */
width: 90%;
height: 90%;
margin: 0 auto;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
Updated after comment
Not sure how you added the size() function but probably the problem was with using ...cssText that you overwrote the changes. So now I changed the code with passing the element to the functions and then only change the single CSS statements which need to be changed.
function position(element) {
var playSpace = document.querySelector('#playSpace');
var shape = document.getElementById("shape");
var maxHeight = playSpace.offsetHeight - shape.offsetHeight;
var maxWidth = playSpace.offsetWidth - shape.offsetWidth;
var positionTop = Math.random() * (maxHeight - 0) + 0;
var positionLeft = Math.random() * (maxWidth - 0) + 0;
// think of this like so:
// var positionTop = Math.random() * (max - min) + min;
// more information about it https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
element.style.position = 'absolute';
element.style.top = positionTop + "px";
element.style.left = positionLeft + "px";
}
function size(element) {
var sizeMath = (Math.random() * 200) + 50;
element.style.width = sizeMath + "px";
element.style.height = sizeMath + "px";
}
document.getElementById("shape").onclick = function() {
size(document.getElementById("shape"));
position(document.getElementById("shape"));
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin:0;
padding-top: 30px;
}
#playSpace {
position: relative; /* add this line */
width: 90%;
height: 90%;
margin: 0 auto;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
You can use a specify the range (min/max) in Math.random function and then use this function Math.floor(Math.random() * (max - min + 1)) + min; to limit the returned value of the random function between min and max.
maxTop : height of the container - height of shape
maxLeft : width of the container - width of shape
minTop : 0
minLeft : 0
You need to use position:absolute and px value on shape for this to work
See code snippet:
function position() {
var minTop = 0;
var maxTop = document.getElementById("playSpace").clientHeight - document.getElementById("shape").clientHeight;
var minLeft = 0;
var maxLeft = document.getElementById("playSpace").clientWidth - document.getElementById("shape").clientWidth ;
var positionTop = Math.floor(Math.random() * (maxTop - minTop + 1) + minTop);
var positionLeft = Math.floor(Math.random() * (maxLeft - minLeft + 1) + minLeft);
var position = "position:absolute;top:" + positionTop + "px;left:" + positionLeft + "px;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin: 0;
padding-top: 30px;
text-align:center;
}
#playSpace {
width: 90%;
height: 90%;
border: 1px black solid;
position:relative;
display:inline-block;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
With CSS calc, limit the position inside playSpace area (you can use different units, here % and px).
Then with offsetTop/offsetLeft, get the real position of the square and avoid negative positions (when positionTop < 100px or positionLeft < 100px).
function position() {
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
var position = "position:relative;top: calc(" + positionTop + "% - 100px);left: calc(" + positionLeft + "% - 100px);";
return position;
}
document.getElementById("shape").onclick = function() {
var shapeDiv = document.getElementById("shape");
shapeDiv.style.cssText = position();
var top = shapeDiv.offsetTop;// Result of calc(" + positionTop + "% - 100px) in px
if(top < 0) {
shapeDiv.style.top = '0px';
}
var left = shapeDiv.offsetLeft;// Result of calc(" + positionLeft + "% - 100px) in px
if(left < 0) {
shapeDiv.style.left = '0px';
}
}
Don't forget to add position: relative to #playSpace, to get offsetTop/left correct
#playSpace {
position:relative;/* mandatory */
}
You can get the parent element size, so that in your scirpt, use a condition to prevent the square to exceed.
function position() {
var maxWidth = document.getElementById("playSpace").offsetWidth;
var maxTop = document.getElementById("playSpace").offsetHeight;
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
if (positionTop > maxTop) { positionTop = maxTop;)
if (positionLeft > maxWidth) { positionLeft = maxWidth;)
var position = "position:relative;top:" + positionTop + "%;left:" + positionLeft + "%;"
return position;
}
I'm not sure if .offsetWidth and .offsetHeight works well, but you got the idea :)
By tweaking just a little your positionTop and positionLeft variable, I managed to keep the square inside. You just need to change the maximum % that it needs to have to not exceed the container. I found that 75% for the top and 80% for the left seems to be the max!
var positionTop = Math.floor(Math.random() * 75) ;
var positionLeft = Math.random() * 80;
Here's a fiddle
If you set the containing element to position: relative; and the inner element with position: absolute; the inner elements top left right and bottom properties will calculate from the borders of the containing element.
Here is a good source of css positioning.
In your case additional validation should be set to positionTop and positionLeft.
positionTop should be in the interval [( shapeHeight/2 ) - (100 - shapeHeight/2 ) ]
positionLeft should be in the interval [( shapeWidth/2 ) - (100 - shapeWeight/2 ) ]
To see a working example simply copy code into notepad++ and run in chrome as a .html file, I have had trouble getting a working example in snippet or code pen, I would have given a link to those websites if I could get it working in them.
The QUESTION is; once I fire the laser once it behaves exactly the way I want it to. It increments with lzxR++; until it hits boarder of the game arena BUT if I hit the space bar WHILST the laser is moving the code iterates again and tries to display the laser in two places at once which looks bad and very choppy, so how can I get it to work so the if I hit the space bar a second time even whilst the laser was mid incrementation - it STOPS the incrementing and simply shoots a fresh new laser without trying to increment multiple lasers at once???
below is the Code:
<html>
<head>
<style>
#blueCanvas {
position: absolute;
background-color: black;
width: 932px;
height: 512px;
border: 1px solid black;
top: 20px;
left: 20px;
}
#blueBall {
position: relative;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 10px;
border-radius: 100%;
top: 0px;
left: 0px;
}
#laser {
position: absolute;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 1px;
top: 10px;
left: 10px;
}
#pixelTrackerTop {
position: absolute;
top: 530px;
left: 20px;
}
#pixelTrackerLeft {
position: absolute;
top: 550px;
left: 20px;
}
</style>
<title>Portfolio</title>
<script src="https://ajax.googleapis.com/
ajax/libs/jquery/1.12.4/jquery.min.js">
</script>
<SCRIPT LANGUAGE="JavaScript" type="text/javascript">
document.addEventListener("keydown", keyBoardInput);
var topY = 0;
var leftX = 0;
var lzrY = 0;
var lzrX = 0;
function moveUp() {
var Y = document.getElementById("blueBall");
topY = topY -= 1;
Y.style.top = topY;
masterTrack();
if (topY < 1) {
topY = 0;
Y.style.top = topY;
};
stopUp = setTimeout("moveUp()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYup();
console.log('moveUp');
};
function moveDown() {
var Y = document.getElementById("blueBall");
topY = topY += 1;
Y.style.top = topY;
masterTrack();
if (topY > 500) {
topY = 500;
Y.style.top = topY;
};
stopDown = setTimeout("moveDown()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYdown();
console.log('moveDown');
};
function moveLeft() {
var X = document.getElementById("blueBall");
leftX = leftX -= 1;
X.style.left = leftX;
masterTrack();
if (leftX < 1) {
leftX = 0;
Y.style.leftX = leftX;
};
stopLeft = setTimeout("moveLeft()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXleft();
console.log('moveLeft');
};
function moveRight() {
var X = document.getElementById("blueBall");
leftX = leftX += 1;
X.style.left = leftX;
masterTrack();
if (leftX > 920) {
leftX = 920;
Y.style.leftX = leftX;
};
stopRight = setTimeout("moveRight()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXright();
console.log('moveRight');
};
function masterTrack() {
var pxY = topY;
var pxX = leftX;
document.getElementById('pixelTrackerTop').innerHTML =
'Top position is ' + pxY;
document.getElementById('pixelTrackerLeft').innerHTML =
'Left position is ' + pxX;
};
function topStop() {
if (topY <= 0) {
clearTimeout(stopUp);
console.log('stopUp activated');
};
if (topY >= 500) {
clearTimeout(stopDown);
console.log('stopDown activated');
};
};
function leftStop() {
if (leftX <= 0) {
clearTimeout(stopLeft);
console.log('stopLeft activated');
};
if (leftX >= 920) {
clearTimeout(stopRight);
console.log('stopRight activated');
};
};
function stopConflictYup() {
clearTimeout(stopDown);
};
function stopConflictYdown() {
clearTimeout(stopUp);
};
function stopConflictXleft() {
clearTimeout(stopRight);
};
function stopConflictXright() {
clearTimeout(stopLeft);
};
function shootLaser() {
var l = document.getElementById("laser");
var lzrY = topY;
var lzrX = leftX;
fireLaser();
function fireLaser() {
l.style.left = lzrX; /**initial x pos **/
l.style.top = topY; /**initial y pos **/
var move = setInterval(moveLaser, 1);
/**continue to increment laser unless IF is met**/
function moveLaser() { /**CALL and start the interval**/
var bcrb = document.getElementById("blueCanvas").style.left;
if (lzrX > bcrb + 920) {
/**if the X axis of the laser goes beyond the
blueCanvas 0 point by 920 then stop incrementing the laser on its X
axis**/
clearInterval(move);
/**if statement was found true so stop increment of laser**/
} else {
lzrX++;
l.style.left = lzrX;
};
};
};
};
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
shootLaser();
};
if (i == 38) {
if (topY > 0) {
moveUp();
};
};
if (i == 40) {
if (topY < 500) {
moveDown();
};
};
if (i == 37) {
if (leftX > 0) {
moveLeft();
};
};
if (i == 39) {
if (leftX < 920) {
moveRight();
};
};
};
/**
!! gradual progression of opacity is overall
!! being able to speed up element is best done with setTimout
!! setInterval is constant regards to visual speed
!! NEXT STEP IS ARRAYS OR CLASSES
IN ORDER TO SHOOT MULITPLE OF SAME ELEMENT? MAYBEE?
var l = document.getElementById("laser");
lzrX = lzrX += 1;
l.style.left = lzrX;
lzrY = topY += 1;
l.style.top = lzrY;
**/
</SCRIPT>
</head>
<div id="blueCanvas">
<div id="laser"></div>
<div id="blueBall">
</div>
</div>
<p id="pixelTrackerTop">Top position is 0</p>
<br>
<p id="pixelTrackerLeft">Left position is 0</p>
</body>
</html>
Solved the problem with using a variable called "g" and incrementing it once the laser is shot!
var g = 0;
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
if (g < 1) {
shootLaser();
g++;
};
};