Move a coordinate along an angled line using mouse movement - javascript

Please run the code snippet below - you will see an angled line derived from the corners of a box. Given mouse movement, I would like to move the marker along this line - where either:
movement in x will move along the line, and additional movement in y will accelerate
movement in x will move along the line and you can get from start to finish with it (whatever is simplest)
So far I haven't got anywhere but I'll update if I do and many thanks for any help.
PS doesn't really matter if it doesn't move exactly along the line - it's the algorithm to move along an angle I'm interested in.
Fiddle: http://jsfiddle.net/ngr3dbhx/3/
var boxEl = document.getElementById('box');
var lineEl = document.getElementById('line');
var markerEl = document.getElementById('marker');
var rad, deg;
// Draw an angled line for demonstration purposes.
function getElementOffset (el) {
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
return {
top: rectTop,
left: rectLeft
};
}
function calcAndDrawAngle () {
var boxOffset = getElementOffset(boxEl);
var x1 = boxOffset.left;
var y1 = boxOffset.top;
var x2 = boxOffset.left + boxEl.offsetWidth;
var y2 = boxOffset.top + boxEl.offsetHeight;
var deltaX = x2 - x1;
var deltaY = y2 - y1;
rad = Math.atan2(deltaY, deltaX);
deg = rad * (180 / Math.PI);
lineEl.style.transform = 'rotate(' + deg + 'deg) translate(-50%, 0)';
}
// On mouse move I want to move the marker along the anged line..
// Do something with rad or deg?
document.addEventListener('mousemove', function (e) {
markerEl.style.top = e.clientY + 'px';
markerEl.style.left = e.clientX + 'px';
});
calcAndDrawAngle();
window.addEventListener('resize', calcAndDrawAngle);
html,
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#box {
position: absolute;
top: 20%;
left: 30%;
width: 35%;
height: 30%;
border: 1px solid #999;
}
#line {
position: absolute;
top: 0;
left: 0;
transform-origin: top left;
height: 1px;
width: 9999em;
background-color: black;
}
#marker {
position: absolute;
width: 8px;
height: 8px;
background-color: red;
}
<div id="box">
<div id="line"></div>
</div>
<div id="marker"></div>

you mean this?
https://jsfiddle.net/2q1nLh3q/1/
The formula is basic mathematics:
y = k * x + d
Where k is deltaY / deltaX and d is the point where the line crosses the y axis.
So your function could look like this:
document.addEventListener('mousemove', function (e) {
window.requestAnimationFrame(function() {
var boxOffset = getElementOffset(boxEl);
var k = boxEl.offsetHeight / boxEl.offsetWidth;
var d = boxOffset.top - boxOffset.left * k;
var mouseY = k * e.clientX + d;
markerEl.style.top = mouseY + 'px';
markerEl.style.left = e.clientX + 'px';
});
});

Using fairly simple math, calculate y for any x within limited range (subtracted 4 to place rectangle at center):
//var canvas = document.getElementById('canvas');
var boxEl = document.getElementById('box');
var lineEl = document.getElementById('line');
var markerEl = document.getElementById('marker');
var rad, deg;
var coords;
// Draw an angled line for demonstration purposes.
function getElementOffset (el) {
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
return {
top: rectTop,
left: rectLeft
};
}
function calcAndDrawAngle () {
var boxOffset = getElementOffset(boxEl);
var x1 = boxOffset.left;
var y1 = boxOffset.top;
var x2 = boxOffset.left + boxEl.offsetWidth;
var y2 = boxOffset.top + boxEl.offsetHeight;
coords = [{x:x1,y:y2},{x:x2,y:y1}];
var deltaX = x2 - x1;
var deltaY = y2 - y1;
rad = Math.atan2(deltaY, deltaX);
deg = rad * (180 / Math.PI);
lineEl.style.transform = 'rotate(' + deg + 'deg) translate(-50%, 0)';
}
// On mouse move I want to move the marker along the anged line..
// Do something with rad or deg?
document.addEventListener('mousemove', function (e) {
var x = Math.min(Math.max(e.clientX, coords[0].x), coords[1].x) - 4;
var y = coords[0].y + ((coords[1].y - coords[0].y) * (coords[1].x - x) / (coords[1].x - coords[0].x));
markerEl.style.top = y + 'px';
markerEl.style.left = x + 'px';
});
calcAndDrawAngle();
window.addEventListener('resize', calcAndDrawAngle);
html,
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#box {
position: absolute;
top: 20%;
left: 30%;
width: 35%;
height: 30%;
border: 1px solid #999;
}
#line {
position: absolute;
top: 0;
left: 0;
transform-origin: top left;
height: 1px;
width: 9999em;
background-color: black;
}
#marker {
position: absolute;
width: 8px;
height: 8px;
background-color: red;
}
<div id="box">
<div id="line"></div>
</div>
<div id="marker"></div>

