I want to append a div as a child of an other div without changing its position (from a user point of view).
The "futur" parent is rotated of -30° the element that will become a child is rotated of -30°.
how to compute the right (left, top) position of the futur child ?
I tried to cancel the rotation of the futur parent
transform:rotate(0°)
and do the same for the futur child
transform:rotate(60°)
but with a transform-origin set to the transform-origin of the futur parent that means
Left(futur child)-tranform-originx (parent)
top(futur child)-transform-originy (parent)
but it does not seem to run
any idea ?
HTML :
<div id="futurParentFrame" style="z-index:1000;position:absolute;left:100px;top:100px;background-color:red;border:black 6px dashed;width:800px;height:500px;transform:rotate(-30deg)">
</div>
<div id="futurChildDiv" style="z-index:1002;position:absolute;left:493px;top:192px;background-color:gray;border-left:blue 4px dashed;border-right:blue 2px dashed;width:250px;height:150px;transform:rotate(30deg);"></div>
The math is somehow complex. I have tried to done it in the most simple way possible.
Click on the button to create the new child. The old one is semitransparent so that you can see them overlapping
function createChild () {
var parent = document.getElementById("futurParentFrame");
var child = document.getElementById("futurChildDiv");
var parentCoor = getCoor (parent);
var childCoor = getCoor (child);
var newChild = child.cloneNode(true);
newChild.id = "newChild";
newChild.style.transform = "rotate(60deg)";
var r = Math.hypot (parentCoor.x - childCoor.x, parentCoor.y - childCoor.y);
var alpha = -30 * Math.PI / 180;
var relativeX = childCoor.x - parentCoor.x;
var relativeY = childCoor.y - parentCoor.y;
var rotatedX = relativeX * Math.cos(alpha) + relativeY * Math.sin(alpha);
var rotatedY = relativeX * -Math.sin(alpha) + relativeY * Math.cos(alpha);
var left = (parentCoor.width * 0.5) - parentCoor.borderLeft - (childCoor.width * 0.5) + rotatedX;
var top = (parentCoor.height * 0.5) - parentCoor.borderTop - (childCoor.height * 0.5) + rotatedY;
newChild.style.left = left + "px";
newChild.style.top = top + "px";
parent.appendChild(newChild);
}
function getCoor (element) {
var left = element.offsetLeft;
var top = element.offsetTop;
var width = element.offsetWidth;
var height = element.offsetHeight;
var centerX = left + width * 0.5;
var centerY = top + height * 0.5;
var borderLeft = parseInt (element.style.borderLeftWidth);
var borderTop = parseInt (element.style.borderTopWidth);
return {left: left,
top: top,
width: width,
height: height,
x: centerX,
y: centerY,
borderLeft: borderLeft,
borderTop: borderTop};
}
#futurChildDiv {
opacity: 0.5;
}
<div id="futurParentFrame" style="z-index:1000;position:absolute;left:100px;top:100px;background-color:red;border:black 6px dashed;width:800px;height:500px;transform:rotate(-30deg)">
</div>
<div id="futurChildDiv" style="z-index:1002;position:absolute;left:493px;top:192px;background-color:gray;border-left:blue 4px dashed;border-right:blue 2px dashed;width:250px;height:150px;transform:rotate(30deg);"></div>
<button onclick="createChild();">run</button>
Related
I'm working on my Javascript project.
In this project I have to create some animations.
In this specific case, I have to make a ball bounce up and down.
The code below works great just from the top to the bottom, but not viceversa.
var div = document.getElementById('container-ball');
function createBall(event)
{
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.style.position = "absolute"
newBall.style.width = '15px';
newBall.style.height = '15px';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
newBall.style.borderRadius = '10px';
newBall.style.backgroundColor = 'white';
var incrementPos = 0;
var id = setInterval(bounce, 5);
function bounce()
{
incrementPos++;
by = newBall.style.top = incrementPos + y + "px";
if(by == 650 + "px")
{
clearInterval(id)
var id2 = setInterval(function bounceUp()
{
incrementPosYMax -= 650
by = newBall.style.bottom = by + "px" - incrementPosYMax
}, 5)
}`/*Function that make the ball bounce down and up(but when it came at 650 px it stopped )*/ì
} /*End of the set interval */`
div.appendChild(newBall);
}
div.addEventListener("click", createBall);
This down below is the HTML CODE
<html>
<head>
<link rel= "stylesheet" href="style/style.css">
</head>
<body>
<div id ="container-ball">
</div>
<script src="js/main.js"></script>
</body>
Working example (comments see below):
const areaHeight = 150; // it is 650px in the original question
var div = document.getElementById('container-ball');
function createBall(event) {
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.className = 'ball';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
var incrementPos = 0;
var id = setInterval(bounce, 5);
let direction = 1; // 1 = down, -1 = up
function bounce() {
incrementPos += direction;
by = newBall.style.top = incrementPos + y + "px";
if (by == areaHeight + "px" || by == y + 'px') {
direction = -direction;
}
}
div.appendChild(newBall);
}
#container-ball {
width: 300px;
height: 157px;
background: gray;
}
#container-ball .ball {
position: absolute;
width: 15px;
height: 15px;
border-radius: 10px;
background-color: white;
border: 1px solid #333;
box-sizing: border-box;
}
<div id="container-ball" onclick="createBall(event)"></div>
Click on the grey box
Now, the explanation.
I've moved ball's styles to CSS - this is easier to control in the future. So when I have a class for ball, I can write in my code: newBall.className = 'ball';
I removed incrementPosYMax because I do not really understand if you need it
I understand your 'bounce' as bounce, so my ball just fall to the floor and then return to the original position. I do not really understand if you mean that (please, comment if it is wrong).
Your program is quite small, so I do not see the need for another setInterval, so all the animation in my example is inside only one setInterval
I've added new variable direction to control the direction for the ball (1 = down, -1 = up)
I do not like the parts with by == areaHeight + "px", but I keep them for you, because you use it in your code.
This code have some bugs, that you (or me if you ask) can fix. I just need to understand that my approach is correct
How the direction works:
Take a look at this line by = newBall.style.top = incrementPos + y + "px"; here you set new "y" coordinate for the ball as sum of 'original' "y" coordinate (in y) and offset (the distance that ball moved over the time) in incrementPos. So, if you increase incrementPos, then the ball's position will be lower (because "zero" in browser is at the top left corner, bigger "y" means lower the element).
Before my change, in your code you changed the offset with this line: incrementPos++; (means you increase incrementPos by 1 on every bounce step).
To move to another direction, you need to subtract 1 on every bounce step.
To reflect the "direction" of that move, I've added direction variable (so 1 means move down, and -1 means move up)
Now the offset is changed by: incrementPos += direction; (so I add this direction, not always 1)
Sometimes we need to change the "direction" with this code: direction = -direction;
The "levels" where we need to change direction is checked by this code: if (by == areaHeight + "px" || by == y + 'px') - here we check bottom (areaHeight) and top (y - it is where user clicks the mouse)
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
I am attempting to code a custom mouse trail. To do this I am using temporary divs that appear at the mouse location and fade out after a small interval. Currently the divs only appear at mouse location. I am trying to make them appear at where the mouse is at the moment and stay there while the mouse moves on and draws more divs. Also, how would I add a fade out effect to each individual div so after being placed it would slowly go transparent.
Here's my code so far.
var posX,
posY,
paused = null,
circles = [],
maxCircles = 30,
//width & height of div
circleSize,
//border radius to make it look like a circle
rad,
randCircle;
$(document).ready(function() {
for (var i = 0; i < maxCircles; i++) {
circleSize = Math.floor((Math.random() * 10) + 5);
rad = Math.floor(circleSize / 2);
circles[i] = document.createElement("Div");
circles[i].style.opacity = "0.6";
circles[i].className = "circle";
circles[i].style.width = circleSize + "px";
circles[i].style.height = circleSize + "px";
circles[i].style.borderRadius = rad + "px";
circles[i].style.background = "green";
circles[i].style.zIndex = "-1";
}
$("body").mousemove(function(e) {
posX = e.clientX - 5;
posY = e.clientY - 5;
randCircle = Math.floor((Math.random() * (maxCircles - 1)) + 0);
if (!paused) {
document.getElementsByTagName("body")[0].appendChild(circles[randCircle]);
$('.circle').css({
position: "absolute",
top: posY,
left: posX
});
paused = setTimeout(function() {
paused = null
}, 100);
}
});
});
body, html {
height: 100%;
background: #eee;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Your divs change position because you're setting the position of every div with the class name "circle".
You need to change
$('.circle').css({position:"absolute", top:posY,left:posX});
To
circles[randCircle].css({position:"absolute", top:posY,left:posX});
To make them fade out you could add an animation to the circles using CSS.
I know little about JavaScript but, as with everything I do, I'm throwing myself headfirst into some complex code (at least I think it's complex).
The code is basically designed to fit as many (approx.) 300x200 images into a div called "supercontainer".
A div called "container" is supposed to be created and then this is supposed to be populated with images.
The images will be resized slightly to make them fit in more aesthetically and nr of rows and columns are calculated also.
I'm not so concerned about how the logic of calculating number, or size, or position of images (or boxes) works, because I think this is sound.
I'm more concerned with my syntax and use of javascript language itself because literally nothing is displayed at the moment.
The 'setupboxes' function is called within my 'index.php' and I'm pretty sure I couldn't have got that wrong.
See below the .js code:
function setupboxes() {
var stdbw=300; //standard box width
var stdbh=200; //standard box height
var w=document.getElementById("containerparent").clientWidth; //get width of containerparent
var h=document.getElementById("containerparent").clientHeight; //get height of containerparent
var b=10; //set containerparent padding
var p=b; //set box padding
if(w>999) {
b=20; //set containerparent padding to 20px if screen width over 999px
p=b; //set box padding to 20px if screen width over 999px
}
var nrcols=Math.round((w-2*b)/(stdbw+2*p)); //set integer for nr of columns
var nrrows=Math.round((h-2*b)/(stdbh+2*p)); //set integer for nr of rows
var nrboxes=nrcols*nrrows; //calculate nr of boxes
var bh=(((h-2*b)/nrrows)-2*p); //set box height
var bw=(((w-s*b)/nrcols)-2*p); //set box width
var top=(h-(nrcols*(bh+2*p))); //set distance of container from top of containerparent
var left=(w-(nrrows*(bw+2*p))); //set distance of container from left of containerparent
var conth=((nrcols*(bh+2*p))+2*b); //set height of container
var contw=((nrrows*(bw+2*p))+2*b); //set width of container
var divcont=document.getElementById('containerparent').insertAdjacentHTML('afterbegin', '<div id="container"></div>'); //create container
divcont.style.position = 'relative';
divcont.style.top = top;
divcont.style.left = left;
divcont.style.border = 'red 1px solid';
var nrboxesit=nrboxes;
var boxnr=1;
while(nrboxesit>0){ //create boxes
document.getElementById('container').insertAdjacentHTML('beforeend', '<img src="images/0000001.jpg">');
img.style.padding = p;
img.style.height = bh;
img.style.width = bw;
boxnr=boxnr+1;
nrboxesit=nrboxesit-1;
}
}
Like I say I'm new to JavaScript so please don't assume I would have known to try some simple fixes!
Thanks in advance.
I made the minimum number of changes to get images to display. Specifically I didn't know what 's' stood for, so I set it equal to 10.
divcont is assigned to the element before attempting to insert HTML to it.
img near the end is undefined, I assigned an id and used that for a reference. Perhaps you won't want to wastefully assign ids like that.
function setupboxes() {
var stdbw = 300; //standard box width
var stdbh = 200; //standard box height
var w = document.getElementById("containerparent").clientWidth; //get width of containerparent
var h = document.getElementById("containerparent").clientHeight; //get height of containerparent
var b = 10; //set containerparent padding
var p = b; //set box padding
// FIXME
var s = 10;
if (w > 999) {
b = 20; //set containerparent padding to 20px if screen width over 999px
p = b; //set box padding to 20px if screen width over 999px
}
var nrcols = Math.round((w - 2 * b) / (stdbw + 2 * p)); //set integer for nr of columns
var nrrows = Math.round((h - 2 * b) / (stdbh + 2 * p)); //set integer for nr of rows
var nrboxes = nrcols * nrrows; //calculate nr of boxes
var bh = (((h - 2 * b) / nrrows) - 2 * p); //set box height
var bw = (((w - s * b) / nrcols) - 2 * p); //set box width
var top = (h - (nrcols * (bh + 2 * p))); //set distance of container from top of containerparent
var left = (w - (nrrows * (bw + 2 * p))); //set distance of container from left of containerparent
var conth = ((nrcols * (bh + 2 * p)) + 2 * b); //set height of container
var contw = ((nrrows * (bw + 2 * p)) + 2 * b); //set width of container
// FIXME
var divcont = document.getElementById('containerparent');
divcont.insertAdjacentHTML('afterbegin', '<div id="container"></div>'); //create container
divcont.style.position = 'relative';
divcont.style.top = top;
divcont.style.left = left;
divcont.style.border = 'red 1px solid';
var nrboxesit = nrboxes;
var boxnr = 1;
while (nrboxesit > 0) { //create boxes
// USED PLACEHOLDER IMAGE HERE
document.getElementById('container').insertAdjacentHTML('beforeend', '<img id="myBox' + nrboxesit + '" src="http://lorempixel.com/300/200/">');
// FIXME img is undefined. I just added an id based on
// current iteration of box number
var img = document.getElementById('myBox' + nrboxesit);
img.style.padding = p;
img.style.height = bh;
img.style.width = bw;
boxnr = boxnr + 1;
nrboxesit = nrboxesit - 1;
}
}
setupboxes();
<div style="display: inline-block; width: 600px; height: 600px;" id="containerparent">
<div style="display: inline-block; width: 600px; height: 600px;" id="container">
</div>
</div>
I made a carousel using 2 divs named "left" and "right" putting mousemove events on them. I wanted to make it go up and down as well so I created a "top" and "bottom" and noticed that I couldn't make them combine to go the way the cursor goes.
I thus thought of targeting a specific area in the container (i.e top half of my container div) instead of creating divs inside triggering a specific direction, this way (I think) I can trigger all these event altogether. However after now hours of research I couldn't find a way to do so.
How should I proceed ? here is the code : http://jsfiddle.net/pool4/vL5g3/3/
var x=0,
y=0,
rateX=0,
rateY=0,
maxspeed=10;
var backdrop = $('.backdrop');
$('.directionx', backdrop).mousemove(function(e){
var $this = $(this);
var left = $this.is('.left');
var right = $this.is('.right');
if (left){
var w = $this.width();
rateX = (w - e.pageX - $this.offset().left + 1)/w;
}
else if (right){
var w = $this.width();
rateX = -(e.pageX - $this.offset().left + 1)/w;
}
});
$('.directiony', backdrop).mousemove(function(e){
var $this = $(this);
var top = $this.is('.top');
var bottom = $this.is('.bottom');
if (top){
var h = $this.height();
rateY = (h - e.pageY - $this.offset().top + 1)/h;
}
else if (bottom) {
var h = $this.height();
rateY = -(e.pageY - $this.offset().top + 1)/h;
}
});
backdrop.hover(
function(){
var scroller = setInterval( moveBackdrop, 30 );
$(this).data('scroller', scroller);
},
function(){
var scroller = $(this).data('scroller');
clearInterval( scroller );
}
);
function moveBackdrop(){
x += maxspeed * rateX;
y += maxspeed * rateY;
var newpos = x+'px '+y+'px';
backdrop.css('background-position',newpos);
}
Your problem is that the divs that control movement up and down are placed over the ones that control left and right, so the latter do not receive the mousemove event ever. Mouse events do not propagate through layers, even if they're transparent. I changed your code and CSS, so each div is in one of the corners. To make things easier, I've used data-* attributes so the direction controlled by each div is set in a declarative way, without the need to change the code. You'll see that the code is much simpler (and it could be simplified even more).
By the way, you could achieve this witout extra divs, just controlling where the cursor is (to the top, right, left or bottom of the center of the div).
backdrop.on('mousemove', '.dir', function(e){
var $this = $(this);
var direction = $(e.target).attr('data-direction');
var left = direction.indexOf('left') > - 1;
var right = direction.indexOf('right') > - 1;
var top = direction.indexOf('up') > - 1;
var bottom = direction.indexOf('down') > - 1;
if (left){
var w = $this.width();
rateX = (w - e.pageX - $this.offset().left + 1)/w;
}
else if (right){
var w = $this.width();
rateX = -(e.pageX - $this.offset().left + 1)/w;
}
if (top){
var h = $this.height();
rateY = (h - e.pageY - $this.offset().top + 1)/h;
}
else if (bottom) {
var h = $this.height();
rateY = -(e.pageY - $this.offset().top + 1)/h;
}
});
I've updated your fiddle.
EDIT In this new fiddle I do it without extra divs:
var w = backdrop.width() / 2;
var h = backdrop.height() / 2;
var center = {
x: backdrop.offset().left + backdrop.width() / 2,
y: backdrop.offset().top + backdrop.height() / 2
};
backdrop.on('mousemove', function(e){
var offsetX = e.pageX - center.x;
var offsetY = e.pageY - center.y;
rateX = -offsetX / w;
rateY = -offsetY / h;
});
backdrop.hover(
function(){
var scroller = $(this).data('scroller');
if (!scroller) {
scroller = setInterval( moveBackdrop, 30 );
$(this).data('scroller', scroller);
}
},
function(){
var scroller = $(this).data('scroller');
if (scroller) {
clearInterval( scroller );
$(this).data('scroller', null);
}
}
);
As you see, the mousmove handler is considerably simpler.
To avoid issue of children losing event could use just the one.
First HTML from 4 child divs to just one
<div class="backdrop">
<div class="direction"></div>
</div>
<div id="pos"></div>
Next Inside the mousemove find your relative position
//Get Relative Position
var relX = e.pageX - $this.offset().left;
var relY = e.pageY - $this.offset().top;
Get Relative Position as a percentage of width and put 50% of it in negative for direction
var w = $this.width();
rateX = ((relX / w) - 0.5) * -1;
var h = $this.height();
rateY = ((relY / h) - 0.5) * -1;
Fiddle