How can I repair moving square - javascript - javascript

I want to ask you for help. I'm creating a moving square that have to work in this way:
I'm grabbing it
It follows my cursor
It stays in place where I moused up
But I have a problem with the second point because the square is lagging when I'm using offsetX / Y. I discovered that the reason is the cursor on the square, because then the offset stops counting the axes X/Y. What can I do with it? When I move square to left or top, then it's ok.
HTML CODE:
<div class="container">
<div class="square">
</div>
</div>
CSS:
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
background: bisque;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80vw;
height: 80vh;
border: 1px solid rgb(255, 122, 122);
position: relative;
overflow: hidden;
}
.square {
width: 100px;
height: 100px;
background: blueviolet;
cursor: pointer;
position: relative;
}
JAVASCRIPT:
const square = document.querySelector('.square')
const container = document.querySelector('.container')
square.addEventListener('mousedown', createClass);
square.addEventListener('mouseup', removeClass);
container.addEventListener('mousemove', movingSquare);
// ADDING CLASS 'ACTIVE'
function createClass(e) {
e.target.className = "square active";
}
// MOVING SQUARE
function movingSquare (e) {
if (square.classList.contains('active')) {
square.style.transform = `translate(${e.offsetX}px, ${e.offsetY}px)`;
// square.style.left = e.offsetX + 'px';
// square.style.top = e.offsetY + 'px';
}
}
// REMOVING CLASS 'ACTIVE'
function removeClass(e) {
e.target.className = "square"
}

I can see a possible advantage of of using .offset values but I prefer to use clientX and clientY values. It does require adjustment to allow for the positions of the parent element but is not difficult.
In my snippet, instead of adding and removing classes, I access the .left and .top style properties of the element. A class name could be added if you want to change other aspects.
Like in your code I use event listeners for mousedown, mousemove, and mouseup.
In the mousedown event I set a boolean flag true if the target element is the moveable box (this equates to your class addition). I also set .client cursor positions for the start, and update the initial left and top values (relative to its parent container by subtraction) for the moveable element.
This allows the mousemove listener to update (provided the target flag was set true) the style left and top properties by an amount equal to the movement of the cursor coordinates since the mousedown was made.
const box = document.getElementsByClassName('square')[0];
let downInBox=false;
let initialLeft = 0;
let initialTop = 0;
let deltaX = 0;
let deltaY = 0;
let startX = 0;
let startY = 0;
document.addEventListener('mousedown', event => {
if (event.target == box ) {
downInBox=true;
initialLeft = parseInt(box.getBoundingClientRect().left - box.parentElement.getBoundingClientRect().left);
initialTop = parseInt(box.getBoundingClientRect().top - box.parentElement.getBoundingClientRect().top);
startX = event.clientX;
startY = event.clientY;
} // end if
}); // end mousedown;
document.addEventListener('mouseup', event => {
downInBox=false;
}); // end mouseup;
document.addEventListener('mousemove', event => {
if (downInBox) {
deltaX = startX-event.clientX;
deltaY = startY-event.clientY;
box.style.left = `${initialLeft-deltaX}px`;
box.style.top = `${initialTop-deltaY}px`;
} // end if;
}); // end mousemove;
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
background: bisque;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80vw;
height: 80vh;
border: 1px solid rgb(255, 122, 122);
position: relative;
overflow: hidden;
}
.square {
width: 100px;
height: 100px;
background: blueviolet;
cursor: pointer;
position: relative;
left: 0px;
top: 0px;
}
<div class="container">
<div class="square">
</div>
</div>
The mouseup listener simply resets the flag false (equivalent to you removing the class).

Related

Is it possible to see through a div to the one below around cursor?

