I am building simple slider with dragging ability. For now, I use pageX to determine the position, like so:
var dragdir = 0,
drag = null;
$('article.small-section').on('mousedown', function(ev) {
drag = 1;
dragdir = ev.pageX;
if(drag) {
$(this).css('cursor','move');
}
}).on('mouseup', function(e){
drag = null
if(!drag) {
$(this).css('cursor','initial');
$(this).css('left','0')
}
});
$('article.small-section').on('mousemove', function(e) {
if(drag) {
if(dragdir > e.pageX) {
//console.log(dragdir+'|'+e.pageX)
if(dragdir > e.pageX+100) {
$(this).parent('section').cycle('prev');
}
$(this).css('right','');
$(this).css('left','-'+(parseInt(e.pageX/10))+'px');
} else {
if(dragdir < e.pageX-100) {
$(this).parent('section').cycle('next');
}
$(this).css('left','');
$(this).css('right','-'+(parseInt(e.pageX/10))+'px');
}
}
});
It works great dragging to the right. To the left - it does, then it backs (or doesn't go at all). I noticed that pageX is going down - that's the problem. How can I fix it?
Working fiddle: http://jsfiddle.net/2jEJH/1/
Related
I have a webpage that when scrolled down, the text freezes when it reaches the last paragraph of text but the images keep on scrolling. I've got the implementation working but there is a lot of jank when scrolling with a mouse wheel, not so much if I click and drag the scroll bar.
Are there any optimizations I can make to this code to make work as intended or is there a different way to accomplish the same task?
window.addEventListener('scroll', function (e) {
window.requestAnimationFrame(keepTextStationary);
//keepTextStationary(); // Less janky, but still horrible
});
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && document.documentElement.scrollTop > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
}
Here is the site so you can see the problem.
https://bowerbankninow.azurewebsites.net/exhibitions/oscar-perry-the-pheasant
You are causing layout trashing every time you call getBoundingClientRect. Try debouncing your scroll events:
var lastScrollY = 0;
var ticking = false;
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && lastScrollY > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
ticking = false;
}
function onScroll() {
lastScrollY = document.documentElement.scrollTop;
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(keepTextStationary);
ticking = true;
}
}
window.addEventListener('scroll', onScroll );
See this article for in-depth explanation: https://www.html5rocks.com/en/tutorials/speed/animations/
You dont.
Relocations / styling in javascript take place after the CSS has been loaded. Bad practise. What you can do, is make it animated to make it look less horrible.
Why is pure CSS not an option ?
Can you recommend a JS library that actually provides edge swipe functionality when working with bare-bones HTML & CSS?
I've searched all over and haven't found a source of truth for that problem.
I've seen lots and lots of libraries enabling swipe gestures but not edge swipe.
My last attempt was using Hammer.js which I've tried implementing as:
var swipe = new Hammer(document);
// detect swipe and call to a function
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
var endPoint = e.pointers[0].pageX;
var distance = e.distance;
var origin = endPoint - distance;
//swipe right to open nav
if (origin <= 15 && e.type == 'swiperight') {
// open main menu
$('#navigation-menu').animate({
left: '0'
});
} else {
// close/hide menu(s)
$('#navigation-menu').animate({
left: '-100%'
});
}
});
Further, if not using any library, how can I implement a mobile edge swipe to show and hide content, (in my case it'd be a navigation menu) with vanilla JS?
At this point I'm open to either solution/direction.
Here is a solution, you can set thresholdStart, End, Milliseconds. You may want to tidy up the code, and port it for touch events (I used mouse events for testing in my browser more easily).
Use:
swipeEdgeFromLeft function and swipeEdgeFromRight function.
var div = document.body;
var mouse = {
isDown: false,
inLeft: false,
inRight: false,
downTimestamp: null
};
var width, thresholdStart, thresholdEnd, thresholdMilliseconds;
function resize(){
width = window.innerWidth;
thresholdStart = 0.1*width;//within 10% of screen width
thresholdEnd = 0.13*width;//beyond 13% of screen width
thresholdMilliseconds = 500;//must be done in 500 milliseconds
}
document.addEventListener("resize", resize, false);
resize();//initialize
div.addEventListener('mousedown'/*'touchstart'*/, function(e){
var x = e./*touches[0].*/pageX;
mouse.isDown = true;
mouse.downTimestamp = performance.now();
if(x < thresholdStart){
mouse.inLeft = true;
} else if(x > width-thresholdStart){
mouse.inRight = true;
}
});
div.addEventListener('mousemove'/*'touchmove'*/, function(e){
var x = e./*touches[0].*/pageX;
if(mouse.inLeft && x > thresholdEnd){
mouse.inLeft = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromLeft();
}
} else if(mouse.inRight && x < width-thresholdEnd){
mouse.inRight = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromRight();
}
}
});
div.addEventListener('mouseup'/*'touchend'*/, function(e){
//var x = e./*changedTouches[0].*/pageX;
mouse.isDown = false;
mouse.inLeft = false;
mouse.inRight = false;
mouse.downTimestamp = null;
});
function swipeEdgeFromLeft(){
console.log("edge swipe from left");
}
function swipeEdgeFromRight(){
console.log("edge swipe from right");
}
body {
max-width: 100vw;
height: 100vh;
}
.bar {
height: 100vh;
background-color: rgba(0,0,0,0.4);
position: fixed;
pointer-events: none;
}
#left-inner-threshold {
width: calc(0.1 * 100vw);
left: 0;
}
#right-inner-threshold {
width: calc(0.1 * 100vw);
right: 0;
}
#left-outer-threshold {
width: calc(0.13 * 100vw);
left: 0;
}
#right-outer-threshold {
width: calc(0.13 * 100vw);
right: 0;
}
<div id="left-inner-threshold" class="bar"></div>
<div id="left-outer-threshold" class="bar"></div>
<div id="right-inner-threshold" class="bar"></div>
<div id="right-outer-threshold" class="bar"></div>
Here's a solution to your existing code using Hammer.js v2.0.8
The explanation for how to achieve the edge swipe can be found here answered by #jovinbm.
$(document).ready(function () {
const swipe = new Hammer(document);
function getStartPosition(e) {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
}
};
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
const { x } = getStartPosition(e);
console.log(x);
//swipe right to open nav /* note the condition here */
if (e.type == 'swiperight' && x >= 0 && x <= 50) {
// open menu
$('#navigation').animate({
left: '0'
});
//swiping left should slide out nav and/or sub-nav
} else {
// close/hide menu
$('#navigation, #task-menu').animate({
left: '-100%'
});
}
});
});
Here's a pen showing it in action:
For swipes, only the final pointerup event is included as the srcEvent in the event object passed to your handler (see http://hammerjs.github.io/api/). The initial pointerdown event that carries the details of the initial position of where the swipe event started is not provided in the hammer event object. Fortunately, you can use the srcEvent in the event object to get the starting position of the event initial pointerdown event.
const getStartPosition = (e) => {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
};
};
const handleSwipe = (e) => {
const {x} = getStartPosition(e);
if (x >= 0 && x <= 50) {
// handle swipe from left edge e.t.c
}
else {
// handle other case
}
};
The srcEvent is just a normal javascript event that inherits properties from UIEvent hence the pageX/pageY api above. This will probably not work in other browsers since some of them are not standardized
I'm doing a webpage with listed items and those items are in Alphabetic order. Today i saw Tooltip in Google's contact web when scrolling my Mobile contact list from that web. Tooltip was fixed with scroll-bar and moves along with scroll-bar. I was wondering to implement that idea into my project because my list items are also in Alphabetic order. Can someone help out how to make a Tooltip like Google does?
This should get you started - clearly this wont work on mobile devices, but it may be a good jumping off point
var tooltip = document.createElement('div');
tooltip.style.cssText = 'position:fixed;right:18px;top:0;display:none;width:4em;height:1.2em;background:black;font-size:24px;font-weight:bold;color:white;text-align:center;padding-top:5px;';
document.body.appendChild(tooltip);
var mouseIsDown = false;
var displayed = false;
window.addEventListener('mousedown', function() {
mouseIsDown = true;
});
window.addEventListener('mouseup', function() {
mouseIsDown = false;
if (displayed) {
displayed = false;
tooltip.style.display = 'none';
}
});
window.addEventListener('mousemove', function(e) {
if (displayed) {
tooltip.style.top = e.clientY + 'px';
console.log(e);
}
});
window.addEventListener('scroll', function() {
if (mouseIsDown) {
var pos = parseInt(window.scrollY * 100.0 / window.scrollMaxY);
tooltip.textContent = pos + '%';
if (!displayed) {
tooltip.style.display = 'block';
displayed = true;
}
}
});
I was working on the same problem.. what about this (naive) solution?
$(window).scroll(function(){
$("#scrollerTooltip").css("top",(window.pageYOffset+window.pageYOffset*window.innerHeight)/document.body.scrollHeight)+'px');
});
Here's an example for this issue (working on chrome)
I want to detect scroll/touch direction on phones. For desktop, I use .on('DOMMouseScroll') and .('mousewheel') but this does not work on phones. Body of page is overflow: hidden; Anybody know how to detect this?
It seems I fell into the same case of yours.
Since I needed a lot of research to put all things together, I'm posting it all here:
if (Modernizr.touch) {
// Determining swipe direction
// http://stackoverflow.com/a/22257774/1064325
var touchStartY;
document.addEventListener('touchstart', function (e){
touchStartY = e.touches[0].clientY;
}, false);
// Preventing iOS end of page bounce effect
// http://stackoverflow.com/a/7771215/1064325
document.addEventListener('touchmove', function (e){
e.preventDefault();
}, false);
document.addEventListener('touchend', function (e){
var touchEndY = e.changedTouches[0].clientY;
if (touchStartY > touchEndY + 5) {
// NEXT
} else if (touchStartY < touchEndY - 5) {
// PREV
}
}, false);
} else {
// Handling wheeling properly
// http://stackoverflow.com/a/3515490/1064325
var deltaWheel = 0;
var wheelTimeout = 0;
document.addEventListener('wheel', function(event) {
deltaWheel += event.deltaY;
clearTimeout(wheelTimeout);
wheelTimeout = setTimeout(function() {
if (deltaWheel < -5) {
// PREV
}
if (deltaWheel > +5) {
// NEXT
}
deltaWheel = 0;
}, 50);
});
}
i'm trying to build some JQuery UI Selectable demo.
Is it possible to know if the user is selecting from left to right or right to left ?
I have tried this solution but it's not working
var last_selected_id="first";
$( ".selectable" ).selectable({
selecting: function(event, ui) {
if(last_selected_id=="first")
{
//first, do nothing
}
else
{
if(ui.selecting.id>last_selected_id)
{
//left to right
console.log('left to right');
}
else
{
console.log('right to left');
}
last_selected_id = ui.selecting.id;
},....
The ids are sequentially bigger from left to right
You should use the coordinates of mouse pointer provided by JQuery UI event object.
var startX;
var direction;
$( ".selectable" ).selectable({
start: function( event, ui ) {
startX = event.pageX;
direction = "";
},
selecting: function( event, ui ) {
direction = (event.pageX >= startX) ? "right" : "left";
}
});
you're missing 2 statments, one at the beggining saying the ui.selecting.id>0, and the the else if when the ui.selecting.id is lower the the last_element_id this code should work`
if(ui.selecting.id>0)
{
if(last_selected_id=="first")
{
console.log(last_selected_id);
}
else
{
if(ui.selecting.id>last_selected_id)
{
//left to right
direction = "ltr";
}else if(ui.selecting.id<last_selected_id){
//right to left
direction = "rtl";
}
}
if(ui.selecting.id>0){
last_selected_id = ui.selecting.id;
}
}
},