How to check if two round divs are overlapping - javascript

This is my codepen. I want to check if the divs are overlapping each other with jQuery. I wrote a code for that but it doesn't work with round boxes. it only works with squares and rectangles. how can I make it work with round divs?
const coordinates = (className) => {
const val = document.querySelector(className);
return {
y: val.offsetTop,
x: val.offsetLeft,
yh: val.offsetTop + val.offsetHeight,
xw: val.offsetLeft + val.offsetWidth,
}
}
const cm = coordinates(".circle.small");
const cl = coordinates(".circle.large");
const offset_x = cm.x < cl.x && cm.xw > cl.x;
const offset_xw = cm.x < cl.xw && cm.xw > cl.xw;
const offset_cx = cm.x < cl.xw && cm.xw < cl.xw;
const offset_cy = cm.y < cl.yh && cm.yh < cl.yh;
const offset_y = cm.y < cl.y && cm.yh > cl.y;
const offset_yh = cm.y < cl.yh && cm.yh > cl.yh;
const is_x = offset_x || offset_xw || offset_cx;
const is_y = offset_y || offset_yh || offset_cy;
console.log(is_x, is_y);
.circle {
width: var(--square);
height: var(--square);
background: var(--bg);
border-radius: 50%;
}
.parent {
margin-left: 5px;
}
.parent2 {
margin-left: 15px;
}
.small {
--square: 50px;
--bg: red;
margin-bottom: -5px;
}
.large {
--square: 100px;
--bg: green;
}
<div class="parent">
<div class="circle small"></div>
</div>
<div class="parent2">
<div class="circle large"></div>
</div>