Related

Drawing a path between points in javascript/react/css

I am trying to draw a path between points wihtout using canvas or any other librares.
Everything works fine for positive degrees, but it doesn't for negative..
My "points" are 150x150 [px], that's why there is that +75px in left position.
This is my code:
useEffect(() => {
let x1 = spider1.x;
let y1 = spider1.y;
let x2 = spider2.x;
let y2 = spider2.y;
if (x1 > x2) {
let xTmp = x1;
let yTmp = y1;
x1 = x2;
y1 = y2;
x2 = xTmp;
y2 = yTmp;
}
if (x1 === x2) {
const height = Math.abs(y2 - y1).toString();
setStyle({
width: '5px',
left: (x1 + 75).toString() + 'px',
top: `${Math.min(y1, y2) + 75}px`,
height: `${height}px`,
transform: 'rotate(0deg)'
});
} else {
const a = (y2 - y1) / (x2 - x1);
const radians = Math.atan(a);
const degrees = radians * (180 / Math.PI);
setStyle({
width: `${Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))}px`,
left: (x1 + 75).toString() + 'px',
top: `${Math.min(y1, y2) + 75}px`,
height: `5px`,
transform: `rotate(${degrees}deg)`,
transformOrigin: `${degrees > 0 ? '0% 0%' : '100% 100%'}`
});
}
}, [spider1, spider2]);
return <div className='net' style={style} ref={myRef}></div>;
And the net class:
.net {
position: absolute;
background-color: red;
z-index: 90;
}
On the screens we have the same objects, but the degrees on the second one are "-" and the line doesn't connect that two squares because of that
This example works. You will have to compare yours with mine as to what the differences are.
Note: I just wrote it from scratch rather using your example.
Just click "Random position"
var button = document.getElementById("new-points");
button.onclick = function() {
var xy1 = {
m_x : Math.floor((Math.random() * 200) + 1),
m_y : Math.floor((Math.random() * 200) + 1),
};
var xy2 = {
m_x : Math.floor((Math.random() * 200) + 1),
m_y : Math.floor((Math.random() * 200) + 1),
};
var dotA = document.getElementById("dotA");
dotA.style.left = (xy1.m_x - 5) + "px";
dotA.style.top = (xy1.m_y - 5) + "px";
var dotB = document.getElementById("dotB");
dotB.style.left = (xy2.m_x - 5) + "px";
dotB.style.top = (xy2.m_y - 5) + "px";
var line = document.getElementById("line");
var distance = Math.hypot(xy2.m_x - xy1.m_x, xy2.m_y - xy1.m_y);
line.style.width = distance + "px";
line.style.left = xy1.m_x + "px";
line.style.top = xy1.m_y + "px";
var angle = ((Math.atan2(xy1.m_x - xy2.m_x, xy2.m_y - xy1.m_y) + (Math.PI / 2.0)) * 180 / Math.PI);
line.style.transform = "rotate(" + angle + "deg)";
line.style.display = "block";
}
#new-points {
border: 1px solid black;
padding: 5px;
width: 200px;
text-align: center;
}
.dot {
position: absolute;
width: 10px;
height: 10px;
background: blue;
}
#line {
position: absolute;
border-top: solid 1px red;
height: 1px;
max-height: 1px;
transform-origin: 0% 0%;
}
<div id="dotA" class="dot"></div>
<div id="dotB" class="dot"></div>
<div id="line"></div>
<div id="new-points" style="">
Random position
</div>
I've also tried the atan method for the above and it doesn't work. You can try it yourself and see what happens.

