Correctly angling Div to point to cursor - javascript

I have created a div inside a container that points to the cursor. The issue is that the accuracy is off and I need to change the angle.
HTML
<div class="shooting_container">
<div class="shooter">
<div class="shooting_arm">
</div>
</div>
</div>
CSS
.shooter {
height: 480px;
width: 200px;
background-color: white;
bottom: 150px;
margin: 0 100px;
position: absolute;
.shooting_arm {
height: 245px;
width: 166px;
bottom: 500px;
position: absolute;
top: calc(50% - 40px);
transform: translateY(-50%) rotate(-12deg);
z-index: 500;
transform-origin: 156px 8px;
left: -43px;
background-color: red;
// Where the transform origin lies
&::after {
position: absolute;
top: 8px;
left: 156px;
width: 5px;
height: 5px;
content: '';
background-color: #f0f;
border-radius: 50%;
transform: translate(-50%, -50%);
}
}
}
JS
$(document).on('mousemove', moveCursor);
function moveCursor(e) {
var box = $(".shooter .shooting_arm");
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2];
var angle = Math.atan2(e.pageX - boxCenter[0], - (e.pageY - boxCenter[1]) )*(180/Math.PI) - 180;
if (angle < -160) {
angle = -0;
}
setArm(box, angle);
}
function setArm(arm, angle) {
arm.css({ "-webkit-transform": 'translateY(-50%) rotate(' + angle + 'deg)'});
arm.css({ '-moz-transform': 'translateY(-50%) rotate(' + angle + 'deg)'});
}
CodePen:
https://codepen.io/anon/pen/vrWJwZ
Problem 1: Angle is incorrect and differs as you move
Incorrect angle. I need the top of the container to align with the cursor, not the center. I tried adjusting:
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2];
to
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2/2];
But it still was not in line with the top of the container.
Problem 2: Glitches when cursor is within the div itself
For some reason it returns two different angle values when the cursor is within the container and causes it to jiggle. Why does it do this?

Related

How do I make the eyes follow the object?

Im not entirely sure what's wrong with my code. The eyes (iris) seem to be locked on the bottom right corner of the eyes and are rotating around on that fixed position. I've looked at tutorials online and tried variety of codes, but none seem to work. The end goal that I would like to achieve is to have the eyes follow the given block.
Thank you in advance!
window.addEventListener('mousemove', eyeball);
function eyeball() {
const eye = document.querySelectorAll('.eye');
eye.forEach(function(eye){
let x = (eye.getBoundingClientRect().left) + (eye.clientWidth / 2);
let y = (eye.getBoundingClientRect().top) + (eye.clientHeight / 2);
let radian = Math.atan2(event.pageX - x, event.pageY - y);
let rotation = (radian * (180 / Math.PI) * -1) + 270;
eye.style.transform = "rotate("+rotation+"deg)";
});
}
.eye-container {
position: absolute;
display: flex;
justify-content: center;
width: 100%;
margin-top: 20px;
background: pink;
}
.eyeball{
position: relative;
width: 100px;
height: 90px;
border-radius: 50%;
background: white;
margin-right: 20px;
box-shadow: inset 0 0 5px black;
overflow: hidden;
}
.pupil{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 45px;
border-radius: 50%;
background: black;
border: 10px solid purple;
box-sizing: border-box;
}
.block{
position: absolute;
top: 50%;
width: 100px;
height: 100px;
background: red;
}
<section class="eye-container">
<div class="eyelids eyelid-left"></div>
<div class="eyelids eyelid-right"></div>
<div class=" eyeball left-eye">
<div class=" eye pupil left-pupil"></div>
</div>
<div class="eyeball right-eye">
<div class="eye pupil right-pupil"></div>
</div>
</section>
<div class="block"></div>
What you want is probably something more like this. This will translate the pupils into place, giving it that "following look".
function eyeball(target_x, target_y) {
const eye = document.querySelectorAll('.eye');
eye.forEach(function(eye){
let x = (eye.getBoundingClientRect().left) + (eye.clientWidth / 2);
let y = (eye.getBoundingClientRect().top) + (eye.clientHeight / 2);
let radian = Math.atan2(target_x - x, target_y - y);
let transform_x = Math.round(Math.sin(radian) * 100);
let transform_y = Math.round(Math.cos(radian) * 100);
eye.style.transform = "translate(" + transform_x + "%, " + transform_y + "%)";
});
}
You will also need to change the initial style of the pupils to get a correct offset for the position change.
.pupil{
margin-left: 25px;
margin-top: 25px;
width: 50px;
height: 45px;
border-radius: 50%;
background: black;
border: 10px solid purple;
box-sizing: border-box;
}

