Two arrows rotating inside a circle - javascript

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>

Related

Css jittery text translate

[SOLUTION]
Solution is to use the will-change CSS property taht forces GPU rendering:
will-change: transform;
[ORIGINAL QUESTION]
I've been digging a lot internet and found no solution to a problem that seems rather simple which is, translating a holder containing text, and having this text moving smoothly.
Here is an example of the problem, you can see the text is following its holder step by step, not smoothly:
I also made a small codepen to see the effect live :
https://codepen.io/Durss/pen/ExgBzVJ?editors=1111
var angle = 0;
var radius = 100;
function renderFrame() {
requestAnimationFrame(renderFrame);
var cx = document.documentElement.clientWidth / 2;
var cy = document.documentElement.clientHeight / 2;
var div = document.getElementById("text");
var px = cx + Math.cos(angle) * radius;
var py = cy + Math.sin(angle) * radius;
angle += .001;
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
}
renderFrame();
body {
background-color:black;
width:100%;
height:100%;
}
#text {
position:absolute;
left:0;
top:0;
border: 2px solid white;
background-color: #2f9da7;
padding: 10px;
border-radius:20px;
color: white;
font-weight: bold;
}
<div id="text">blah blah</div>
Basically, the problem is that the holder is moving at a subpixel level but the text position seems rounded no matter what i try.
I used translate3d() so it uses GPU rendering which fixes the holder's displacement but not its text content.
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
I've seen here and there the following CSS "solutions" that didn't work for me:
text-rendering: geometricPrecision;
-webkit-font-smoothing: antialiased;
transform-style: preserve-3d;
backface-visibility: hidden;
-webkit-font-smoothing:none;
I've had this problem many time in the past but always gave up fixing it,i think it's time to seek for help as a last bullet !
Thanks for reading me!
will-change: contents;
resolved my own jittery text issues when doing transform rotations. This tells the browser that the contents of an element are expected to change therefore should not be cached.
This must be applied immediately before the transform.
CSS transitions help on making sub-pixels animations smoother.
transition: all 0.001s linear;
It works fine on chrome, but seems a bit less effective on firefox.
This article may help :
Firefox CSS Animation Smoothing (sub-pixel smoothing)
As a workaround you can Math.round the px values. This would prevent the text from jittering on its own (makes the whole containing div jittery).
var angle = 0;
var radius = 100;
function renderFrame() {
requestAnimationFrame(renderFrame);
var cx = document.documentElement.clientWidth / 2;
var cy = document.documentElement.clientHeight / 2;
var div = document.getElementById("text");
var px = Math.round(cx + Math.cos(angle) * radius);
var py = Math.round(cy + Math.sin(angle) * radius);
angle += .001;
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
}
renderFrame();
body {
background-color:black;
width:100%;
height:100%;
}
#text {
position:absolute;
left:0;
top:0;
border: 2px solid white;
background-color: #2f9da7;
padding: 10px;
border-radius:20px;
color: white;
font-weight: bold;
}
<div id="text">blah blah</div>

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!

Image clip centre

I want my image to be clipped in the centre and be able to move around left right up down while being on cover to fit the whole screen
The user should be able to see only a certain portion of the image and be able to move around just like the link below but his viewpoint is the little rectangle in a fit to screen perspective
What i get so far is just the clipped upleft of my image
In case i am not clear what I am trying to achieve this effect but the user can't see move than the square
https://www.w3schools.com/howto/howto_js_image_zoom.asp
I will soon close this ticket if you happen to stumble on this check out this q I believe I am as as clear as I can here
How to clip your image freely
<style>
img {
background-position: cover;
position: absolute;
clip:rect(0px,500px,500px,0px);
}
.image1 {
background: url(images/bg.jpg) no-repeat center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
</style>
</head>
<body>
<div class='clipper-div'>
<img class='image1' src='office.gif'/>
</div>
The kind you were looking for is an inset clipping:
clip-path: inset(top bottom left right);
You can listen to the mouse move event to update the clipping. In the example below, I used CSS custom properties I added to the clipper-element style definition.
These custom properties are used as CSS variables for the clipping definition.
// Globals variables (we could store them into an object,
// which would be a cleaner way
var clipperDiv = document.getElementById("clipper-div");
var hoveringClippedImg = document.getElementById("hovering-clipped");
var imgBoundingRect = hoveringClippedImg.getBoundingClientRect();
var clippingSize = 40;
// Surrouding DIV element mouse move event callback
clipperDiv.onmousemove = clipHoveredArea;
// Update image clipping
function clipHoveredArea(e) {
// First step: getting clipping coordinates from mouse position
var pos = getMousePos(e);
var top = (pos.y - clippingSize / 2);
var bottom = (imgBoundingRect.height - pos.y - (clippingSize / 2));
var left = (pos.x - clippingSize / 2);
var right = (imgBoundingRect.width - pos.x - clippingSize / 2);
// Second step: CSS custom properties
hoveringClippedImg.style.setProperty("--top", top + "px");
hoveringClippedImg.style.setProperty("--bottom", bottom + "px");
hoveringClippedImg.style.setProperty("--left", left + "px");
hoveringClippedImg.style.setProperty("--right", right + "px");
};
// Get mouse position relative to an element
// Source: //stackoverflow.com/a/42111623/4375327
function getMousePos(e) {
// e = Mouse click event.
var rect = e.currentTarget.getBoundingClientRect();
var x = e.clientX - Math.round(rect.left);
var y = e.clientY - Math.round(rect.top);
return {
x: x,
y: y
};
}
#clipper-div {
border: 2px solid black;
width: 200px;
height: 200px;
}
#hovering-clipped {
padding: 0;
margin: 0;
width: 200px;
height: 200px;
clip-path: inset(var(--top) var(--right) var(--bottom) var(--left));
--top: 0px;
--right: 0px;
--bottom: 0px;
--left: 0px;
cursor: crosshair;
}
<div id='clipper-div'>
<img id="hovering-clipped"
src="//placehold.it/200x200/d0d8f8/000000" />
</div>
Note: I used Clippy. It's a handy tool to design the clipping you want.

How to adjust the mouse position after rotating an object?

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);
}

Categories

Resources