Hi this is a bit of an odd question, I've seen similar effects to whats I'm going for but not exactly the same not sure if what I want to do is possible.
I want to have two divs stacked with the contents of the div below revealed only in a specific area (around the cursor), is there a way to make only part of a div transparent? Or is there any other way to achieve this effect?
Instead of having the element you want to show in the background you can put it in front and only show part of it via a clip-path;
For the coordinates I use CSS variables though you could also overwrite the style directly.
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const { offsetX, offsetY } = event;
// Update coordinates
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
}
.container {
border: 1px solid #000;
width: 300px;
height: 300px;
}
/* Show child when hovering the container */
.container:hover .child {
display: block;
}
.child {
clip-path: ellipse(30px 30px at var(--x) var(--y));
display: none;
}
<div class="container">
<img class="child" src="//picsum.photos/300" width="300" height="300" />
</div>
You can use requestAnimationFrame to make the circle move more smoothly
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const { offsetX, offsetY } = event;
// Update coordinates
requestAnimationFrame(() => {
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
});
}
.container {
border: 1px solid #000;
width: 300px;
height: 300px;
}
/* Show child when hovering the container */
.container:hover .child {
display: block;
}
.child {
clip-path: ellipse(30px 30px at var(--x) var(--y));
display: none;
}
<div class="container">
<img class="child" src="//picsum.photos/300" width="300" height="300" />
</div>
Example with text
// Get element from the DOM
const container = document.querySelector('.container');
// Apply event listener
container.addEventListener('mousemove', updateCoords, false);
function updateCoords(event) {
// Get X and Y coordinates
const {offsetX, offsetY} = event;
// Update coordinates
requestAnimationFrame(() => {
container.style.setProperty('--x', offsetX + 'px');
container.style.setProperty('--y', offsetY + 'px');
});
}
.container {
min-height: 100vh;
min-width: 100vh;
overflow: hidden;
}
.container:hover .code {
display: flex;
}
.display,
.code {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background-color: rgb(49, 49, 49);
color: rgb(240, 191, 29);
pointer-events: none;
}
.code {
clip-path: ellipse(100px 100px at var(--x) var(--y));
display: none;
background-color: rgb(3, 3, 3);
color: rgb(101, 253, 101);
}
<div class="container">
<div class="display">
<h1>Header</h1>
</div>
<div class="code">
<h3><h1>Header</h1></h3>
</div>
</div>

Send mouseover event to all overlaid elements [duplicate]

Is it possible to make two overlapping divs, both clickable?
I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?
window.addEventListener('load', init);
function init() {
calculateGrid();
//calculate grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
$(".grid").click(function() {
$(this).toggleClass('selected');
});
};
#container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: column;
overflow: hidden;
}
#container .grid {
border: 1px solid blue;
}
#container2 {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: row;
overflow: hidden;
}
#container2 .grid {
border: 1px solid red;
}
.grid {
font-size: 10px;
color: white;
}
#container .selected {
background-color: blue;
}
#container2 .selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
<div id="container"></div>
<div id="container2"></div>
</div>
View on CodePen
One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.
window.addEventListener('load', init);
function init() {
// build grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container1').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
// handle grid clicks
function handleGridClick(e) {
let elms = document.elementsFromPoint(e.clientX, e.clientY);
Array.from(elms).forEach(elm => {
if (elm.classList.contains('grid'))
elm.classList.add('selected');
});
}
// initialize grid and click handler
calculateGrid();
document.addEventListener('click', handleGridClick);
};
.container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
overflow: hidden;
}
#container1 {
flex-direction: column;
}
#container1 .grid {
border: 1px solid blue;
}
#container1 .grid.selected {
background-color: blue;
}
#container2 .grid {
border: 1px solid red;
}
#container2 .grid.selected {
background-color: red;
}
<div id="wrapper">
<div id="container1" class="container"></div>
<div id="container2" class="container"></div>
</div>
You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.
So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.
For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

Problems with moving a div by cursor if I move it too fast