Rotate 2 circles with each other in circular motion by mouse drag

I want to rotate 2 circles around each other in a circular motion by mouse drag like in this code using jQuery
When I drag the white ball it rotates correctly and makes red ball rotate also and that what I need.
My problem is that when I click on red ball it doesn't rotate and doesn't make as the white ball.
I want to make a red ball like a white ball exactly that when drag red ball the red ball and white ball rotate with mouse like in white-ball case.
https://jsfiddle.net/Sarah_Lotfy/h1ye8Ld3/4/
If there is a code that does the same thing please share it with me
var circle = document.getElementById('circle'),
picker = document.getElementById('picker'),
pickerCircle = picker.firstElementChild,
rect = circle.getBoundingClientRect(),
center = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
},
transform = (function(){
var prefs = ['t', 'WebkitT', 'MozT', 'msT', 'OT'],
style = document.documentElement.style,
p;
for (var i = 0, len = prefs.length; i < len; i++){
if ( (p = prefs[i] + 'ransform') in style ) return p
}
alert('your browser doesnt support css transforms!')
})(),
rotate = function(x, y){
var deltaX = x - center.x,
deltaY = y - center.y,
angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI
return angle
},
// DRAGSTART
mousedown = function(event){
event.preventDefault()
document.body.style.cursor = 'move'
mousemove(event)
document.addEventListener('mousemove', mousemove)
document.addEventListener('mouseup', mouseup)
},
// DRAG
mousemove = function(event){
picker.style[transform] = 'rotate(' + rotate(event.x, event.y) + 'deg)'
},
// DRAGEND
mouseup = function(){
document.body.style.cursor = null;
document.removeEventListener('mouseup', mouseup)
document.removeEventListener('mousemove', mousemove)
}
// DRAG START
pickerCircle.addEventListener('mousedown', mousedown)
// ENABLE STARTING THE DRAG IN THE BLACK CIRCLE
circle.addEventListener('mousedown', function(event){
if(event.target == this) mousedown(event)
})
#circle{
position: relative;
width: 300px;
height: 300px;
border-radius: 50%;
background: #000;
}
#circle-in{
position: absolute;
top: 35px;
left: 35px;
width: 230px;
height: 230px;
border-radius: 50%;
background: #fff;
}
#picker{
position: absolute;
top: 50%;
left: 50%;
height: 30px;
margin-top: -15px;
width: 50%;
transform-origin: center left;
}
#picker-circle{
width: 30px;
height: 30px;
border-radius: 50%;
background: #fff;
margin: 0 3px 0 auto;
cursor: move;
}
#picker2{
position: absolute;
top: -200%;
left: -100%;
height: 30px;
margin-top: -10px;
width: 50%;
transform-origin: center right ;
}
#picker-circle2{
width: 30px;
height: 30px;
border-radius: 50%;
background: red;
margin: 0 3px 0 auto;
cursor: move;
}
<div id="circle">
<div id="circle-in"></div>
<div id="picker">
<div id="picker-circle"></div>
</div>
<div id="picker2">
<div id="picker-circle2"></div>
</div>
</div>

Resizing a rotated item with javascript (interact.js)