Your logic to calculate the delta between circle positions isn't quite right. You need to get the X and Y from the centre of each circle, then work out if the hypotenuse calculated from those two points is less than half of the combined radii.
Here's a working example. Note that I only added jQuery/jQueryUI to make dragging the circles around easier for testing - neither of these libraries are required for production use.
let $label = $('.overlap-label span');
const hasOverlap = (x0, y0, r0, x1, y1, r1) => Math.hypot(x0 - x1, y0 - y1) <= r0 + r1;
const coordinates = (className) => {
const el = document.querySelector(className);
const rect = el.getBoundingClientRect();
const radius = el.offsetHeight / 2;
return {
y: rect.top + radius,
x: rect.left + radius,
r: radius
}
}
const checkForOverlap = () => {
const cm = coordinates(".circle.small");
const cl = coordinates(".circle.large");
$label.text(hasOverlap(cm.x, cm.y, cm.r, cl.x, cl.y, cl.r));
}
$('.parent').draggable().on('drag', checkForOverlap);
.circle {
width: var(--square);
height: var(--square);
background: var(--bg);
border-radius: 50%;
display: inline-block;
}
.parent {
display: inline-block;
}
.small {
--square: 50px;
--bg: red;
}
.large {
--square: 100px;
--bg: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/themes/base/jquery-ui.min.css" />
<div class="overlap-label">Circles are overlapping? <span>false</span></div>
<div class="parent">
<div class="circle small"></div>
</div>
<div class="parent">
<div class="circle large"></div>
</div>

Related

How to auto generate divs with javascript?

I made a basic javascript code so you can poke divs with you mouse.
Unfortunately I have to add them manualy but i wanna add so much of them with a pattern.
First i decided to use grid but i guessed it wont work because divs (which i call them squares from now on :D) can change their position.
So I was about to ask, how can I create a javascript code that i can spawn them until they fill the screen.
Also i have another question which is realted to this project, How can i make these squares just decors. I mean by decors i dont want them to effect the webside at all, when the blocks goes out of the screen the body starts to expend, is there any way to avoid that?
(Also it will be better if you make the snippet full screen!)
EDIT: I put the refresh-button on the top left on the main div so you can draw squares by clicking it!
let mouse = {
speedX: 0,
speedY: 0,
posX: 0,
posY: 0,
movement: 0,
speed: 0
}
//on mousemove update the moouse object
document.onmousemove = function(e) {
mouse.speedX = e.movementX;
mouse.speedY = e.movementY
mouse.posX = e.pageX;
mouse.posY = e.pageY;
}
//refresh the mouse movement and speed every 100ms
setInterval(() => {
mouse.movement =
Math.sqrt(Math.pow(mouse.speedX, 2) + Math.pow(mouse.speedY, 2));
mouse.speed = mouse.movement * 10;
}, 100);
//add a square div in parent element
function addSquare(parent) {
const newDiv = document.createElement("div");
newDiv.classList.add("square")
parent.appendChild(newDiv)
return newDiv;
}
//add squares in the parent element filling the available size
//gap is the space between squares, size is the edge of the square
//if skipbefore is false it will begin to draw the next square also if it won't fit entirely
function addSquares(parent, gap, size, skipbefore = true) {
const squares = [];
let rect = parent.getBoundingClientRect();
const availableWidth = rect.width;
const availableHeight = rect.height;
let top = 100;
while (top < availableHeight) {
let left = 0;
if (skipbefore && top + size > availableHeight)
break;
while (left < availableWidth) {
if (skipbefore && left + size > availableWidth)
break;
const square = addSquare(parent);
square.style.left = `${left}px`;
square.style.top = `${top}px`;
squares.push(square);
left += gap + size;
}
top += gap + size;
}
return squares;
}
//onmoveover event handler
const squareOnMouseOver = (event) => {
const element = event.target;
const y = mouse.speedY;
const x = mouse.speedX;
const rad = Math.atan2(y, x);
yAxis = mouse.movement * Math.sin(rad);
xAxis = mouse.movement * Math.cos(rad);
const rect = element.getBoundingClientRect();
const left = Math.round(rect.x + xAxis * 3);
const top = Math.round(rect.y + yAxis * 3);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
const o = rad * (180 / Math.PI);
element.style.transform = `rotate(${o}deg)`;
}
//resets the .target parent and redraw the squares inside it
function drawSquares() {
const parent = document.querySelector('.target');
parent.innerHTML = '';
const squares = addSquares(parent, 25, 75);
const colors = [
'lightcoral',
'bisque',
'aquamarine',
'cadetblue',
'greenyellow',
'yellowgreen'
];
squares.forEach(square => {
const iColor = Math.floor(Math.random() * (colors.length - 1));
const color = colors[iColor];
square.style.background = color;
square.addEventListener('mouseover', squareOnMouseOver);
});
}
body{
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(242, 239, 231);
color: rgb(10, 10, 9);
}
.square{
background-color: lightcoral;
width: 75px;
height: 75px;
position: absolute;
transform: rotate(0deg);
transition: all ease-out 0.5s;
}
.background {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.container .row .col > * {
display: inline-block;
}
.target {
display: block;
width: 100%;
height: 100%;
}
#draw {
font-size: 20px;
padding: .2em 1em;
cursor: pointer;
}
.name{
font-size: 40px;
}
#main-container{
position: absolute;
padding: 35px 45px;
width: 950px;
height: 285px;
box-shadow: 0px 2px 5px 2px rgb(191, 188, 182);
}
.links{
display: flex;
justify-content: center;
align-items: center;
}
.icons{
width: 55px;
height: auto;
margin: 0px 25px;
padding: 10px;
border-radius: 5px;
transition: all ease-in 0.2s;
}
.icons:hover{
background-color: rgb(144, 144, 144);
}
.refresh{
position: absolute;
}
.refresh-button{
width: 25px;
height: auto;
}
.btn:hover{
background-color: rgb(144, 144, 144);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="css.css">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="background">
<div class="target"></div>
<div class="container text-center" id="main-container">
<div class="">
<div class="refresh">
<button class="btn" id="draw" onclick="drawSquares()"><img class="refresh-button" src="SVG/arrow-clockwise.svg"></button>
</div>
<div class="name">Berk Efe Keskin</div>
<br>
<i>This website is working in progress right now...</i>
<br>
<i>Here is some useful links.</i>
<br>
<br>
<div class="links">
<img class="icons" src="SVG/github.svg">
<img class="icons" src="SVG/linkedin.svg">
<img class="icons" src="SVG/stack-overflow.svg">
</div>
</div>
</div>
</div>
<script type="text/javascript" src = "javascript.js"></script>
</body>
</html>
You may have a function that given a container will be filled with how many squares can fit inside as long as there is still avaiable width and available height in the target.
Here in this demo I better factored your code and added a main function called drawSquares that gets called when the button reDRAW is clicked. Each time the squares are redrawn, the target content is emptied.
I'm using a button to trigger the box drawing because the available space depends on the size of the area when the action is fired. For example you can expand the snippet and decide to redraw the squares to have the whole new area filled again.
You may decide to call the action on document ready or when the window gets resized.
let mouse = {
speedX: 0,
speedY: 0,
posX: 0,
posY: 0,
movement: 0,
speed: 0
}
//on mousemove update the moouse object
document.onmousemove = function(e) {
mouse.speedX = e.movementX;
mouse.speedY = e.movementY
mouse.posX = e.pageX;
mouse.posY = e.pageY;
}
//refresh the mouse movement and speed every 100ms
setInterval(() => {
mouse.movement =
Math.sqrt(Math.pow(mouse.speedX, 2) + Math.pow(mouse.speedY, 2));
mouse.speed = mouse.movement * 10;
}, 100);
//add a square div in parent element
function addSquare(parent) {
const newDiv = document.createElement("div");
newDiv.classList.add("square")
parent.appendChild(newDiv)
return newDiv;
}
//add squares in the parent element filling the available size
//gap is the space between squares, size is the edge of the square
//if skipbefore is false it will begin to draw the next square also if it won't fit entirely
function addSquares(parent, gap, size, skipbefore = true) {
const squares = [];
let rect = parent.getBoundingClientRect();
const availableWidth = rect.width;
const availableHeight = rect.height;
let top = 100;
while (top < availableHeight) {
let left = 0;
if (skipbefore && top + size > availableHeight)
break;
while (left < availableWidth) {
if (skipbefore && left + size > availableWidth)
break;
const square = addSquare(parent);
square.style.left = `${left}px`;
square.style.top = `${top}px`;
squares.push(square);
left += gap + size;
}
top += gap + size;
}
return squares;
}
//onmoveover event handler
const squareOnMouseOver = (event) => {
const element = event.target;
const y = mouse.speedY;
const x = mouse.speedX;
const rad = Math.atan2(y, x);
yAxis = mouse.movement * Math.sin(rad);
xAxis = mouse.movement * Math.cos(rad);
const rect = element.getBoundingClientRect();
const left = Math.round(rect.x + xAxis * 3);
const top = Math.round(rect.y + yAxis * 3);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
const o = rad * (180 / Math.PI);
element.style.transform = `rotate(${o}deg)`;
}
//resets the .target parent and redraw the squares inside it
function drawSquares() {
const parent = document.querySelector('.target');
parent.innerHTML = '';
const squares = addSquares(parent, 25, 75);
const colors = [
'lightcoral',
'bisque',
'aquamarine',
'cadetblue',
'greenyellow',
'yellowgreen'
];
squares.forEach(square => {
const iColor = Math.floor(Math.random() * (colors.length - 1));
const color = colors[iColor];
square.style.background = color;
square.addEventListener('mouseover', squareOnMouseOver);
});
}
body {
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
}
.square {
background-color: lightcoral;
width: 75px;
height: 75px;
position: absolute;
transform: rotate(0deg);
transition: all ease-out 0.5s;
}
#Header {
font-size: italic;
}
.background {
width: 100%;
height: 100%;
}
.target {
position: relative;
display: block;
width: 100%;
height: 100%;
z-index: -1;
}
#draw {
font-size: 20px;
padding: .2em 1em;
cursor: pointer;
}
.container .row .col > * {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link el="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<body>
<div class="background">
<span>Work in progress...</span>
<div class="container text-center">
<div class="row">
<div class="col">
<h1 id="Header">Work in progress...</h1>
<button id="draw" onclick="drawSquares()">reDRAW</button>
</div>
</div>
</div>
<div class="target"></div>
</div>
</body>

Why is my cursor and my line of draw are in diffrent sides?

I am attempting to create a drawing app in JS, however, whenever anything is drawn, it is positioned away from my cursor depending on where it is on the canvas, when I am on the furthest left/bottom side of the canvas, you can draw where your cursor is, but the further right/up I move, the more the brush begins to "drift" and go further than where my cursor is.
const canvas = document.getElementById("canvas");
const increaseBtn = document.getElementById("increase");
const decreaseBtn = document.getElementById("decrease");
const sizeEl = document.getElementById("size");
const colorEl = document.getElementById("color");
const clearEl = document.getElementById("clear");
//Core Drawing Functionality (with some research)
const ctx = canvas.getContext("2d");
let size = 5;
let isPressed = false;
let color = "black";
let x;
let y;
let fakeSize = 1;
canvas.addEventListener("mousedown", (e) => {
isPressed = true;
x = e.offsetX;
y = e.offsetY;
});
canvas.addEventListener("mouseup", (e) => {
isPressed = false;
x = undefined;
y = undefined;
});
canvas.addEventListener("mousemove", (e) => {
if (isPressed) {
const x2 = e.offsetX;
const y2 = e.offsetY;
drawCircle(x2, y2);
drawLine(x, y, x2, y2);
x = x2;
y = y2;
}
});
function drawCircle(x, y) {
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = color;
ctx.lineWidth = size * 2;
ctx.stroke();
}
function updateSizeOnScreen() {
sizeEl.innerHTML = fakeSize;
}
increaseBtn.addEventListener("click", () => {
size += 5;
fakeSize++;
if (fakeSize > 10) {
fakeSize = 10;
}
if (size > 50) {
size = 50;
}
updateSizeOnScreen();
});
decreaseBtn.addEventListener("click", () => {
size -= 5;
fakeSize--;
if (fakeSize < 1) {
fakeSize = 1;
}
if (size < 5) {
size = 5;
}
updateSizeOnScreen();
});
colorEl.addEventListener("change", (e) => {
color = e.target.value;
});
clearEl.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
//Eraser and Pencil Actions (my own algorithm)
const eraser = document.getElementById("eraser");
const pencil = document.getElementById("pencil");
eraser.addEventListener("click", () => {
localStorage.setItem("colorEl", JSON.stringify(color));
color = "#fff";
colorEl.disabled = true;
canvas.classList.add("eraseractive");
eraser.classList.add("eraseractive");
colorEl.classList.add("eraseractive");
canvas.classList.remove("pencilactive");
eraser.classList.remove("pencilactive");
colorEl.classList.remove("pencilactive");
});
pencil.addEventListener("click", () => {
JSON.parse(localStorage.getItem("colorEl"));
color = colorEl.value;
colorEl.disabled = false;
canvas.classList.remove("eraseractive");
eraser.classList.remove("eraseractive");
colorEl.classList.remove("eraseractive");
canvas.classList.add("pencilactive");
eraser.classList.add("pencilactive");
colorEl.classList.add("pencilactive");
});
// Dark/Light Mode
const darkMode = document.getElementById("darkMode");
const lightMode = document.getElementById("lightMode");
const toolbox = document.getElementById("toolbox");
darkMode.addEventListener("click", () => {
darkMode.classList.add("mode-active");
lightMode.classList.remove("mode-active");
lightMode.classList.add("rotate");
darkMode.classList.remove("rotate");
toolbox.style.backgroundColor = "#293462";
document.body.style.backgroundImage =
"url('/assets/images/darkModeBackground.svg')";
document.body.style.backgroundSize = "1920px 1080px";
canvas.style.borderColor = "#293462";
toolbox.style.borderColor = "#293462";
});
lightMode.addEventListener("click", () => {
lightMode.classList.add("mode-active");
darkMode.classList.remove("mode-active");
darkMode.classList.add("rotate");
lightMode.classList.remove("rotate");
toolbox.style.backgroundColor = "#293462";
document.body.style.backgroundImage =
"url('/assets/images/lightModeBackground.svg')";
document.body.style.backgroundSize = "1920px 1080px";
canvas.style.borderColor = "#293462";
toolbox.style.borderColor = "#293462";
});
* {
box-sizing: border-box;
font-size: 20px !important;
}
body {
background: url("https://drawing-app-green.vercel.app/assets/images/lightModeBackground.svg");
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
margin: 0;
position: relative;
max-height: 100vh;
overflow: hidden;
}
::selection {
background: transparent;
}
::-moz-selection {
background: transparent;
}
.mode {
display: flex;
position: absolute;
top: 10px;
right: 25px;
cursor: pointer;
}
.light-mode {
color: yellow;
}
.dark-mode {
color: #16213e;
}
.container {
display: flex;
flex-direction: column;
max-width: 1200px;
width: 100%;
max-height: 600px;
height: 100%;
}
canvas {
display: flex;
border: 2px solid #293462;
cursor: url("https://drawing-app-green.vercel.app/assets/images/pencilCursor.png") 2 48, pointer;
background-color: #fff;
margin-top: 3rem;
width: 100%;
height: 600px;
}
.toolbox {
background-color: #293462;
border: 1px solid #293462;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
padding: 0.2rem;
}
.toolbox > * {
background-color: #fff;
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 2rem;
height: 30px;
width: 30px;
margin: 0.25rem;
padding: 0.25rem;
cursor: pointer;
}
.toolbox > *:last-child {
margin-left: auto;
}
canvas.eraseractive {
cursor: url("https://drawing-app-green.vercel.app/assets/images/eraserCursor.png") 2 48, pointer;
}
#color.eraseractive {
cursor: not-allowed;
}
canvas.pencilactive {
cursor: url("https://drawing-app-green.vercel.app/assets/images/pencilCursor.png") 2 48, pointer;
}
.mode-active {
visibility: hidden;
}
.rotate {
transform: rotate(360deg);
transition: transform 1s linear;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drawing App</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</head>
<body>
<i class="fa-solid fa-moon dark-mode fa-2x mode" id="darkMode"></i>
<i
class="fa-solid fa-sun light-mode fa-2x mode mode-active"
id="lightMode"
></i>
<div class="container">
<canvas id="canvas" width="1024" height="600"></canvas>
<div class="toolbox" id="toolbox">
<button id="decrease">-</button>
<span id="size">1</span>
<button id="increase">+</button>
<input type="color" id="color" />
<button id="pencil">
<img src="assets/images/pencilCursor.png" alt="" />
</button>
<button id="eraser">
<img src="assets/images/eraserCursor.png" alt="" />
</button>
<button id="clear">X</button>
</div>
</div>
<script src="assets/js/script.js"></script>
</body>
</html>
Your problem is that your canvas dimentions don't match with the dimentions of the HTML element that contains it. You see: your canvas has a fixed width="" and height="" attributes set. But in your HTML your canvas element has a width of 100%. So that means that the container vairies in dimentions but the canvas inside it not. This result in the canvas trying to resize to show inside the container thus giving you issues with calculating exacly what pixel you are clicking.
You have two options:
Option 1: calculate your click position taking into account canvas deformation
If you want your canvas to resize, then calculate the real position using a simple ratio formula. If for example your canvas has a width of 100 but right now its container is 10px wide, then if you click on pixel 5 you expect a dot to be drawn at pixel 50. In other words if your canvas is smaller by a factor of 10 then you need to multiply your position by a factor of 10.
In your code it would look something like this:
// this is your same code in lines 33 ana34 but see that I added a multiplication by the ratio between the canvas size and the canvas container
const x2 = e.offsetX * (canvas.width / ctx.canvas.getBoundingClientRect().width);
const y2 = e.offsetY * (canvas.height / ctx.canvas.getBoundingClientRect().height);
Option #2: Dont allow your canvas to deform
Remove the container class, and remove the width:100% from your canvas css. Your canvas will overflow and cause a scrollbar but the positions will be calculated properly with your code.

Check if square and rounded div are overlapping

In my code, I used Jquery UI to make the elements draggable for easy debugging. The code I wrote with the help of someone tells me if the rounded divs are overlapping each other but I want to check if a rounded div is overlapping a square div. How can I do that?
BTW If you remove the round class the div will become a square.
let $label = $('.overlap-label span');
const hasOverlap = (x0, y0, r0, x1, y1, r1) => {
return Math.hypot(x0 - x1, y0 - y1) <= r0 + r1;
}
const coordinates = (className) => {
const val = document.querySelector(className);
const rect = val.getBoundingClientRect();
return {
y: rect.top + val.offsetHeight / 2,
x: rect.left + val.offsetHeight / 2,
rad: val.offsetHeight / 2
}
}
const checkForOverlap = () => {
const cm = coordinates(".circle.small");
const cl = coordinates(".circle.large");
$label.text(hasOverlap(cm.x, cm.y, cm.rad, cl.x, cl.y, document.querySelector(".circle.large").offsetHeight / 2));
}
$('.parent').draggable().on('drag', checkForOverlap);
.circle {
width: var(--square);
height: var(--square);
background: var(--bg);
display: inline-block;
}
.round {
border-radius: 50%;
}
.parent {
display: inline-block;
}
.small {
--square: 50px;
--bg: red;
}
.large {
--square: 100px;
--bg: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/themes/base/jquery-ui.min.css" />
<div class="overlap-label">Circles are overlapping? <span>false</span></div>
<div class="parent">
<div class="circle small round"></div>
</div>
<div class="parent">
<div class="circle large round"></div>
</div>

carousel with clickable card not working properly

I have this carousel with card divs wrapped inside an href so users can click the card. Right now I can only slide the card but it's not clickable.
If I remove pointer-events: none; from .inner-slider then the card is clickable, but I cannot slide the carousel smoothly and it's all jumpy when I tried to slide it.
How can I fix this?
let sliderContainer = document.querySelector(".slider-container");
let innerSlider = document.querySelector(".inner-slider");
let banner = document.querySelector(".banner");
let pressed = false;
let startX;
let x;
let bannerOpacity = banner.style.opacity
let oldx = 0
sliderContainer.addEventListener("mousedown", (e) => {
pressed = true;
startX = e.offsetX - innerSlider.offsetLeft;
sliderContainer.style.cursor = "grabbing";
});
// sliderContainer.addEventListener("mouseenter", () => {
// sliderContainer.style.cursor = "grab";
// });
sliderContainer.addEventListener("mouseleave", () => {
sliderContainer.style.cursor = "default";
});
sliderContainer.addEventListener("mouseup", () => {
sliderContainer.style.cursor = "grab";
pressed = false;
});
window.addEventListener("mouseup", () => {
// pressed = false;
});
sliderContainer.addEventListener("mousemove", (e) => {
if (!pressed) return;
console.log(`e.pageX:${e.pageX};oldx:${oldx};op:${banner.style.opacity}`);
console.log(banner.style.opacity + 1);
if (e.pageX > oldx && banner.style.opacity < 1) {
banner.style.opacity = parseFloat(banner.style.opacity) + 0.01;
console.log('right');
} else if (e.pageX < oldx && banner.style.opacity > 0) {
console.log('left');
banner.style.opacity -= 0.01
}
e.preventDefault();
x = e.offsetX;
innerSlider.style.left = `${x - startX}px`;
oldx = e.pageX;
checkBoundary();
});
const checkBoundary = () => {
let outer = sliderContainer.getBoundingClientRect();
let inner = innerSlider.getBoundingClientRect();
if (parseInt(innerSlider.style.left) > 150) {
innerSlider.style.left -= 10;
// bannerOpacity -= 0.01
}
if (inner.right < outer.right) {
innerSlider.style.left = `-${inner.width - outer.width}px`;
}
};
.card {
height: 300px;
width: 400px;
border-radius: 5px;
}
.banner {
z-index: 2;
height: 300px;
width: 200px;
border-radius: 5px;
background-color: red;
}
.card:nth-child(odd) {
background-color: blue;
}
/* .card:first-child {
visibility: hidden;
} */
.card:nth-child(even) {
background-color: rgb(0, 183, 255);
}
.slider-container {
width: 80%;
height: 350px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
}
.inner-slider {
width: 150%;
display: flex;
gap: 10px;
pointer-events: none;
position: absolute;
top: 0;
left: 250px;
}
<div class="slider-container">
<div class="banner" style="opacity: 1;"></div>
<div class="inner-slider">
<!-- <div class="card"></div> -->
<a href="google.com">
<div class="card"></div>
</a>
<a href="https://www.google.com/">
<div class="card"></div>
</a>
<a href="https://www.google.com/">
<div class="card"></div>
</a>
<a href="https://www.google.com/">
<div class="card"></div>
</a>
<a href="https://www.google.com/">
<div class="card"></div>
</a>
</div>
</div>
CodePen Link: https://codepen.io/mjkno1/pen/zYWPjEw
Here is a possible solution.
Changes made:
make cards as <div> blocks instead of <a> and save the block link in the data attribute
whenever a user clicks on the card, check if it's a "mousemove" event or "click" - if mouse moved more then clickDelta value assume it's a move, otherwise - click. If click - make redirect programmatically to the url stored in the data attribute
some changes made to the way how we calculate new position for the slider innerSlider.style.left. Memorise the initial position of the slider and add "move delta".
Hope this makes sense
let sliderContainer = document.querySelector(".slider-container");
let innerSlider = document.querySelector(".inner-slider");
let banner = document.querySelector(".banner");
const clickDelta = 5;
let pressed = false;
let startX;
let blockPos;
let clickStartX;
let clickStartY;
let clickedTarget;
let x;
let bannerOpacity = banner.style.opacity;
let oldx = 0;
sliderContainer.addEventListener("mousedown", (e) => {
pressed = true;
startX = e.pageX;
blockPos = parseInt(innerSlider.style.left, 10) || 0;
sliderContainer.style.cursor = "grabbing";
});
sliderContainer.addEventListener("mouseleave", () => {
sliderContainer.style.cursor = "default";
});
sliderContainer.addEventListener("mouseup", () => {
sliderContainer.style.cursor = "grab";
pressed = false;
});
innerSlider.addEventListener("mousedown", (e) => {
clickedTarget = e.target;
clickStartX = event.pageX;
clickStartY = event.pageY;
});
innerSlider.addEventListener("mouseup", (e) => {
const deltaX = Math.abs(event.pageX - clickStartX);
const deltaY = Math.abs(event.pageY - clickStartY);
if (deltaX < clickDelta && deltaY < clickDelta && clickedTarget) {
const href = clickedTarget.dataset.href;
if (href) {
window.location.href = href;
}
}
});
window.addEventListener("mouseup", () => {
pressed = false;
});
sliderContainer.addEventListener("mousemove", (e) => {
e.preventDefault();
if (pressed) {
const newLeft = blockPos + (e.pageX - startX);
innerSlider.style.left = `${newLeft}px`;
if (e.pageX > oldx && banner.style.opacity < 1) {
banner.style.opacity = parseFloat(banner.style.opacity) + 0.01;
} else if (e.pageX < oldx && banner.style.opacity > 0) {
banner.style.opacity -= 0.01;
}
oldx = e.pageX;
checkBoundary();
}
});
const checkBoundary = () => {
let outer = sliderContainer.getBoundingClientRect();
let inner = innerSlider.getBoundingClientRect();
if (parseInt(innerSlider.style.left) > 150) {
innerSlider.style.left -= 10;
}
if (inner.right < outer.right) {
innerSlider.style.left = `-${inner.width - outer.width}px`;
}
};
.card {
height: 300px;
width: 400px;
border-radius: 5px;
}
.banner {
z-index: 2;
height: 300px;
width: 200px;
border-radius: 5px;
background-color: red;
}
.card:nth-child(odd) {
background-color: blue;
}
.card:nth-child(even) {
background-color: rgb(0, 183, 255);
}
.slider-container {
width: 80%;
height: 350px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
}
.inner-slider {
width: 150%;
display: flex;
gap: 10px;
position: absolute;
top: 0;
}
<div class="slider-container">
<div class="banner" style="opacity: 1"></div>
<div class="inner-slider" style="left: 250px">
<div class="card" data-href="https://www.google1.com/"></div>
<div class="card" data-href="https://www.google2.com/"></div>
<div class="card" data-href="https://www.google3.com/"></div>
<div class="card" data-href="https://www.google4.com/"></div>
<div class="card" data-href="https://www.google5.com/"></div>
</div>
</div>

Drag'n'drop: Element not staying at its original position when I click on it

I am writing my own Drag'n'drop functionality for one of my projects and I am running into an issue. All my "draggable" elements are inside a container with display:flex. On mousedown event on one of this elements I set the position to absolute so I would be able to set the left and top properties of the element when I am dragging. Here is what I am doing:
let container = document.querySelector("#big-container")
var dragging = false;
var draggedObject;
let shiftX=0;
let shiftY=0;
document.querySelectorAll(".draggable").forEach((draggable,index) => {
draggable.style.order = index;
draggable.draggable =false;
draggable.ondragstart = ()=>{return false}
draggable.addEventListener("mousedown",ev =>{
draggedObject = draggable;
shiftX = ev.offsetX+5;
shiftY = ev.offsetY+5;
draggable.style.position = "absolute";
draggable.style.left = (ev.clientX - shiftX) + 'px';
draggable.style.top = (ev.clientY - shiftY) + 'px';
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
})
})
document.addEventListener("mousemove", ev =>{
if(dragging){
draggedObject.style.left = ev.clientX - shiftX + 'px';
draggedObject.style.top = ev.clientY - shiftY + 'px';
}
})
document.addEventListener("mouseup",ev =>{
if(dragging){
draggedObject.style.position = 'static'
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false
}
})
/* the :not(:last-of-type(div)) is there so the console doesn't get affected */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div id = "big-container" style ="display: flex; background-color: rgb(76, 104, 95); width: 500px; height: 500px;">
<div style="background-color: rgb(204, 125, 111);" class="draggable"></div>
<div style="background-color: rgb(170, 214, 120);" class="draggable"></div>
<div style="background-color: rgb(129, 212, 167);" class="draggable"></div>
<div style="background-color: rgb(162, 137, 196);" class="draggable"></div>
</div>
</body>
What I am trying to achieve is that on mousedown the element should stay where it was and after that when I move my mouse to move the element as well.(the anchor point should be where I clicked the element)
I am doing shiftX = ev.offsetX+5; because I need to account for the element's margin.
The issue is when I click on a element(and don't move my mouse at all), you can see a little shift in the element's position. It is very minor(maybe 1 or 2px) and is not happening in all places(some zones in the element do not introduce this position shift)
Do you guys have any idea what might be causing it?
You can use getBoundingClientRect() to get the actual position.
let container = document.querySelector("#big-container");
var dragging = false;
var draggedObject;
let shiftX = 0;
let shiftY = 0;
document.querySelectorAll(".draggable").forEach((draggable, index) => {
draggable.style.order = index;
draggable.draggable = false;
draggable.ondragstart = () => {
return false;
};
draggable.addEventListener("mousedown", (ev) => {
draggedObject = draggable;
var x = draggable.getBoundingClientRect().top - 5;
var y = draggable.getBoundingClientRect().left - 5;
shiftX = ev.offsetX + 5;
shiftY = ev.offsetY + 5;
draggable.style.position = "absolute";
draggable.style.left = y + "px";
draggable.style.top = x + "px";
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
});
});
document.addEventListener("mousemove", (ev) => {
if (dragging) {
draggedObject.style.left = ev.clientX - shiftX + "px";
draggedObject.style.top = ev.clientY - shiftY + "px";
}
});
document.addEventListener("mouseup", (ev) => {
if (dragging) {
draggedObject.style.position = "static";
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false;
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
#big-container {
width: 500px;
height: 500px;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div
id="big-container"
style="display: flex; background-color: rgb(76, 104, 95);"
>
<div
style="background-color: rgb(204, 125, 111);"
class="draggable"
></div>
<div
style="background-color: rgb(170, 214, 120);"
class="draggable"
></div>
<div
style="background-color: rgb(129, 212, 167);"
class="draggable"
></div>
<div
style="background-color: rgb(162, 137, 196);"
class="draggable"
></div>
</div>
</body>

Categories

Resources