to specify my question I wrote an standalone example of my problem. I want to precisely move a div inside a wrapping container (only in x-direction), like a trackbar. The wrapping div should specify the space for the slider.
My script works, if I slowly move the cursor. But if I move the cursor too fast I kind of loose the slider div somewhere inside the container. Especially in the right and left corner.
How can I improve the code to have a stable solution, without the need of librarys? I know that there is a kind of simple solution with jQuery, but I would be very happy if we could find a way in plain javascript.
var x_mouse_position;
var x_offset;
var isDown = false;
var new_slider_left_position;
var container = document.getElementById("container");
var slider = document.getElementById("slider");
slider.addEventListener('mousedown', function (e) {
isDown = true;
x_offset = slider.offsetLeft - e.clientX;
}, true);
document.addEventListener('mouseup', function () {
isDown = false;
}, true);
document.addEventListener('mousemove', function (event) {
if (isDown) {
x_mouse_position = event.clientX;
new_slider_left_position = x_mouse_position + x_offset;
if (new_slider_left_position >= 0 && new_slider_left_position <= container.offsetWidth - slider.offsetWidth) {
slider.style.left = new_slider_left_position + 'px';
}
}
}, true);
html,
body {
height: 100%;
width: 100%;
}
body {
background-color: antiquewhite;
display: flex;
justify-content: center;
align-items: center;
}
#container {
position: relative;
width: 400px;
height: 30px;
background-color: cornflowerblue;
border-radius: 5px;
overflow: hidden;
}
#slider {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
border: 1px solid black;
width: 50px;
height: 100%;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.2);
cursor: move;
}
<div id="container">
<div id="slider"></div>
</div>

How to convert the height of a div to 100% value using JavaScript

I have a div that I want to apply CSS transform scale property with a dynamic value based on the parent div's scroll position.
So say I have a div with an id called #circle that has a parent div called #main. I want to find the #main div's height and divide it by 100 to create a dynamic value. Using this dynamic value, I want to animate the #circle div on scroll event. So when the user is at the top of the #main div, the #circle is scaled at zero per cent, and when the user scrolls to the bottom of the main div, the circle has a scaling value of 100%.
Here is a jsFiddle.
Currently your scrolling container is actually the html element itself. So based on it's scrollTop,scrollHeight and the visible height i.e. window.innerHeight, we can calculate the circleScale value. All that part is handled inside scrollLogic() function.
const circle = document.getElementById('circle');
const html = document.documentElement;
document.body.onscroll = () => {
const circleScale = scrollLogic();
circle.style.transform = `scale(${circleScale})`;
};
const scrollLogic = () => {
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop / maxScrollTop;
const circleScale = Math.min(
1,
scrollFraction+0.1
);
return circleScale
};
* {
box-sizing: border-box;
margin: 0;
padding:0;
}
#main {
display: flex;
align-items: center;
justify-content: center;
margin: 30px 0;
border: 3px solid green;
overflow: scroll;
}
#circle {
width: 200px;
height: 200px;
background-color: red;
transform: scale(0.1);
border-radius: 50%;
}
.spacing {
height: 150vh;
}
<section id="main">
<div id="circle"></div>
<div class="spacing"></div>
</section>
Edit - For main to be a scrolling-container, it needs a certain fixed height so that the spacing of height 150vh can cause the main of height 80vh to overflow. Then the scroll event and calculations will happen on main itself. But also keep in mind that now since main itself is scrolling, the perceived position of the circle will also change and as it's scale increases, some of it will get cut down by the top of main container.
const circle = document.getElementById('circle');
const html = document.documentElement;
const main = document.getElementById('main');
main.onscroll = () => {
const circleScale = scrollLogic();
circle.style.transform = `scale(${circleScale})`;
};
const scrollLogic = () => {
const scrollTop = main.scrollTop;
const maxScrollTop = main.scrollHeight - main.offsetHeight;
const scrollFraction = scrollTop / maxScrollTop;
const circleScale = Math.min(
1,
scrollFraction+0.1
);
return circleScale
};
* {
box-sizing: border-box;
margin: 0;
padding:0;
}
#main {
display: flex;
align-items: center;
justify-content: center;
margin: 30px 0;
border: 3px solid green;
height:80vh;
overflow: scroll;
}
#circle {
width: 200px;
height: 200px;
background-color: red;
transform: scale(0.1);
border-radius: 50%;
}
.spacing {
height: 150vh;
}
<section id="main">
<div id="circle"></div>
<div class="spacing"></div>
</section>
You can use the offsetHeight property to get the height of the element.
ex.
document.getElementById("circle").offsetHeight

