How to adjust the mouse position after rotating an object? - javascript

I am trying to create an eye that follows cursor movement.
I got the horizontal and vertical coordinate of the mouse and the browser width and height.
Everything works perfectly. Except that I used rotate(45 deg) on the design of the eye so now the ball is not moving in the right position.
I was thinking about a math equation that finds the distance between the old and new coords, but I am not sure how to implement it.
Here is the full code:
https://jsfiddle.net/Mr_MeS/3ym6kuec/3/
so this is the .eye where its rotated
.eye {
width: 37.5px;
height: 37.5px;
background: white;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
border-radius: 75% 0;
overflow: hidden;
cursor: pointer;
}
.ball {
width: 7.5px;
height: 7.5px;
background: #222f3e;
border-radius: 50%;
border: 5px solid #576574;
position: relative;
top: 50%;
left: 50%;
}
and here is the JS that does the work and needs to be edited.
var balls = document.getElementsByClassName("ball");
document.onmousemove = function () {
var x = event.clientX * 100 / window.innerWidth + "%";
var y = event.clientY * 100 / window.innerHeight + "%";
//event.clientX => get the horizontal coordinate of the mouse
//event.clientY => get the Vertical coordinate of the mouse
//window.innerWidth => get the browser width
//window.innerHeight => get the browser height
for (var i = 0; i < 2; i++) {
balls[0].style.left = x;
balls[0].style.top = y;
balls[0].style.transform = "translate(-" + x + ",-" + y + ")";
}
}
Now, if I remove the rotation from the .eye, it works perfectly, expect that the whole shape doesn't look to be in position.
If I keep the 45deg rotation, the shape is good, but the ball moves wrongly.

You could try to put the eye-background (the white part that needs to rotate 45 degrees) into a div (or pseudo-element) that's inside the .eye element. In that way you don't need to rotate the container element, so the coordination of the ball element stays the same.
Another point, why are you using that for-loop? I think running the code once will be sufficient :)
EDIT: I've been playing around with your example a bit and fixed it. What happens is that if you rotate an element, the direction in which things will transform (and top/left positioning) will also change. So moving the element 10px to the left, will go 10px to the left, under a 45 degree angle, because it's rotated 45 degrees.
What I did now was to put an element (.inner) inside the eye div, which I gave a counter-rotation of -45 degrees. In this way, the container element of the ball has the correct orientation again, which fixes the problem: https://jsfiddle.net/bxprjvgL/
HTML:
<div class="eye">
<div class="inner">
<div class="shut"><span></span></div>
<div class="ball"></div>
</div>
</div>
CSS:
.inner {
width: 100%;
height: 100%;
transform: rotate(-45deg);
}

Related

How to set the origin for a div?

Let's say I have a div that follows the mouse cursor on my screen. This is done by handling the document mousemove event and setting its left and top position based on the event data.
For this example let's say it has a width and height of 10.
It is desired for it to always be centered on the cursor. To make this happen the left and top need to be horizontally offset by half of div's width and vertically offset by half of the div's height. This allows it to be perfectly centered on the mouse cursor.
document.querySelector(".circle").style.left = e.x - (width / 2)
However, this is done in many places in the application and it seems cumbersome. Is it possible to instead set the origin such that when I set the left and top properties, I do not need to offset by half the width and height, since the origin would be at the center of the div?
The only thing I could find was the transform-origin property, but this seems to only apply to the transform CSS properties, and not properties like left, margin-left, etc.
Can this be done?
You could use:
transform: translate(-50%, -50%);
This calculates the percentage from the width and height of the element itself.
Example snippet:
const move = document.getElementById("move")
const mouseMoveHandler = e => {
move.style.left = e.pageX + 'px';
move.style.top = e.pageY + 'px';
}
window.addEventListener('mousemove', mouseMoveHandler)
#move {
position: absolute;
transform: translate(-50%, -50%);
}
<div id="move">I follow the cursor</div>
You can offset it absolutely relative to a parent who is 0x0 and positioned relatively, and further make the parent move with your mouse.
<!DOCTYPE html>
<html>
<head>
<style>
.origin {
position: relative;
width: 0px;
height: 0px;
}
.circle {
position: absolute;
top: -10px;
right: -10px;
width: 20px;
height: 20px;
border-radius: 50%;
border: 3px solid red;
}
</style>
</head>
<body>
<div class="origin">
<div class="circle"/>
</div>
</body>
</html>
See more details: https://www.w3schools.com/css/css_positioning.asp