Calculate position (X, Y) on cirlcle base on height

I think the title is a little bit confusing so I will try to say it better.
Image you have this code:
#NadrzFrontView:before {
--beginHeight: var(--startHeight);
--endHeight: var(--finishHeight);
animation: 2s fillin ease forwards;
content: "";
position: absolute;
bottom: 0;
width: 300px;
height: 0;
background-color: #00FFF5;
display: inline-block;
}
#NadrzFrontView {
width: 300px;
height: 300px;
border-radius: 50%;
border: 3px solid black;
position: relative;
overflow: hidden;
}
<div id="NadrzFrontView" style="--startHeight: 0%; --finishHeight: 50%;"> </div>
It looks like this:
Now, you can determent left, top position and height of an element using this script:
function getOffset(el) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
Ok, when we are able to find these then we go to our problem:
function SomeFunction() {
var thickness = 1;
var color = '#000000';
var off_nadrz = getOffset(document.getElementById('NadrzFrontView'));
var elem = document.getElementById('NadrzFrontView');
var pseudoStyle = window.getComputedStyle(elem, ':before');
var off_pseudo_elem = getOffset(elem);
off_pseudo_elem.top += parseInt(pseudoStyle.top, 10);
off_pseudo_elem.left += parseInt(pseudoStyle.left, 10);
off_pseudo_elem.height = parseInt(pseudoStyle.height, 10);
//finding middle of the circle
var x1 = off_nadrz.left + (off_nadrz.width / 2);
var y1 = off_nadrz.top + (off_nadrz.height / 2);
//draw point in the middle of circle
document.getElementById("AllLines").innerHTML += CreateHtmlPoint(color, x1, y1);
//fincding point on the side of an element
var x2; //I need to find this -- see more in examples
var y2; //I need to find this -- see more in examples
document.getElementById("AllLines").innerHTML += CreateHtmlPoint(color, x2, y2); //This does not work
}
function CreateHtmlPoint(color, cx, cy) {
cx -= 5; // - 5, because width of point is 10
cy -= 5; // - 5, because height of point is 10
return "<div style='padding:0px; z-index: 2; margin:0px; height: 10px; width: 10px; background-color:" + color + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; border-radius: 50%;' />";
}
So, now example of an x2 and y2 would look like this:
Basically, we know on what coordinades is top of circle (off_nadrz.top), bottom of circle (off_nadrz.top + (off_nadrz.height / 2)) and height of an circle (off_nadrz.height). We also know same things of an psuedo_element (blue thing in the circle). From this we need to calculate x2 and y2.
Thanks for every suggestion because I am fighting with this problem for 2 days now...
you have only to apply the circumference formula:
var y2 = y1 - ( off_pseudo_elem.height - off_nadrz.height / 2 );
once you have y2, calculate x2:
var r = off_nadrz.height / 2; // if the figure is a circle, not an ellipse
var x2 = x1 + Math.sqrt( r * r - ( y2 - y1 ) * ( y2 - y1 ) );

Gradient follows mouse but doesn't catch up