Dragging DIV with JavaScript mouse events moves to quickly

Im trying to move the #frame-slider-thumb across the image. I had it working by just keeping track of the diff in mouseX position. But the problem was that if the thumb wasn't at 0 to begin with it would jump back to 0. Thus I added the curr variable in the logic to add the diff from its current position. Now it moves much to quickly though. I'm not sure why. Any help much appreciated.
Heres a codepen.
HTML
<div id="frame-slider">
<img id="frame-slider-background" src="http://imagej.1557.x6.nabble.com/file/n5009735/OCT_pre_segmented.png" alt="" />
<div id="frame-slider-track">
<div id="frame-slider-thumb">
<div class="top-half"></div>
<div class="bottom-half"></div>
</div>
</div>
</div>
JS
var mouseStartPosition = {};
var thumb = document.getElementById('frame-slider-thumb');
window.addEventListener("mousedown", mousedownThumb);
function mousedownThumb(e) {
mouseStartPosition.x = e.pageX;
// add listeners for mousemove, mouseup
window.addEventListener("mousemove", mousemoveThumb);
window.addEventListener("mouseup", mouseupThumb);
}
function mousemoveThumb(e) {
var curr = isNaN(parseFloat(thumb.style.left)) ? 0 : parseFloat(thumb.style.left);
var diff = -1 * (mouseStartPosition.x - e.pageX);
var newLeft = curr + diff;
thumb.style.left = newLeft + 'px';
}
function mouseupThumb(e) {
window.removeEventListener("mousemove", mousemoveThumb);
window.removeEventListener("mouseup", mouseupThumb);
}
CSS
html,
body {
width: 100%;
height: 100%;
}
#frame-slider {
height: 150px;
width: 50%;
position: relative;
}
#frame-slider-background {
width: 100%;
max-height: 100%;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-user-drag: none;
user-drag: none;
-webkit-touch-callout: none;
}
#frame-slider-track {
height: 100%;
width: 100%;
position: absolute;
top: 0;
}
#frame-slider-thumb {
position: absolute;
left: 0;
margin-left: -4px;
width: 8px;
height: 100%;
cursor: pointer;
}
#frame-slider-thumb .top-half {
background-color: rgba(0, 0, 255, 0.7);
height: 50%;
}
#frame-slider-thumb .bottom-half {
background-color: rgba(255, 0, 0, 0.7);
height: 50%;
}
Fixed by adding a thumbStart position to mousedownThumb. Basically diff isn't the difference in position from the last mousemove event, its the difference from the last mousemove event and the mousedown event.
var mouseStartPosition = {};
var thumbStart;
var thumb = document.getElementById('frame-slider-thumb');
window.addEventListener("mousedown", mousedownThumb);
function mousedownThumb(e) {
mouseStartPosition.x = e.pageX;
thumbStart = isNaN(parseFloat(thumb.style.left)) ? 0 : parseFloat(thumb.style.left);
// add listeners for mousemove, mouseup
window.addEventListener("mousemove", mousemoveThumb);
window.addEventListener("mouseup", mouseupThumb);
}
function mousemoveThumb(e) {
var diff = -1 * (mouseStartPosition.x - e.pageX);
var newLeft = thumbStart + diff;
thumb.style.left = newLeft + 'px';
}
function mouseupThumb(e) {
window.removeEventListener("mousemove", mousemoveThumb);
window.removeEventListener("mouseup", mouseupThumb);
}

Categories

Resources