Why does the circle in this code snippet lag behind the actual mouse cursor?

I am new to JavaScript and taking JavaScript Training course. The js code is supposed to render a circle at the mouse pointer location and listen for any changes.
const AREA = document.body;
const CIRCLE = document.querySelector('.circle');
function mouseCoordinates(e) {
var horizontalPosition = e.clientX - 26;
var verticalPosition= e.clientY - 26;
// Set horizontal and vertical position.
CIRCLE.style.left = horizontalPosition + 'px';
CIRCLE.style.top = verticalPosition + 'px';
}
AREA.addEventListener('mousemove', mouseCoordinates, false);
body {
width: 100vw;
height: 100vh;
}
.circle {
position: absolute;
top: 1px;
width: 50px;
height: 50px;
color: transparent;
border: 2px solid red;
border-radius: 50%;
}
<div class="circle"></div>
Initially the mouse pointer stays at the center of the circle, but when I am slightly increasing the speed of mouse movement the circle seems to "lag" behind.
Why does this happen?
Is this related to the performance of my system?
Isn't the listener supposed to run as and when the mouse moves and draw the circle exactly over the current position of the mouse?

3D animations using CSS and Javascript

On Steam there is a game called Dota 2. They had this section of "cards" that showed the characters you play. When you hover over the card, there is a really cool animation. The card lifts up and pivots towards the mouse pointer.
Pictures are attached. The red dot represents where the mouse is hovering:
Bottom-left:
Center:
Left-center:
Currently, I have a basic version here: https://codepen.io/riza-khan/pen/mdyvEeg but the animation isn't working as intended.
I will also post the code at the end of this question should Codepen links not be allowed on Stackoverflow. Some of the code is redundant, but I kept it in there to gain more insight.
const container = document.querySelector('.container')
const card = document.querySelector('.card')
let x = document.querySelector('.x-axis')
let y = document.querySelector('.y-axis')
container.addEventListener('mousemove', (e) => {
let xCoords = e.offsetX
let yCoords = e.offsetY
x.innerHTML = `xCoords:${xCoords}`
y.innerHTML = `yCoords:${yCoords}`
card.style.transform = `rotateY(${yCoords}deg) rotateX(${xCoords}deg) scale(1.1)`
})
container.addEventListener('mouseout', (e) => {
card.style.transform = `rotateY(0deg) rotateX(0deg) scale(1)`
})
container.addEventListener('click', (e) => {
console.log(e)
})
body {
display: flex;
height: 90vh;
background: grey;
}
.x-axis,
.y-axis {
position: absolute;
}
.y-axis {
left:100px;
}
.container {
margin: auto;
}
.card {
height: 500px;
width: 300px;
border-radius: 10px;
overflow: hidden;
transition: all 1s ease;
box-shadow: 10px 8px 20px -20px black;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.container:hover {
.card {
transform-style: preserve-3d;
}
}
<p class="x-axis"></p>
<p class="y-axis"></p>
<div class="container">
<div class="card">
<img src="https://vignette.wikia.nocookie.net/wowwiki/images/d/d9/Illidan.png/revision/latest?cb=20140503134345" alt="">
</div>
</div>
This is what you're looking for. To give a 3D feel to an object, you have to use the CSS property perspective. Here's a working example (also available on CodePen):
const container = document.querySelector('.container')
const card = document.querySelector('.card')
const output = document.querySelector('.output')
let x = document.querySelector('.x-axis')
let y = document.querySelector('.y-axis')
container.addEventListener('mousemove', (e) => {
let xOffset = e.offsetX
let yOffset = e.offsetY
let cardHeight = card.clientHeight
let cardWidth = card.clientWidth
let heightCenter = Math.round(cardHeight / 2)
let widthCenter = Math.round(cardWidth / 2)
let rotateBaseValue = 20
let rotateXValue = (yOffset - heightCenter) / heightCenter * rotateBaseValue
let rotateYValue = (widthCenter - xOffset) / widthCenter * rotateBaseValue
card.style.transform = `scale(1.1) rotateX(${rotateXValue}deg) rotateY(${rotateYValue}deg)`
})
container.addEventListener('mouseout', (e) => {
card.style.transform = ''
})
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100vw;
overflow: hidden;
}
body {
display: flex;
background: url(https://pbs.twimg.com/media/DySLFjlV4AEWf_F.jpg) no-repeat;
background-position: center bottom;
background-size: cover;
}
.container {
margin: auto;
perspective: 1000px;
}
.card {
height: 25vw;
width: 15vw;
border-radius: 10px;
overflow: hidden;
transition: all .25s linear;
}
.card img {
width: 100%;
height: 100%;
object-fit: cover;
}
.container:hover .card {
box-shadow: 10px 30px 50px -6px black;
}
<div class="container">
<div class="card">
<img src="https://66.media.tumblr.com/baf4a8fec55a6cb6fd7b18a7855998e4/tumblr_ply7xcI7pl1sjt61g_540.png" alt="Moonlight Cookie's Alluring Crescent Moon Costume">
</div>
</div>
EDIT
As per request, I will explain how I got the formula for rotateXValue and rotateYValue.
Before getting into that, you need to know what rotateX and rotateY do. rotateX rotates an item on the horizontal axis (x-axis) and rotateY rotates an item on the vertical axis (y-axis). Positive value on both rotateX and rotateY means that their movements are clockwise; negative means that their movements are counter-clockwise.
Try holding up a piece of paper. You'll notice that if you rotate the paper a little (doesn't matter how many degrees exactly) counter-clockwise on both x-axis and y-axis, you'll see that the paper seems exactly like when you are hovering on the top-right corner of the card. Try clockwise on both x-axis and y-axis, you'll see that the paper's pointing towards you like when you are hovering on the bottom-left corner of the card. Try all four combinations of different rotation directions.
After doing the above experiment, you can soon conclude that:
Top-left corner: rotateX counter-clockwise, rotateY clockwise
Top-right corner: rotateX and rotateY counter-clockwise
Bottom-left corner: rotateX and rotateY clockwise
Bottom-right corner: rotateX clockwise and rotateY counter-clockwise
Say that the maximum rotation is 15degrees. On the x-axis, value ranges from 15degrees to -15degrees (from left to right). On the y-axis, value ranges from -15degrees to 15degrees (from top to bottom). The card does not rotate when you're hovering in the middle of the card. Calculate the y-axis. The center is when the value is 0degree. Simply subtract the current y-offset with the center-offset and you'll get how much the distance is from the center-offset. Convert that to fraction relative to center-offset by dividing with the center-offset value. Multiply the fractional value to the maximum degrees to get how many degrees to rotate. Do the same with the x-axis (in this case, you need to invert the subtraction because the value ranges from positive to negative).
P.S.: This one was extremely fun to make. Thanks for the idea!