I've put together a simple jQuery script to create a radial gradient that follows the mouse with a delay, but since it's in a mousemove function, when the mouse stops moving the gradient doesn't catch up. Is there a simple way to make the gradient catch up with the mouse when the mouse stops without writing a function that's constantly running?
var xPos = 0;
var yPos = 0;
var dX = 0;
var dY = 0;
var repeater;
$(document).mousemove(function(event) {
windowWidth = $(window).width();
windowHeight = $(window).height();
mouseXpercentage = Math.round(event.pageX / windowWidth * 100);
mouseYpercentage = Math.round(event.pageY / windowHeight * 100);
dX = mouseXpercentage - xPos;
dY = mouseYpercentage - yPos;
xPos += (dX / 50);
yPos += (dY / 50);
$('.rgradient').css('background', 'radial-gradient(at ' + xPos + '% ' + yPos + '%, #e6e6e6, #1e1e1e)');
});
.rgradient {
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background: #1e1e1e;
background: radial-gradient( at center, #e6e6e6 #1e1e1e);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="rgradient"></div>
You can use requestAnimationFrame(), as it only runs once for each frame:
var xPos = 0;
var yPos = 0;
var dX = 0;
var dY = 0;
var mouseRaf = null;
var gradMoveRaf = null;
$(document).mousemove(function(event) {
if (!mouseRaf) {
mouseRaf = requestAnimationFrame(function() {
windowWidth = $(window).width();
windowHeight = $(window).height();
mouseXpercentage = Math.round(event.pageX / windowWidth * 100);
mouseYpercentage = Math.round(event.pageY / windowHeight * 100);
dX = mouseXpercentage - xPos;
dY = mouseYpercentage - yPos;
mouseRaf = null;
});
}
if (!gradMoveRaf) {
gradMoveRaf = requestAnimationFrame(gradMove);
}
});
function gradMove() {
xPos += (dX / 50);
yPos += (dY / 50);
$('.rgradient').css('background', 'radial-gradient(at ' + xPos + '% ' + yPos + '%, #e6e6e6, #1e1e1e)');
var absX = Math.abs(mouseXpercentage - xPos);
var absY = Math.abs(mouseYpercentage - yPos);
if (absX < 1 && absY < 1) {
gradMoveRaf = null;
console.log("stop");
} else {
gradMoveRaf = requestAnimationFrame(gradMove);
console.log("repeat");
}
}
.rgradient {
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background: #1e1e1e;
background: radial-gradient(at center, #e6e6e6 #1e1e1e);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="rgradient"></div>

Making div appear from off-screen

I'm doing this assignment where I have to use pure javascript to make a div appear from the side of the screen without having the browser expand. This is what I have so far:
function moveImg() {
var div = document.getElementsByTagName('div')[0];
var pos = 500;
var id = setInterval(Move, 500);
function Move() {
if (pos <= 0) {
clearInterval(id);
} else {
pos++;
div.style.right = pos + 'px';
div.style.top = pos + 'px';
}
}
}
moveImg()
div {
width: 50px;
height: 50px;
background: tomato;
}
<div></div>
I'd use transform generally.
I've done some maths too, I've commented to code but feel free to ask if there's anything you need help understanding.
function moveImg() {
var div = document.getElementsByTagName('div')[0];
var posX = document.body.clientWidth - div.offsetWidth; // start position for X
var posY = document.body.clientHeight - div.offsetHeight; // start position for Y
var tarX = posX / 2; // end position for X
var tarY = posY / 2; // end position for Y
var time = 5; // how many seconds should the animation take
var fps = 60; // frames per second, heigher is smoother but requires more power
var stepX = ((posX - tarX) / (time * fps)); // how far X should move each frame
var stepY = ((posY - tarY) / (time * fps)); // how far Y should move each frame
var id = setInterval(Move, (1000 / fps));
div.style.transform = "translate(" + posX + "px, " + posY + "px)";
div.style.opacity = "1";
function Move() {
if (posX <= tarX && posY <= tarY) {
clearInterval(id);
} else {
posX -= stepX;
posY -= stepY;
div.style.transform = "translate(" + posX + "px, " + posY + "px)";
}
}
}
moveImg();
body {
margin: 0;
width: 100vw;
height: 100vh;
}
div {
display: inline-block;
color: white;
padding: 15px 30px;
background: tomato;
opacity: 0;
}
<div>Example!</div>
Like what was said in the comments, you don't say in what direction you want your div to move, nor from which position it should start.
I've set the starting position of top:100, left:100 and reducing the top and left values by 1px each second:
function moveImg() {
var div = document.getElementsByTagName('div')[0];
var pos = 100;
var id = setInterval(Move, 500);
function Move() {
if (pos <= 0) {
clearInterval(id);
} else {
pos--;
div.style.left = pos + 'px';
div.style.top = pos + 'px';
}
}
}
moveImg()
div {
width: 50px;
height: 50px;
background: tomato;
position: relative;
top: 100px;
left: 100px;
}
<div></div>

Placing interactive box on top of the canvas on mouseleave

I'm trying to make a box follow the cursor on mousemove. However, it should stop following the mouse if
the user clicks the box
the user's mouse leaves the surrounding div
Here's what the current setup looks like:
$(function() {
$(document).mousemove(function(e) {
// assign the co-ords as vars so
var vert = e.pageY;
var horz = e.pageX;
// get co -ords for the center of the box
centx = Math.ceil($('div').width() / 2);
centy = Math.ceil($('div').height() / 2);
// checking to see if the cursor is outside the boudries of the box and stoping the circle moving past them
if (e.pageY + 53 > Math.ceil($('div').height())) var vert = Math.ceil($('div').height() - 47);
if (e.pageY - 53 < 0) vert = 53;
if (e.pageX + 53 > Math.ceil($('div').width())) var horz = Math.ceil($('div').width() - 47);
if (e.pageX - 53 < 0) horz = 53;
// work out the distance between the cursor and the center of box
disx = horz - centx;
disy = vert - centy;
// work out the scale of the shadow
sx = -disx * 0.1;
sy = -disy * 0.1;
// work out the shadow blur
b = (Math.abs(sx) + Math.abs(sy)) * 0.5;
//apply the css
$('p').css({
'top': vert - 53,
'left': horz - 53,
'-webkit-box-shadow': '#a0a0a0 ' + sx + 'px ' + sy + 'px ' + b + 'px, inset ' + sx + 'px ' + sy + 'px ' + b + 'px #e3e3e3'
});
});
});
body {
padding: 0px;
}
div {
position: relative;
width: 400px;
height: 400px;
background-color: #f0f0f0;
}
p {
background-color: orange;
height: 100px;
width: 100px;
position: absolute;
top: 100px;
left: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<div>
<p>set the location</p>
</div>
fiddle
edit
To restrict movement once the box is clicked, just use a helper variable:
var cond = false;
$(yourElement).click(function () {
cond = true;
})
Then, in the body of your mousemove function:
if (cond) return false;
You should check whether the mouse is within the div using Element.getBoundingClientRect:
var boundary = document.getElementById("theDiv").getBoundingClientRect()
var vert = e.pageY;
var horz = e.pageX;
if (boundary.top <= vert && vert <= boundary.bottom &&
boundary.left <= horz && horz <= boundary.right) {
/* the rest of your code */
}
Element.getBoundingClientRect returns an object like this:
{
bottom: 408,
height: 400,
left: 8,
right: 408,
top: 8,
width: 400,
}
If you want to use jQuery, you can instead use
var left = $(item).offset().left
, top = $(item).offset().top
, right = left + $(item).width()
, bottom = top + $(item).height()
;
and set up a similar if condition.
demo:
$(function() {
var cond = false;
$("#theDiv").click(function () {
cond = true;
})
$(document).mousemove(function(e) {
if (cond) return false;
var boundary = document.getElementById("theDiv").getBoundingClientRect()
// assign the co-ords as vars so
var vert = e.pageY;
var horz = e.pageX;
if (boundary.top <= vert && vert <= boundary.bottom && boundary.left <= horz && horz <= boundary.right) {
// get co -ords for the center of the box
centx = Math.ceil($('div').width() / 2);
centy = Math.ceil($('div').height() / 2);
// checking to see if the cursor is outside the boudries of the box and stoping the circle moving past them
if (e.pageY + 53 > Math.ceil($('div').height())) var vert = Math.ceil($('div').height() - 47);
if (e.pageY - 53 < 0) vert = 53;
if (e.pageX + 53 > Math.ceil($('div').width())) var horz = Math.ceil($('div').width() - 47);
if (e.pageX - 53 < 0) horz = 53;
// work out the distance between the cursor and the center of box
disx = horz - centx;
disy = vert - centy;
// work out the scale of the shadow
sx = -disx * 0.1;
sy = -disy * 0.1;
// work out the shadow blur
b = (Math.abs(sx) + Math.abs(sy)) * 0.5;
//apply the css
$('p').css({
'top': vert - 53,
'left': horz - 53,
'-webkit-box-shadow': '#a0a0a0 ' + sx + 'px ' + sy + 'px ' + b + 'px, inset ' + sx + 'px ' + sy + 'px ' + b + 'px #e3e3e3'
});
}
});
});
body {
padding: 0px;
}
div {
position: relative;
width: 400px;
height: 400px;
background-color: #f0f0f0;
}
p {
background-color: orange;
height: 100px;
width: 100px;
position: absolute;
top: 100px;
left: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="theDiv">
<p>set the location</p>
</div>
fiddle

Categories