I am using CSS to define a ball animation with certain class. This works fine when adding the class on click, the ball just jumps on one place.
However, when I add the class after moving the ball using touchmove/mousedrag and transform, the ball jumps on its original position, instead its new position I have moved it to.
Is there any way I can make the ball jump on the new position but still keep the jump animation nicely in the CSS?
Thanks if you're reading this.
fiddle here: https://jsfiddle.net/9bvucd3q/
code here:
<html>
<head>
<!--<meta name="viewport"
content="width=device-width,
initial-scale=1.0,
user-scalable=no" />
<title>Drag/Drop/Bounce</title>-->
<style>
#item {
width: 100px;
height: 100px;
background-color: rgb(245, 230, 99);
border: 10px solid rgba(136, 136, 136, .5);
border-radius: 50%;
touch-action: none;
user-select: none;
position: absolute;
}
.bugout{animation: card-out 0.6s cubic-bezier(.8,.2,.1,0.8);}
#keyframes card-out {
0% { z-index: 20; transform: translateY(0%) rotate(0deg); }
5% { z-index: 20; transform: translateY(-5%) rotate(-4deg); }
49% { z-index:20;transform: translateY(-120%) ; }
100% { z-index:5;transform: translateY(-120%) ; }
}
#box1 {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<h1>Drag and Drop</h1>
<div id="item"></div>
<div id="box1">
</div>
<script>
var dragItem = document.querySelector("#item");
var box1 = document.querySelector("#box1");
var container = dragItem;
//Declare Variables
var active = false;
var currentX;
var currentY;
var initialX;
var initialY;
var xOffset = 0;
var yOffset = 0;
//Add Event Listeners for Touchscreens
container.addEventListener("touchstart", dragStart, false);
container.addEventListener("touchend", dragEnd, false);
container.addEventListener("touchmove", drag, false);
//Add Event Listeners for Mice
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);
function dragStart(e) { //when the drag starts
if (e.type === "touchstart") { //if its a touchscreen
initialX = e.touches[0].clientX - xOffset; //set initial x-cordinate to where it was before drag started
initialY = e.touches[0].clientY - yOffset; //set initial y-cordinate to where it was before drag started
} else { //if its not a touchscreen (mouse)
initialX = e.clientX - xOffset; //set initial x-cordinate to where it was before drag started
initialY = e.clientY - yOffset; //set initial y-cordinate to where it was before drag started
}
if (e.target === dragItem) { //if user is dragging circle
active = true; //the drag is active
dragItem.classList.remove('bugout');
}
}
function dragEnd(e) { //when the drag ends
const box1Size = box1.getBoundingClientRect(); //the size of box1
const elementSize = dragItem.getBoundingClientRect(); //the size of the circle
if (elementSize.left >= box1Size.left && elementSize.right <= box1Size.right && elementSize.top >= box1Size.top && elementSize.bottom <= box1Size.bottom) { //if the circle is in box1
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
dragItem.classList.add('bugout');
}
else { //if the circle is in neither box1 nor box2
currentX = 0;
currentY = 0;
initialX = 0;
initialY = 0;
xOffset = 0;
yOffset = 0;
setTranslate(0, 0, dragItem);
}
active = false; //the drag is no longer active
}
function drag(e) { //when the circle is being dragged
if (active) { //if the drag is active
e.preventDefault(); //the user cant do anything else but drag
if (e.type === "touchmove") { //if its a touchscreen
currentX = e.touches[0].clientX - initialX; //set current x-cordinate to where it is now
currentY = e.touches[0].clientY - initialY; //set current y-cordinate to where it is now
} else { //if its not a touchscreen (mouse)
currentX = e.clientX - initialX; //set current x-cordinate to where it is now
currentY = e.clientY - initialY; //set current y-cordinate to where it is now
}
//Im not sure what this does but it dosnt work without it
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, dragItem);
}
}
function setTranslate(xPos, yPos, el) { //Im not sure what this does but it dosnt work without it
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
</script>
</body>
</html>```
So in the end I have decided to use GSAP, a really fast animation lib which also supports dragging via its Draggable plugin. It's just simpler if I have to code the animations in JS as opposed as to having them nicely in CSS.
Related
I'm trying to develop a maths game for kids that does not need the keyboard.
Round divs are draggable into blue boxes (also divs).
Dragging down quickly on any round div over the other round divs causes them to wander! (touch or slow mouse both work fine).
sample reference removed 21.2.2022 all relevant information is in the useful answer thanks to Oleg
My hunch is a 3D transform at the end of the script is to blame but I don't really understand it.
Any help Greatly appreciated
If I understood correctly, then in your example, you'd better use Pointer Events and Pointer capture. Your example with PointerEvents below:
const box1 = document.querySelector("#box1");
const box2 = document.querySelector("#box2");
const box3 = document.querySelector("#box3");
const box4 = document.querySelector("#box4");
const box5 = document.querySelector("#box5");
const item1 = document.querySelector("#item1");
const item2 = document.querySelector("#item2");
const item3 = document.querySelector("#item3");
const item4 = document.querySelector("#item4");
const item5 = document.querySelector("#item5");
let active = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
//////////////////////////////////////////
// Moved from dragend
//////////////////////////////////////////
const box1Size = box1.getBoundingClientRect(); //the size of box1
const box2Size = box2.getBoundingClientRect(); //the size of box2
const box3Size = box3.getBoundingClientRect(); //the size of box2
const box4Size = box4.getBoundingClientRect(); //the size of box2
const box5Size = box5.getBoundingClientRect(); //the size of box2
//Add Event Listeners for Touchscreens
//container.addEventListener("touchstart", dragStart, false);
//container.addEventListener("touchend", dragEnd, false);
//container.addEventListener("touchmove", drag, false);
item1.addEventListener('pointerdown', dragStart);
item1.addEventListener('pointerup', dragEnd);
item2.addEventListener('pointerdown', dragStart);
item2.addEventListener('pointerup', dragEnd);
item3.addEventListener('pointerdown', dragStart);
item3.addEventListener('pointerup', dragEnd);
item4.addEventListener('pointerdown', dragStart);
item4.addEventListener('pointerup', dragEnd);
item5.addEventListener('pointerdown', dragStart);
item5.addEventListener('pointerup', dragEnd);
function dragStart(e) { //when the drag starts
this.addEventListener('pointermove', drag);
this.setPointerCapture(e.pointerId);
if (e.type === "touchstart") { //if its a touchscreen
initialX = e.touches[0].clientX - xOffset; //set initial x-cordinate to where it was before drag started
initialY = e.touches[0].clientY - yOffset; //set initial y-cordinate to where it was before drag started
} else { //if its not a touchscreen (mouse)
initialX = e.clientX - xOffset; //set initial x-cordinate to where it was before drag started
initialY = e.clientY - yOffset; //set initial y-cordinate to where it was before drag started
}
}
function dragEnd(e) { //when the drag ends
this.removeEventListener('pointermove', drag);
this.releasePointerCapture(e.pointerId);
const elementSize = e.target.getBoundingClientRect(); //the size of the circle
if (elementSize.left >= box1Size.left && elementSize.right <= box1Size.right && elementSize.top >= box1Size.top && elementSize.bottom <= box1Size.bottom) {
//if the circle is in box1
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
box1.innerHTML = box1.innerHTML + " " + e.target.innerHTML;
} else if (elementSize.left >= box2Size.left && elementSize.right <= box2Size.right && elementSize.top >= box2Size.top && elementSize.bottom <= box2Size.bottom) {
//if the circle is in box2
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
box2.innerHTML = box2.innerHTML + " " + e.target.innerHTML;
} else if (elementSize.left >= box3Size.left && elementSize.right <= box3Size.right && elementSize.top >= box3Size.top && elementSize.bottom <= box3Size.bottom) {
//if the circle is in box3
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
box3.innerHTML = box3.innerHTML + " " + e.target.innerHTML;
} else if (elementSize.left >= box4Size.left && elementSize.right <= box4Size.right && elementSize.top >= box4Size.top && elementSize.bottom <= box4Size.bottom) {
//if the circle is in box4
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
box4.innerHTML = box4.innerHTML + " " + e.target.innerHTML;
} else if (elementSize.left >= box5Size.left && elementSize.right <= box5Size.right && elementSize.top >= box5Size.top && elementSize.bottom <= box5Size.bottom) {
//if the circle is in box5
initialX = currentX; //set the initial x-cordinate to where it is now
initialY = currentY; //set the initial y-cordinate to where it is now
box5.innerHTML = box5.innerHTML + " " + e.target.innerHTML;
} else { //if the circle is in neither box1 nor box2
/*currentX = 0;
currentY = 0;
initialX = 0;
initialY = 0;
xOffset = 0;
yOffset = 0;
setTranslate(0, 0, e.target);*/
}
currentX = 0;
currentY = 0;
initialX = 0;
initialY = 0;
xOffset = 0;
yOffset = 0;
setTranslate(0, 0, e.target);
active = false; //the drag is no longer active
}
function drag(e) { //when the circle is being dragged
if (e.type === "touchmove") { //if its a touchscreen
currentX = e.touches[0].clientX - initialX; //set current x-cordinate to where it is now
currentY = e.touches[0].clientY - initialY; //set current y-cordinate to where it is now
} else { //if its not a touchscreen (mouse)
currentX = e.clientX - initialX; //set current x-cordinate to where it is now
currentY = e.clientY - initialY; //set current y-cordinate to where it is now
}
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, e.target);
}
function setTranslate(xPos, yPos, e) {
e.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
.flex-container {
display: flex;
}
[id^=i] {
width: 80px;
height: 80px;
margin: auto;
background-color: ivory;
border: 1px solid DarkRed;
border-radius: 50%;
touch-action: none;
user-select: none;
/*position: absolute;*/
font-size: 40px;
text-align: center;
}
[id^=b] {
/* this applies to all elements with ids beginning with "b" */
font-size: 40px;
border: 2px solid LightGrey;
width: 100px;
height: 100px;
background-color: blue;
color: ivory;
text-align: center;
}
<h2>Trial</h2>
<div>
<table>
<tr>
<td>
<div id="box1" class="box" ;></div>
</td>
<td>
<div id="item1" draggable="false">A</div>
</td>
</tr>
<tr>
<td>
<div id="box2" class="box"></div>
</td>
<td>
<div id="item2">B</div>
</td>
</tr>
<tr>
<td>
<div id="box3" class="box"></div>
</td>
<td>
<div id="item3">C</div>
</td>
</tr>
<tr>
<td>
<div id="box4" class="box"></div>
</td>
<td>
<div id="item4">D</div>
</td>
</tr>
<tr>
<td>
<div id="box5" class="box"></div>
</td>
<td>
<div id="item5">E</div>
</td>
</tr>
</table>
</div>
I'm trying to trigger an event when the cursor position moves X amount of pixels, say 100px. So, for every 100px the cursor moves in either X or Y direction, I trigger an event. This should continue for every 100px movement.
I've successfully detected the 'current' X and Y position of the cursor, and have set a pixel threshold, but am struggling with the maths on the rest. Can anyone help?
$(window).on('mousemove', function(e){
// Vars
var cursorX = e.clientX;
var cursorY = e.clientY;
var cursorThreshold = 100;
... detect every 100px movement here...
});
You need to keep track of the old cursor positions. Then you can calculate the distance using the Pythagorean theorem:
totalDistance += Math.sqrt(Math.pow(oldCursorY - cursorY, 2) + Math.pow(oldCursorX - cursorX, 2))
This works in any direction.
Example:
Note: Unlike #wayneOS's approach (+1 from me) I do not keep track of the direction.
It's a rather minimalistic implementation.
var totalDistance = 0;
var oldCursorX, oldCursorY;
$(window).on("mousemove", function(e){
var cursorThreshold = 100;
if (oldCursorX) totalDistance += Math.sqrt(Math.pow(oldCursorY - e.clientY, 2) + Math.pow(oldCursorX - e.clientX, 2));
if (totalDistance >= cursorThreshold){
console.log("Mouse moved 100px!");
totalDistance = 0;
}
oldCursorX = e.clientX;
oldCursorY = e.clientY;
});
.d {
width: 0;
height: 0;
border-style: solid;
border-width: 100px 100px 0 0;
border-color: #e54646 transparent transparent transparent;
}
.s { display: flex; }
.p1 { margin-left: 100px; }
.p2 { margin-right: 20px; padding-top: 20px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="p1">100px X</p>
<div class="s">
<p class="p2">100px Y</p>
<div class="d"></div>
</div>
To track mouse-movement for defined steps, you just need to save the last position and than check if the cursor has moved more than the threshold in one direction. Here is an example:
// Vars
var lastCursorX = null;
var lastCursorY = null;
var cursorThreshold = 100;
$(window).on('mousemove', function(e){
//set start-points
if (lastCursorX === null)
lastCursorX = e.clientX;
if (lastCursorY === null)
lastCursorY = e.clientY;
//check for move left
if (e.clientX <= lastCursorX - cursorThreshold) {
lastCursorX = e.clientX;
console.log (cursorThreshold + 'px moved left');
}
//check for move right
if (e.clientX >= lastCursorX + cursorThreshold) {
lastCursorX = e.clientX;
console.log (cursorThreshold + 'px moved right');
}
//check for move up
if (e.clientY <= lastCursorY - cursorThreshold) {
lastCursorY = e.clientY;
console.log (cursorThreshold + 'px moved up');
}
//check for move down
if (e.clientY >= lastCursorY + cursorThreshold) {
lastCursorY = e.clientY;
console.log (cursorThreshold + 'px moved down');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I am trying to develop a simple app.
When we drag the small box inside the bigger box, the smaller box should move inside the bigger box.
However, it can't go outside the bigger box. I know how to move the smaller box, but I don't know how to keep it inside the bigger box. Can somebody help me, please?
As I mentioned, my code moves the small box properly but does not keep it inside the bigger box.
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
lastX = event.pageX;
lastY = event.pageY;
}
}
#container {
height:400px;
width:600px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:green;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:red;
left: 200px;
top: 200px;
}
<div id="container" draggable="true" ></div>
<div id="guy"></div>
You need to restrict guy's position to the container's bounds. In other words, guy's x position can at minimum be the container's x position, at maximum the container's x position plus the container's width minus guy's witdh. The same goes for the y axis, but with height instead of width.
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
var minX = cont.offsetLeft;
var maxX = minX + cont.offsetWidth - guy.offsetWidth;
var minY = cont.offsetTop;
var maxY = minY + cont.offsetHeight - guy.offsetHeight;
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
var targetX = guy.offsetLeft + distX;
var targetY = guy.offsetTop + distY;
guy.style.left = Math.min(maxX, Math.max(minX, targetX)) + "px";
guy.style.top = Math.min(maxY, Math.max(minY, targetY)) + "px";
lastX = event.pageX;
lastY = event.pageY;
}
}
#container {
height:200px;
width:300px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:green;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:red;
left: 100px;
top: 100px;
}
<div id="container" draggable="true" ></div>
<div id="guy"></div>
I now have a working touchscreen image slider but the only problem is that when I touchscreen the movement position begins from the center of the div and moves the whole div to be positioned with the pointer in the middle and lift off screen - and then attempt to do another movement. the movement position begins again in the center of the div.
I know I need to reset the co-ords somehow when I lift off screen, or maybe unbind, but I have no idea how to do this. It takes me days if not weeks to simply figure these things out without help. Don't get me wrong I'm all about learning for myself, but I also learn a great deal more where I can examine a working solution of my own code.
Here is a little HTML for the divs:
.container{
position:absolute;
top:0%;
left:0%;
width:500px;
height:100%;
}
.drag{
position: absolute;
top:1%;
left:0%;
width:100%;
height:15%;
-webkit-transform: translate3d(0,0,0);
border-style: solid; 2px;
border-color: blue;
z-index:1000;
}
</head>
<body>
<div id="container" class="container">
<div id='drag' class='drag'><!---video area--->
some images
</div>
</div>
Here is the code for the swipe events.
var dom = {
container: document.getElementById("container"),
drag: document.getElementById("drag"),
}
var container = {
x: dom.container.getBoundingClientRect().left,
y: dom.container.getBoundingClientRect().top,
w: dom.container.getBoundingClientRect().width,
h: dom.container.getBoundingClientRect().height
}
var drag = {
w: dom.drag.offsetWidth
}
target = null;
document.body.addEventListener('touchstart', handleTouchStart, false);
document.body.addEventListener('touchmove', handleTouchMove, false);
document.body.addEventListener('touchend', handleTouchEnd, false);
document.body.addEventListener('touchcancel', handleTouchCancel, false);
function handleTouchStart(e) {
if (e.touches.length == 1) {
var touch = e.touches[0];
target = touch.target;
}
}
function handleTouchMove(e) {
if (e.touches.length == 1) {
if(target === dom.drag) {
moveDrag(e);
}
}
}
function handleTouchEnd(e) {
if (e.touches.length == 0) { // User just took last finger off screen
target = null;
}
}
function handleTouchCancel(e) {
return;
}
function moveDrag(e) {
var touch = e.touches[0];
var posX = touch.pageX - container.x - drag.w / 2;
dom.drag.style.left = posX + "px";
}
I KNOW IT'S DOWN TO THIS LINE OF CODE:
var posX = touch.pageX - container.x - drag.w / 2;
HERE IS THE FIDDLE:
http://jsfiddle.net/xbmyazb2/20/
I am trying to make a web app with two boxes, one contained in the other. The user should be able to click and move the inner box, however, the user should not be able to move this box outside the confines of the outer box. The user can move the outer box by dragging the inner box against one of the edges of the outer box. I know how to move the inner box, but the problem is how to move the other box with this restriction. Can anybody help me please? Here is what I did so far:
<!doctype html>
<head>
<title>JavaScript Game</title>
<style>
#container {
height:400px;
width:600px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:green;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:red;
left: 200px;
top: 200px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="guy"></div>
<script>
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
lastX = event.pageX;
lastY = event.pageY;
}
}
</script>
</body>
You could add a check to see if moving the box would break bounds of cont.
try to use getBoundingClientRect()
Check the snippet below for the working code.
View in full screen for best results.
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
// ********************************************************************
// get bounding box borders
var contBounds = guy.getBoundingClientRect();
var guyBounds = cont.getBoundingClientRect();
// check bottom bounds
if (contBounds.bottom >= guyBounds.bottom){
cont.style.top = cont.offsetTop + distY + "px";
}
// check top bounds
if (contBounds.top <= guyBounds.top){
cont.style.top = cont.offsetTop + distY + "px";
}
// check left bounds
if (contBounds.left <= guyBounds.left){
cont.style.left = cont.offsetLeft + distX + "px";
}
// check right bounds
if (contBounds.right >= guyBounds.right){
cont.style.left = cont.offsetLeft + distX + "px";
}
// ********************************************************************
lastX = event.pageX;
lastY = event.pageY;
}
}
#container {
height:300px;
width:300px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:#CCC;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:#000;
left: 200px;
top: 200px;
}
<div id="container"></div>
<div id="guy"></div>
try this link to get you started as far as keeping the "guy" inside the "contatiner": http://www.w3schools.com/html/html5_draganddrop.asp
their example shows how you can make an element only drop inside another element.
as far as moving the container...i would think that you could add some if else statements into the moved function that will test the position of the guy against the conatiner's outline and say that when they meet to move the container as well.
i am very new to javascript myself but this is just a suggestion from what i think i understand of it.