How to "glue" elements to resizable image?

I have image with width: 100%; height: auto; and on this image are other elements with position: absolute; left: x; top: x; (it's a game).
When I was resizing the page, elements weren't moving (it was messing up), so I wrote this:
// on start
var img = document.getElementById('world_image');
startImageSize.w = img.clientWidth;
startImageSize.h = img.clientHeight;
actImageSize.w = startImageSize.w;
actImageSize.h = startImageSize.h;
// on resize
var img = document.getElementById('world_image');
actImageSize.w = img.clientWidth;
actImageSize.h = img.clientHeight;
// update (each tick)
$("#cr_" + this.ID).animate({
"left" : (this.x - ((startImageSize.w - actImageSize.w) / 2)) + "px",
"top" : (this.y - ((startImageSize.h - actImageSize.h) / 2)) + "px"
}, speed);
But it's not working corretly, here is short GIF representing what is happening:
A "red point" is moving faster than it shoud be.
// EDDIT! JSFiddle with whole project https://jsfiddle.net/BrunonDEV/18mqyd8r/1/
What am I doing wrong?
I wouldn't try to reposition those in JS - it's slow, makes resizing janky, and chews the javascript cycles you'll need for actually running your game, and it's not necessary anyway. Why not just set your dot's position in %, like this:
.map {
background: #68B1FF url('https://upload.wikimedia.org/wikipedia/commons/5/5e/BlankMap-World-Sovereign_Nations.svg') center top no-repeat;
background-size: contain;
position: relative;
height: 0;
padding-top: 51.2%; /* The original map image is 1104 x 566px. This is the aspect ratio as a percentage of height to width */
width: 100%;
}
.marker {
border-radius: 50%;
width: 1vw; /* These allow the markers to scale with the map */
height: 1vw;
background: red;
min-height: 8px;
max-height: 16px;
min-width: 8px;
max-width: 16px;
position: absolute;
}
<div class="map">
<div class="marker" style="top: 20%; left: 33%;"></div>
</div>
One of the important pieces here is fixed aspect ratio boxes in CSS - if you set height to 0, and padding-top (or bottom) to a percentage, the padding height is calculated as a percentage of the width, not the height as you might expect.
For bonus points, you should use an SVG map image - then it'll scale cleanly, too.

Two arrows rotating inside a circle

So, the basic idea that I need to achieve is to have a simple circle that goes from 0 to 360 degrees.
Inside that circle are two arrows. I need them to rotate inside the circle.
One arrow needs to rotate degree per degree until it reaches a specified angle. The other needs to go directly to that angle.
So your arrows would both start at 0 degrees and if you specified you wanted to go to 100 degrees, one arrow would instantly jump and point towards 100 degrees, while the other would gradualy make it's way to 100 degrees.
EDIT:
Sorry about my lack of skill with stackoverflow (I just realised I never included a question into my question...). So I managed to get simple arrows down in canvas earlier through another question on stackoverflow, but when I started looking into actualy rotating the arrows, I just got lost in the code.
I guess my question was this: how can I apply rotation to my two arrows based on a degree value chosen by the user?
So here's what I managed to make my arrows with:
<html>
<head>
</head>
<body>
<canvas id="c" width="500" height="500"></canvas>
<script langauage="javascript">
<!--
ctx = document.getElementById("c").getContext("2d");
ctx.beginPath();
canvas_arrow(ctx,50,50,100,50);
canvas_arrow(ctx,50,50,10,30);
ctx.stroke();
function canvas_arrow(context, fromx, fromy, tox, toy){
var headlen = 10; // length of head in pixels
var dx = tox-fromx;
var dy = toy-fromy;
var angle = Math.atan2(dy,dx);
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle-Math.PI/6),toy-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle+Math.PI/6),toy-headlen*Math.sin(angle+Math.PI/6));
}
-->
</script>
</body>
</head>
The arrows are fine, but getting one of them to rotate while the other one jumps to the desired degree value is what I find hard. I can't find any examples or ideas on how to make them move based on a degree value given by a user.
With jQuery you can get the degrees depending on the mouse position -over your element, and apply the CSS3 transform rotate deg and set the animation transition time:
const $el = $('#circle'),
$cir = $el.children();
$el.on('click', evt => {
const o = $(evt.target).offset(),
rad = $el.width() / 2,
mPos = {
x: evt.pageX - o.left,
y: evt.pageY - o.top
},
a = Math.atan2(mPos.x - rad, mPos.y - rad),
d = -a / (Math.PI / 180) + 180;
$cir.css({transform: `rotate(${d}deg)`});
});
#circle {
position: relative;
width: 150px;
height: 150px;
border-radius: 50%;
font-family: arial, helvetica, sans-serif;
user-select: none; /* prevent text highlight */
cursor: pointer;
}
#circle>* {
text-align: center;
position: absolute;
border-radius: inherit;
width: inherit;
height: inherit;
}
#circle1 {
background: #eee;
transition: 1.3s;
}
#circle2 {
background: #fff;
transition: 0.3s;
top: 20px;
left: 20px;
width: calc(150px - 40px);
height: calc(150px - 40px);
}
<div id="circle">
<div id="circle1">▼</div>
<div id="circle2">▲</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

Categories

Resources