Please tell me, if you start touchmove from the middle of the page and move to the right, then console.log() goes right, but if you move to the left and cross the middle of the screen, console.log() will show the opposite direction, although the swipe goes in the same direction. As I understand it, this is due to the fact that touchstart coordinates are fixed from where touchmove started. How to fix this error. (see video).
https://youtu.be/_pB49S2BR2o
var initialX = null;
var initialY = null;
$("#content, .menu").on("touchstart", function(e) {
initialX = e.touches[0].clientX;
initialY = e.touches[0].clientY;
});
$("#content, .menu").on("touchmove", function(e) {
var diffX = initialX - e.touches[0].clientX;
var diffY = initialY - e.touches[0].clientY;
if (Math.abs(diffX) > Math.abs(diffY)) {
if (diffX > 0) {
console.log("Right");
} else {
console.log("Left");
}
} else {
return;
}
var width = parseInt($("body").css("width"));
clientX = e.touches[0].clientX;
width = width - clientX;
if (width >= 0 || width <= 375) {
$(".menu").css("left", "-" + width + "px");
} else {}
})
body {
margin: 0px;
overflow: hidden;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}
.menu {
background: blue;
width: 100%;
height: 100%;
z-index: 1;
position: absolute;
left: -100%;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
#content {
width: 100%;
height: 100vh;
color: #000;
display: flex;
align-items: center;
justify-content: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
<body>
<div class="menu" style="transition-duration: 0ms;">Menu slide</div>
<div id="content">
Content slide
</div>
</body>
const text = document.querySelector('#direction');
const cursorEl = document.querySelector('#cursorDir');
let lastX = null;
let active = false;
$("#content, .menu").on("touchstart", function(evt) {
active = true;
});
$("#content, .menu").on("touchend", function(evt) {
active = false;
});
$("#content, .menu").on("touchmove", function(evt) {
if (!active) return; // Run only on mousedown
if (lastX === null) {
// Set first value
lastX = evt.clientX;
return;
}
const diff = lastX - evt.clientX;
if (diff === 0) return; // Break if there's 0 pixels difference
console.log("direction: " + (diff > 0 ? "Left" : "Right"));
lastX = evt.clientX; // Set next value to check the difference
});
Is this what you are looking for?
Note: For the purpose of running this in a browser, touchstart and touchmove are replaced by mousedown and mousemove
const text = document.querySelector('#direction');
const cursorEl = document.querySelector('#cursorDir');
let lastX = null;
let active = false;
cursorEl.addEventListener('mousedown', () => { active = true; });
document.documentElement.addEventListener('mouseup', () => { active = false; });
cursorEl.addEventListener('mousemove', (evt) => {
if (!active) return; // Run only on mousedown
if (lastX === null) {
// Set first value
lastX = evt.clientX;
return;
}
const diff = lastX - evt.clientX;
if (diff === 0) return; // Break if there's 0 pixels difference
text.innerHTML = "direction: " + (diff > 0 ? "Left" : "Right");
lastX = evt.clientX; // Set next value to check the difference
});
#cursorDir {
display: block;
width: 150px;
height: 150px;
}
#cursorDir {
background: blue;
}
Click and hold to activate
<div id="cursorDir"></div>
<div id="direction"><div>
Related
I've been waiting to ask this question for a long time, but couldn't, because I knew I would get bad reputation. This post was very hard to post, but I REALLY need this code...
Let's say there was a draggable element with the ID of "dragme"... You have to drag and drop the element to a specific spot. I was wondering if there is a code that does this task automatically for me when I execute a function. Lets name that function "dropElement". I am trying to drag "dragme" to my mouse position with a "dragElement" function with jquery or js.
This is what I tried:
(function() {
'use strict';
var mouseX = 0;
var mouseY = 0;
var timer = 0;
//tracks mouse position
document.body.addEventListener("mousemove", function(e) {mouseX = e.clientX; mouseY = e.clientY;});
function dropElement() {
$("#dragme").trigger($.Event("mousedown", {button: 0}));
$("body").trigger($.Event("mouseup", {button: 0, clientX: mouseX, clientY: mouseY}));
timer = setTimeout(drop, 100);
}
dropElement() //executes function and drops "dragme" to mouse position
I found the code in the question a bit complex to follow, especially with a timing function.
Instead I've gone back to basics (and vanilla JS) to think about the sequence of events. The user moves the mouse, we aren't interested unless they have put the mousedown within the element we want to drag. So this snippet sets a variable isDown which is set to true when the user puts the mouse down on the element.
Then it looks for a mousemove event on the whole window and if isDown is set it moves the element.
We also look for the mouseup event on the window and unset isDown.
The reason for looking for some events on the actual element and some on the window is because things are moving - the mouse may get out of the window before it is released for example.
let isDown = false;
const dragMe = document.querySelector('.dragme');
dragMe.addEventListener('mousedown', function() {
isDown = true;
});
window.addEventListener('mouseup', function() {
isDown = false;
});
window.addEventListener('mousemove', function() {
if (isDown) {
dragMe.style.top = event.clientY + 'px';
dragMe.style.left = event.clientX + 'px';
}
});
.dragme {
width: 5em;
height: 5em;
background-color: cyan;
position: relative;
top: 0;
left: 0;
}
<div class="dragme">Drag me</div>
I hope this sample helps you
var drag = {
elem: null,
x: 0,
y: 0,
state: false
};
var delta = {
x: 0,
y: 0
};
function dropElement(e){
var cur_offset = $("#autoDrag").offset();
$("#autoDrag").animate({
left: (e.pageX),
top: (e.pageY )
});
}
$(document).mousedown(function(e) {
dropElement(e);
})
$("#dragMe").mousedown(function(e) {
drag.elem = dragMe;
drag.x = e.pageX;
drag.y = e.pageY;
drag.state = true;
})
$(document).mousemove(function(e) {
if ( drag.state) {
delta.x = e.pageX - drag.x;
delta.y = e.pageY - drag.y;
var cur_offset = $(drag.elem).offset();
$(drag.elem).offset({
left: (cur_offset.left + delta.x),
top: (cur_offset.top + delta.y)
});
drag.x = e.pageX;
drag.y = e.pageY;
}
})
$("#dragMe").mouseup(function() {
drag.state = false;
})
#dragMe {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: 80px;
height: 80px;
padding:10px;
background-color: #00a1ff;
color: white;
border-radius: 50px;
}
#autoDrag {
position: absolute;
right:0;
display: flex;
justify-content: center;
align-items: center;
text-align:center;
width: 80px;
height: 80px;
padding:10px;
background-color: #ff00ff;
color: white;
border-radius: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span id="dragMe">DragMe!</span>
<span id="autoDrag">Click somewhere I will be there!</span>
Hello guys i have following problem:
Whenever i drag an element it flickers and it looks very annoying. I couldnt find the root of the problem.
Here is my code snippet:
moveElement(element, e) {
let clientX = e.clientX;
let clientY = e.clientY;
let offsetX = e.offsetX;
let offsetY = e.offsetY;
let height = element.getBoundingClientRect().height;
let width = element.getBoundingClientRect().width;
window.requestAnimationFrame(function() {
element.style.setProperty("left", clientX - (width - offsetX) + "px");
element.style.setProperty("top", clientY - (height - offsetY) + "px");
});
}
Here is he full code:
class Dragger{
constructor() {
this.drags = [];
this.drops = [];
this.mover = null;
this.collectDragAndDrop();
}
dragItem(element) {
element.style.setProperty("position", "fixed");
this.mover = this.moveElement.bind(null, element);
element.addEventListener("mousemove", this.mover);
}
moveElement(element, e) {
let clientX = e.clientX;
let clientY = e.clientY;
let offsetX = e.offsetX;
let offsetY = e.offsetY;
let height = element.getBoundingClientRect().height;
let width = element.getBoundingClientRect().width;
window.requestAnimationFrame(function() {
element.style.setProperty("left", clientX - (width - offsetX) + "px");
element.style.setProperty("top", clientY - (height - offsetY) + "px");
});
}
dropItem(element) {
element.removeEventListener("mousemove", this.mover);
}
collectDragAndDrop() {
document.querySelectorAll("[drag]").forEach(element => {
let name = element.attributes.drag.nodeValue;
let findDup = this.drags.some(el => el.name === name);
if (findDup) throw Error("Duplicated drag attribute: " + name);
this.drags.push({
element,
name
});
element.addEventListener("mousedown", this.dragItem.bind(this, element));
element.addEventListener("mouseup", this.dropItem.bind(this, element));
});
}
}
new Dragger();
.box1 {
background: black;
width: 100px;
height: 100px;
color: white;
}
.box2 {
background: red;
width: 100px;
height: 100px;
position: fixed;
right: 100px;
}
<div class="box1" drag="test"></div>
<div class="box2" drag="test2"></div>
Can somebody tell me why this flickers so much?
Your math is wonky. You're only accounting for the current mouse position instead of calculating the amount of movement that has occurred. The only reason your boxes are moving at all is because the function is waiting for the animation frame so there is some change in those coordinates while it waits.
You should also consider that if the mouseup occurs while the mouse is no longer over the element, the element won't get the event and thus will continue dragging when you mouse back over it. It's better to set a flag that keeps track of the mouse state.
var isMouseDown = false;
addEventListener("mousedown", ()=>isMouseDown = true);
addEventListener("mouseup", ()=>isMouseDown = false);
document.querySelectorAll("[drag]").forEach(element=>{
element.addEventListener("mousemove", e=>{
if(!isMouseDown) return;
requestAnimationFrame(function() {
var rect = element.getBoundingClientRect();
element.style.left = rect.x + e.movementX + "px";
element.style.top = rect.y + e.movementY + "px";
});
});
});
.box1 {
background: black;
width: 100px;
height: 100px;
color: white;
position: absolute
}
.box2 {
background: red;
width: 100px;
height: 100px;
position: fixed;
right: 100px;
}
<div class="box1" drag="test"></div>
<div class="box2" drag="test2"></div>
I'm looking to change opacity and then completely hide div on swipe up on certain threshold, like in the video below or in Photoswipe:
https://www.loom.com/share/29741bdadc7846bfbc747d3870815340
Unfortunately most off libraries only allow to register actual event start end, but not the amount of swiped pixels. How would I get the actual swiped distance and connect it to the swipe event?
Note: You can apply the animations used in this example on other elements like an overlay instead. The technique is the same.
Here is some code to move up an element, fade it out and remove it from display. Note that I only implemented the PointerEvent-api. You should also implement a fallback.
A summary about what is going on:
Detect a pointerdown on the element and allow the pointer to be used outside the element with setPointerCapture().
Detect a pointermove on the element. If the mouse/touch is moved up, also move up the element. ( I also restricted movement to the left, right, bottom, but you don't have to)
Detect a pointerup. After releasePointerCapture() the pointer will once more only be available in the default element and not outside it. Depending on the amount the element has moved up, the element is returned to its original position or animated out.
class SwipeOutBehaviour {
constructor(element) {
this.element = element;
this.dy = null;
this.initial_y = null;
this.animation_frame_state = 'completed';
if( window.PointerEvent ) {
this.element.addEventListener('pointerdown', this.start_drag.bind(this), true);
this.element.addEventListener('pointermove', this.drag.bind(this), true);
this.element.addEventListener('pointerup', this.drag_end.bind(this), true);
} else {
//should use composition instead if you re serious, for this example I only implemented PointerEvent some browsers will use Tpuchevent and MouseEvent
}
}
start_drag( event ){
event.preventDefault();
// only respond to a single touch
if( event.touches && event.touches.length > 1 ) return;
// allow pointerevents outside the target
event.target.setPointerCapture(event.pointerId);
// set initial pos
this.initial_y = ( event.targetTouches ) ? event.targetTouches[0].clientY : event.clientY;
}
drag( event ){
event.preventDefault();
if( this.initial_y === null ) return;
if( this.animation_frame_state === 'pending' ) return;
this.dy = ( event.targetTouches ) ? Math.floor( event.targetTouches[0].clientY - this.initial_y ) : Math.floor( event.clientY - this.initial_y );
if( this.dy > 0 ) return;
this.animation_frame_state = 'pending'
window.requestAnimationFrame( () => {
this.element.style.transform = `translateY(${this.dy}px)`
this.animation_frame_state = 'completed';
});
}
drag_end(event) {
event.preventDefault();
if(event.touches && event.touches.length > 0) return;
event.target.releasePointerCapture(event.pointerId);
if( this.dy < -100 ) {
window.requestAnimationFrame( () => {
this.element.style.transition = 'opacity 500ms, translateY 200ms';
this.element.style.transform = `translateY(-175px)`;
this.element.style.opacity = `0`;
this.animation_frame_state = 'completed';
window.setTimeout( () => {
// set display to none, you could remove it from the DOM instead
this.element.style.display = 'none';
}, 500)
});
} else {
window.requestAnimationFrame( () => {
this.element.style.transform = `translateY(0px)`
this.animation_frame_state = 'completed';
});
}
this.initial_y = null;
}
}
let element = document.getElementById('container');
new SwipeOutBehaviour( element );
#container {
margin: auto;
width: 150px;
height: 150px;
border: 1px solid black;
}
#box-of-doom {
margin: auto;
width: 200px;
height: 200px;
border: 1px solid red;
background: orange;
}
p {
text-align: center;
}
<p>Drag the item in the box of doom<p>
<div id='box-of-doom'>
<p>The box of doom<p>
</div>
<div id='container'>
<img alt='a placeholder' src='https://via.placeholder.com/150' />
</div>
Note: This answer is inspired by this documentation/article from Google about touch events, so you may want to read more there.
With a lot of event listeners and computed properties; I made a quick code pen using W3's draggable function, but added the opacity change myself:
// Make the DIV element draggable:
dragElement(document.getElementById("mydiv"));
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
//change background opacity:
const background = document.getElementById("background");
const bgHeight = background.offsetHeight;
const elmntHeight = elmnt.offsetHeight;
const adjustedBottom = bgHeight - elmntHeight;
const percentage = 1 - elmnt.offsetTop / adjustedBottom;
console.log(percentage)
background.style.opacity = percentage;
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
body {
margin: 0;
}
#background {
background: black;
width: 100vw;
height: 100vh;
position: absolute;
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<div id="background"></div>
<!-- Draggable DIV -->
<div id="mydiv">
<!-- Include a header DIV with the same name as the draggable DIV, followed by "header" -->
<div id="mydivheader">Click here to move</div>
<p>Move</p>
<p>this</p>
<p>DIV</p>
</div>
</div>
Far from perfect, but hopefully demonstrates an idea to expand on.
Im working on a site right now with a scroll loop effect (when you reach the bottom of the page it seamlessly jumps back to the top creating an endless loop). Though I am having an issue trying implement an effect to rotate the individual div's based on their offsetTop.
Here is a fiddle link with the rotate effect working without the scroll loop effect-> https://jsfiddle.net/jacob_truax/bgrkewny/3/
Here is a link to a fiddle with both effects -> https://jsfiddle.net/jacob_truax/b1x4dow7/18/
As you can see in the second fiddle, adding the scroll loop effect while implementing the rotation effect breaks the code. Can someone help me figure this out please?
Here is the js for the broken fiddle
const sections = document.querySelectorAll("section")
const divTag = document.querySelector("div.Loop")
const mainTag = document.querySelector("main")
var doc = window.document,
clones = divTag.querySelectorAll('.is-clone'),
disableScroll = false,
scrollHeight = 0,
scrollPos = 0,
clonesHeight = 0,
i = 0;
const addMovement = function() {
const topViewport = divTag.offsetTop
const midViewport = topViewport + (divTag.offsetHeight / 2)
sections.forEach(section => {
const topSection = section.offsetTop
const midSection = topSection + (section.offsetHeight / 2)
const distanceToSection = (midViewport - midSection)
console.log(distanceToSection)
const image = section.querySelector(".info")
image.style.transform = `rotate(${distanceToSection}deg)`
})
}
addMovement()
function getScrollPos () {
return (divTag.offsetTop || divTag.scrollTop) - (divTag.clientTop || 0);
}
function setScrollPos (pos) {
divTag.scrollTop = pos;
}
function getClonesHeight () {
clonesHeight = 0;
for (i = 0; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc () {
scrollPos = getScrollPos();
scrollHeight = divTag.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
function scrollUpdate () {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function () {
disableScroll = false;
}, 40);
}
}
function init () {
reCalc();
divTag.addEventListener('scroll', function () {
window.requestAnimationFrame(scrollUpdate);
addMovement()
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(reCalc);
addMovement()
}, false);
}
if (document.readyState !== 'loading') {
init()
} else {
doc.addEventListener('DOMContentLoaded', init, false)
}
Here is the css
html,
body {
height: 100%;
/* overflow: hidden; */
}
body {
color: #000;
}
main {
height: 100%;
position: relative;
top: 0;
left: 0;
}
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: center;
z-index: 1;
}
.Loop {
position: absolute;
height: 100%;
overflow: auto;
}
section {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
}
::scrollbar {
display: none;
}
section div {
position: absolute;
z-index: 2;
text-align: center;
width: 50%;
background-color: #ff0000;
}
section img {
position: relative;
width: 50%;
background-color: #000;
}
The offsetTop property returns the top position (in pixels) relative
to the top of the offsetParent element.
Changing line #14 to use scrollTop instead works:
const topViewport = divTag.scrollTop;
I have a page structure that looks like in the image below. When clicking anywhere in the #progress element I want to calculate how many percentages of it's full width that was clicked.
How can this be done?
< div id="progress" onMouseUp={ e => this._seekTo(e) }></div>
...
_seekTo(event) {
var progress = document.getElementById('progress');
console.log((event.clientX - progress.offsetLeft) / progress.offsetWidth * 100)
}
You can get the percentage click position of an elements width along the x-axis like:
document.getElementById('progress').addEventListener('click', function(e) {
var bcr = this.getBoundingClientRect();
console.log('You clicked to ', (e.clientX - bcr.left) / bcr.width);
});
e.clientX provides the horizontal coordinate within the application's client area at which the event occurred. source
Example progress bar:
function mouseSliderPosition(element, e) {
var bcr = element.getBoundingClientRect();
return {
x: Math.min(Math.max(0, (e.clientX - bcr.left) / bcr.width), 1),
y: Math.min(Math.max(0, (e.clientY - bcr.top) / bcr.height), 1)
}
};
function activateSlider(e) {
if (e.touches && e.touches.length > 1) {
return;
}
e.preventDefault();
window.activeSlider = this;
handleSliderMove(e);
}
function handleSliderMove(e) {
if (e.touches && e.touches.length > 1) {
return;
}
if (window.activeSlider) {
var progressBar = window.activeSlider.getElementsByClassName('progress-bar')[0];
var progressFill = window.activeSlider.getElementsByClassName('progress-fill')[0];
var value = mouseSliderPosition(progressBar, e.touches && e.touches[0] || e).x;
progressFill.style.transform = 'scaleX(' + value + ')';
}
}
function deactivateSlider(e) {
if (e.touches && e.touches.length > 0) {
return;
}
this.activeSlider = null;
}
document.querySelector('.progress-slider').addEventListener('mousedown', activateSlider)
document.querySelector('.progress-slider').addEventListener('touchstart', activateSlider)
window.addEventListener('mousemove', handleSliderMove);
window.addEventListener('mouseup', deactivateSlider);
window.addEventListener('touchmove', handleSliderMove);
window.addEventListener('touchend', deactivateSlider);
window.activeSlider = null;
.progress-slider {
padding: 10px;
cursor: pointer;
}
.progress-bar {
height: 2px;
background: rgba(100,100,100,0.5);
}
.progress-slider:hover .progress-bar {
height: 3px;
}
.progress-fill {
height: 100%;
background: rgba(255, 0, 0, 0.7);
transform-origin: 0 50%;
transform: scaleX(0);
}
<div class="progress-slider">
<div class="progress-bar"><div class="progress-fill"></div></div>
</div>