I have spent many days trying to make an item resizable that is rotated with interact.js.
This is the code that I have at this moment, I will try to explain the concept.
We have a selector item for two reasons, because the container could be scaled with css transform (like a zoom), and we need to have the selector outside and because we have a multiselection, and the selector grow if I have two rectangle selected, but in this case this is not the main problem and we have calculated the scaled proportion without problems and other things.
When the selector is resize, it take the rectangle, and make the same with the width, height, left, top and rotation.
Javascript:
// TAP - CLICK EVENT (just for positioning the selector)
interact('#rectangle').on('tap', event => {
console.log('Tap Box!');
event.stopPropagation();
const $rectangleCloned = $('#rectangle').clone();
const previousTransform = $rectangleCloned.css('transform');
$rectangleCloned.css('transform', 'none');
$rectangleCloned.css('opacity', '0');
$rectangleCloned.css('display', 'block');
$('#container').append($rectangleCloned);
const values = $rectangleCloned[0].getBoundingClientRect();
// This is just a trick for fast implementation:
$('#selector').css('top', values.y);
$('#selector').css('left', values.x);
$('#selector').css('width', values.width);
$('#selector').css('height', values.height);
$('#selector').css('transform', previousTransform);
$rectangleCloned.remove();
return values;
});
interact('.pointer9').draggable({
max: 1,
onmove: event => {
const angleDeg =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
console.log(this.rotate);
const prevAngle = this.rotate - angleInitial;
const angle = parseInt(angleDeg) + prevAngle;
this.$rectangle.css({
transform: 'rotate(' + angle + 'deg)'
});
this.$selector.css({
transform: 'rotate(' + angle + 'deg)'
});
},
onstart: event => {
const data = event.interactable.getRect(event.target.parentNode);
this.centerRotate = {
posX: data.left + data.width / 2,
posY: data.top + data.height / 2
};
this.angleInitial =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
this.$rectangle = $('#rectangle');
this.$selector = $('#selector');
this.rotate = $rectangle.attr('angle') || 0;
},
onend: event => {
const $box = $('#selector');
const matrix = $box.css('transform');
const values = matrix
.split('(')[1]
.split(')')[0]
.split(',');
var a = values[0];
var b = values[1];
var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
$rectangle.attr('angle', angle);
}
});
interact('#selector')
.resizable({
// resize from all edges and corners
edges: {
left: true,
right: true,
bottom: true,
top: true
},
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: {
width: 100,
height: 50
},
},
inertia: true,
})
.on('resizemove', function(event) {
var target = event.target,
x = parseFloat($(target).offset().left) || 0,
y = parseFloat($(target).offset().top) || 0;
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.left = x + 'px';
target.style.top = y + 'px';
$('#rectangle')[0].style.left = target.style.left;
$('#rectangle')[0].style.top = target.style.top;
$('#rectangle')[0].style.width = target.style.width;
$('#rectangle')[0].style.height = target.style.height;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
});
CSS:
#container {
width: 500px;
height: 400px;
top: 0;
left: 0;
position: absolute;
background-color: #CCC;
}
#rectangle {
top: 50px;
left: 50px;
width: 120px;
height: 60px;
background-color: red;
position: absolute;
}
#selector {
display: inline-block;
position: absolute;
pointer-events: none;
z-index: 9999;
top: -1000px;
/*Not showing at start*/
}
#selector .pointers {
display: inline-block;
position: absolute;
z-index: 2;
width: 10px;
height: 10px;
pointer-events: all;
}
#selector .pointers .point {
width: 10px;
height: 10px;
background-color: #fff;
border: 2px solid rgba(0, 0, 0, 0.9);
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#selector .pointers.pointer1 {
top: -5px;
left: -5px;
}
#selector .pointers.pointer2 {
bottom: -5px;
left: -5px;
}
#selector .pointers.pointer3 {
top: -5px;
right: -5px;
}
#selector .pointers.pointer4 {
bottom: -5px;
right: -5px;
}
#selector .pointers.pointer-north {
top: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-south {
bottom: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-east {
right: -5px;
top: calc(50% - 5px);
}
#selector .pointers.pointer-west {
left: -5px;
top: calc(50% - 5px);
}
#selector .pointer-rotate {
border: 2px solid rgba(0, 0, 0, 0.9);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: rotate;
}
#selector .pointer9 {
bottom: -70px;
left: calc(50% - 11px);
display: inline-block;
width: 20px;
height: 20px;
background-color: #fff;
pointer-events: all;
position: absolute;
}
#selector .rotate-line {
border-left: 1px dashed #5f5f5f;
height: 40px;
position: absolute;
top: -40px;
left: calc(50% - 1px);
width: 1px;
}
HTML:
<div id="container">
<div id="rectangle">
</div>
<div id="selector">
<div class="pointers pointer1">
<div class="point"></div>
</div>
<div class="pointers pointer2">
<div class="point">
</div>
</div>
<div class="pointers pointer3">
<div class="point">
</div>
</div>
<div class="pointers pointer4">
<div class="point">
</div>
</div>
<div class="pointers pointer-north">
<div class="point">
</div>
</div>
<div class="pointers pointer-east">
<div class="point">
</div>
</div>
<div class="pointers pointer-south">
<div class="point">
</div>
</div>
<div class="pointers pointer-west">
<div class="point">
</div>
</div>
<span class="topline lines-resize" />
<span class="rightline lines-resize" />
<span class="botline lines-resize" />
<span class="leftline lines-resize" />
<div class="pointer-rotate pointer9" />
<div class="rotate-line" />
</div>
</div>
Fiddle for testing:
https://jsfiddle.net/ub70028c/46/
I have read about other people trying to make the same without not results...
Thanks!
I checked your code and a similar library for resizable and rotatable and I figure out your problem.
First, checking similar library:
Please see this fiddle that I created by jquery.freetrans.js.
If you inspect on <div class="shape">, you can see
transform: matrix(1, 0, 0, 1, 0, 0);
If you rotate it, transform changed like below:
transform: matrix(0.997373, -0.0724379, 0.0724379, 0.997373, 0, 0);
In similar case, your code uses transform that at first, it doesn't transform and after rotating, it has like below:
transform: rotate(-2.49576deg);
If you can use matrix instead of rotate in transform, your code will work properly. If you can't change it, you can use similar library like jquery.freetrans.jsthat work properly with rotate and resize together.
https://github.com/taye/interact.js/issues/569
https://github.com/taye/interact.js/issues/499
https://github.com/taye/interact.js/issues/394
I am afraid you have chosen a library whose author has clearly stated his intent
There's no built-in way. As I mentioned in #137 I'm not really interested in handling scaled or rotated elements
So the question you should ask yourself is
Do I want to find a workaround to make this library work or choose a different library perhaps?
Update-1: 28-Apr-2018
In case you want to do it in canvas instead of normal elements then I found fabric.js a good option
we are very close to finish the work after five days... we need to optimice all the mathematical calculations... but yes, this is what I was looking for:
Sorry, but we don't have the code ready... I will post all with comments for other people.
Comments: For a mathematician, this task is not very complex because all the angles are rectangular (90ยบ). I will try to make a PR to the Interact.js, even to other libraries to implement this feature by default. Hope this work help to other developers ;)

