Have elements follow cursor while mousedown is active
I have several picture elements that follow mouse cursor on mousemove event. That script is quite simple and it works great, but I'd like it to run only while the mouse is pressed down so you have a feeling like you are dragging those elements around.
I've placed initial script into function and it's called when mousedown is active and you start dragging, but it's buggy. Right now when you press mouse and drag it won't start follow you until you stop dragging and then it starts and never stops.
Can somebody help me with this?
Here is JSfiddle where you can see it working, but not as intended and the entire code is bellow.
HTML
<div class="container">
<h2 class="object" data-value="3">Space<br><span>Background</span></h2>
<img src="1.png" class="object" data-value="-2" alt="">
<img src="2.png" class="object" data-value="6" alt="">
<img src="3.png" class="object" data-value="4" alt="">
<img src="4.png" class="object" data-value="-6" alt="">
<img src="5.png" class="object" data-value="8" alt="">
<img src="6.png" class="object" data-value="-4" alt="">
<img src="7.png" class="object" data-value="5" alt="">
<img src="8.png" class="object" data-value="-9" alt="">
<img src="9.png" class="object" data-value="-5" alt="">
</div>
CSS
.container{
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.container img{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: contain;
}
.container h2{
z-index: 1;
position: relative;
color: #fff;
font-size: 90px;
text-transform: uppercase;
font-weight: 900;
letter-spacing: 32px;
line-height: 60px;
}
.container h2 span{
font-size: 48px;
font-weight: 500;
letter-spacing: 10px;
}
@media (max-width:800px){
.container h2{
font-size: 60px;
letter-spacing: 19px;
line-height: 35px;
}
.container h2 span{
font-size: 26px;
}
}
Javacript
function movingObjects() {
document.addEventListener("mousemove", parallax);
function parallax(e){
document.querySelectorAll(".object").forEach(function(move){
var moving_value = move.getAttribute("data-value");
var x = (e.clientX * moving_value) / 250;
var y = (e.clientY * moving_value) / 250;
move.style.transform = "translateX(" + x + "px) translateY(" + y + "px)";
});
}
}
$(".container").mousedown(function () {
$(this).mousemove(function () {
movingObjects();
});
}).mouseup(function () {
$(this).unbind('mousemove');
}).mouseout(function () {
$(this).unbind('mousemove');
});
Solution 1:
Your code is almost there, there's just two issues which need addressing.
Firstly, you need to know when the mouse is being held down and moving. The nested events are not the best way to do this and are actually causing issues. Similarly, binding/unbinding events at runtime becomes hard to maintain and leads to other issues.
The simplest way to do what you need to is to use a boolean variable to indicate if the mouse button is being held down. It gets set on mousedown
, mouseup
and other relevant events. You can then use this in the parallax()
function to check if processing should continue.
The second issue is that because the img
elements are rendered on top of everything else, and fill the full .container
element, they intercept the mouse click/drag events. To stop this apply pointer-events: none
to them in CSS.
With those corrections in place, the code works:
function parallax(e) {
document.querySelectorAll(".object").forEach(function(move) {
var moving_value = move.dataset.value;
var x = (e.clientX * moving_value) / 250;
var y = (e.clientY * moving_value) / 250;
move.style.transform = `translateX(${x}px) translateY(${y}px)`;
});
}
let mousedown = false;
$('body').on({
mousedown: () => mousedown = true,
mouseup: () => mousedown = false,
mouseleave: () => mousedown = false,
mousemove: e => mousedown && parallax(e)
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", sans-serif;
background: #666 url(bg.png) no-repeat;
background-size: cover;
height: 100vh;
}
.container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
user-select: none;
}
.container img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: contain;
pointer-events: none;
}
.container h2 {
z-index: 1;
position: relative;
color: #fff;
font-size: 90px;
text-transform: uppercase;
font-weight: 900;
letter-spacing: 32px;
line-height: 60px;
}
.container h2 span {
font-size: 48px;
font-weight: 500;
letter-spacing: 10px;
}
@media (max-width:800px) {
.container h2 {
font-size: 60px;
letter-spacing: 19px;
line-height: 35px;
}
.container h2 span {
font-size: 26px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="container">
<h2 class="object" data-value="3">Space<br><span>Background</span></h2>
<img src="https://i.imgur.com/5yrwyPE.png" class="object" data-value="-2" alt="">
<img src="https://i.imgur.com/C688O6f.png" class="object" data-value="6" alt="">
<img src="https://i.imgur.com/zQMI9zR.png" class="object" data-value="4" alt="">
<img src="https://i.imgur.com/JSzKUzo.png" class="object" data-value="-6" alt="">
<img src="https://i.imgur.com/dxgHvQV.png" class="object" data-value="8" alt="">
<img src="https://i.imgur.com/kbNROja.png" class="object" data-value="-4" alt="">
<img src="https://i.imgur.com/2Ud0NwP.png" class="object" data-value="5" alt="">
<img src="https://i.imgur.com/c5BGOj2.png" class="object" data-value="-9" alt="">
<img src="https://i.imgur.com/Qxs8luV.png" class="object" data-value="-5" alt="">
</div>