How can I mirror this animation?

I have a panel on the left side of my screen that flips out when activated. As you move your mouse around, it slightly alters the rotateY transform for a nice interactive effect. I want to mirror this on the right side of the screen, but every adjustment I make just causes the panel to freak out when you move the mouse.
What adjustments need to be made to the second jquery function to mirror the effect? I tried a lot of things including the current code which is replacing x = x + 15 with x = 360 - (x + 15). It's close, but still not right.
$(document).on('mousemove','#viewport1 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = x + 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
$(document).on('mousemove','#viewport2 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = 360 - (x + 15); //this is almost but not quite right...
$(this).css('transform','rotateY(' + x + 'deg)');
});
.viewport {
perspective: 1000px;
position: absolute;
top: 0; bottom:0; width: 30%;
padding: 5px;
}
.menu {
text-align: center;
width: 100%;
border: 1px solid black;
display: inline-block;
height: 100%;
}
#viewport1 {
left: 0;
}
#viewport1 .menu {
perspective-origin: left;
transform-origin: left;
transform: rotateY(15deg);
}
#viewport2 {
text-align: right;
right: 0;
}
#viewport2 .menu {
perspective-origin: right;
transform-origin: right;
transform: rotateY(345deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="viewport1" class="viewport">
<div class="menu">HOVER ME!</div>
</div>
<div id="viewport2" class="viewport">
<div class="menu">HOVER ME!</div>
</div>
You are using event.pageX, which is the position of the mouse pointer, relative to the left edge of the document, to calculate the rotation of the <div>. You need to substract the left offset: $(this).offset().left. After that you change x+15 to x-15 and you get the mirrored effect.
$(document).on('mousemove','#viewport1 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = x + 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
$(document).on('mousemove','#viewport2 .menu',function( event ) {
var x = Math.round((event.pageX - $(this).offset().left) / $(this).width() * 10);
x = x - 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
.viewport {
perspective: 1000px;
position: absolute;
top: 0; bottom:0; width: 30%;
padding: 5px;
}
.menu {
width: 100%;
border: 1px solid black;
display: inline-block;
height: 100%;
}
#viewport1 {
left: 0;
}
#viewport1 .menu {
perspective-origin: left;
transform-origin: left;
transform: rotateY(15deg);
}
#viewport2 {
text-align: right;
right: 0;
}
#viewport2 .menu {
perspective-origin: right;
transform-origin: right;
transform: rotateY(345deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="viewport1" class="viewport">
<div class="menu"></div>
</div>
<div id="viewport2" class="viewport">
<div class="menu"></div>
</div>

Zooming in on hexagon tiles javascript

I created a hexagon grid map and needed to be able to zoom in. The wheel event zooming in was added correctly, but when I zoom in, the top/left side of the map is covered and I can't scroll to the very top/left (bottom/right sides are reachable).
Likewise, if I zoom out, there is a huge gap on the top/left sides, while the bottom/right sides are normal. I'm not sure why this happens but I can't fix it.
HTML:
<div id="map"></div>
CSS:
#map {
position: absolute;
left: 0;
top: 0;
padding: 10px 1px;
width: 3032px;
z-index: 5;
overflow: visible;
transform-origin: 50% 50%;
}
body {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
#map>div {
position: relative;
display: inline-block;
float: left;
width: 60px;
height: 34.64px;
margin: 8px 0;
border-left: solid 1px rgba(35, 35, 35, 0.8);
border-right: solid 1px rgba(35, 35, 35, 0.8);
opacity: 1;
transition: opacity 400ms;
}
#map>div:nth-child(97n+50) {
margin-left: 30px;
}
#map>div:before,
#map>div:after {
content: "";
position: absolute;
z-index: 1;
width: 42.43px;
height: 42.43px;
-webkit-transform: scaleY(0.5774) rotate(-45deg);
-ms-transform: scaleY(0.5774) rotate(-45deg);
transform: scaleY(0.5774) rotate(-45deg);
background-color: inherit;
left: 7.7868px;
}
#map>div:before {
top: -21.2132px;
border-top: solid 1.4142px rgba(35, 35, 35, 0.8);
border-right: solid 1.4142px rgba(35, 35, 35, 0.8);
}
#map>div:after {
bottom: -21.2132px;
border-bottom: solid 1.4142px #333333;
border-left: solid 1.4142px #333333;
}
JavaScript:
for (var j = 0; j < 2500; j++) {
var tile = document.createElement('div');
tile.setAttribute('id', '#' + j);
tile.setAttribute('tabindex', 0);
map.appendChild(tile);
}
var zoom = 1;
document.addEventListener('wheel', function(e) {
//console.log((map.scrollLeft + e.pageX) + ' ' + (map.scrollTop + e.pageY));
map.style.transformOrigin = (map.scrollLeft + e.pageX) + 'px ' + (map.scrollTop + e.pageY) + 'px';
// This is so that the zoom center is at the cursor. (I feel like this is causing the problem)
zoom += e.deltaY / 5;
if (zoom < 0.5) zoom = 0.5;
else if (zoom > 5) zoom = 5;
console.log(map.style.transform = "scale(" + zoom + ")");
}, false);
It seems that some browsers don't provide support for arrow keys navigation in this case. I'm using Firefox and arrow key navigation works.
Can someone help me fix this problem?
I can't really fix it, but I do have a suggestion. When zooming, you set a transform-origin based on the current position on the map. However, when changing the position by scrolling, you do not re-calculate that transform-origin. After having scrolled back to the top-left the transform-origin is not 0 0 that I think it should be.
So it's probably an idea to put the calculations inside a named function and attach event handlers for scroll and key input.
A quick test in FF shows that the map, after zooming, does go to top-left after keypress (obviously the changed code is flawed and the zoom stops working after keypress).
http://jsfiddle.net/11Lagwye/1/
document.addEventListener('wheel', myfunc, false);
document.addEventListener('keydown', myfunc, false);
function myfunc(e) {
// your zoom.transform code
}

Categories