I am working on an AngularJs web app and have just started using ngTouch to recognize swipe left and right, I want to use this to open and close my side bar menu. Currently it works and the menu opens and close however, I have the swipe events tied to the entire wrapper like so <div ng-style="body_style" id="wrapper" ng-swipe-right="showMenu()" ng-swipe-left="hideMenu()"> which makes the entire app the swipe area. I would like to be able to set it to just an area in the top left quadrant of the app like so:
This will allow me to set up other swipe areas for additional functionality rather than being limited to just one area.
I know that I can make a div and place it there with defined width and height but the div is going to block content in other areas so that when someone goes to "click" something they would actually be clicking the invisible div for the swipe area. I know there has to be a way around this but I cannot think of one.
tldr: How do I set up an area to capture swipe events that will not effect the interactivity of other elements that may be below it?
In the source for angular-touch, it uses a function called bind. Bind takes an element that should be watched for swipes and watches its events, such as start. So if you wanted to, you could change the source to stop executing the rest of the code if the start is outside of some x or y coordinate that you set.
You could change this code,
pointerTypes = pointerTypes || ['mouse', 'touch'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event);
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event);
});
To something like this:
pointerTypes = pointerTypes || ['mouse', 'touch'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event);
if (startCoords.y <= 400 && startCoords.y >= 100 && startCoords.x <=500) {
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event);
}
});
Kind of an ugly hack, but it'll work.
Related
Am building a page for iOS 8 where I need to completely block built-in scrolling and scroll the page through javascript alone (Eg. using scrollTo() method).
I don't want to hide the scrollbars, just to block the scrolling action (seems like it's done through event.preventDefault() ) and implement scrolling in JavaScript.
Can this be done?
JavaScript has a scroll event, unfortunately it is not cancelable (i.e. event.preventDefault() does not work).
If however you're only using touch enabled devices and scrolling is done through touch the touchmove event will be fired upon scrolling, which is cancelable, using event.preventDefault(), and will prevent scrolling. Note that this also prevents other actions requiring touchmove such as zooming.
Another solution, which might look a bit glitchy on some devices, is to store the scroll position of the document at the place you want to lock it and go to those coordinates whenever a user is scrolling. Something like the following:
var locked = false,
posX = 0,
posY = 0;
var lock = function(){
//assign the current coordinates to the position variables
var doc = document.documentElement;
posX = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
posY = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
locked = true;
};
var unlock = function(){
locked = false;
};
document.addEventListener('scroll', function(e){
if(locked)
scrollTo(posX, posY);
});
A couple years ago I made a cordova/phonegap app using iScroll. At the time it helped me through some seriously bad scroll view issues.
You can disable scroll, force move to etc
Perhaps it could work for you.
I'm playing with replicating the recent, 30-year Apple Mac retrospective in pure CSS and Javascript for a small timeline of projects. I have the basic layout of the first full screen hero and the scalloped, expand-on-hover working appropriately. But the smooth scrolling of the timeline in the second half of the screen isn't working, even very slow scrolling is obviously jittery in Google Chrome 32.0.1700.102 on Mac OS X. You can download a folder with a single index.html and the necessary CSS and JS here.
Specifically, my two questions are:
What is a pure CSS/JavaScript solution to fixing this smooth scrolling? I'd appreciate something which debugged this example rather than pointed me to another, working one.
And related, what could/should I do to approach debugging this to isolate the problem? I naïvely tried collecting a JavaScript CPU Profile, but nothing jumped out as needing attention.
Basic Structure
The timeline is structured as a nav containing an ordered list, each li containing project, i.e.
<nav id='timeline'>
<ol>
<li class='project' id='zero'>
<div class='description'>
<h2> Project 0 </h2>
<span> The project that changed everything </span>
<div class='icon'></div>
</div> <!-- div.description -->
</li> <!-- li.project#zero -->
</ol>
</nav> <!-- nav#timeline -->
I have a simple event loop to detect global mouse position and handle scroll detection,
// 20ms event loop to update a global mouseX, mouseY position and handle scroll detection
var mouseX = null;
var mouseY = null;
var scrollTimeline = null;
var updateInterval = 10;
var scrolling = false;
window.onmousemove = function(event) {
mouseX = event.clientX;
mouseY = event.clientY;
if (!scrollTimeline) {
scrollTimeline = window.setInterval(scroll, updateInterval);
}
};
Which in turn calls a simple scroll handler every 10ms,
function scroll(event) {
var buffer = window.innerWidth/4;
var distanceToCenter = Math.abs(window.innerWidth/2-mouseX);
var speed = distanceToCenter/(window.innerWidth/2);
if (mouseX < buffer) {
scrolling = true;
scrollLeft(speed);
}
else if ((window.innerWidth - mouseX) < buffer) {
scrolling = true;
scrollRight(speed);
}
else {
scrolling = false;
window.clearInterval(scrollTimeline);
scrollTimeline = null;
}
}
All the actual scrolling is accomplished by adjusting the left attribute of the containing nav via two functions, scrollRight and scrollLeft, called with a speed argument depending on the mouse position.
function scrollRight(speed) {
var leftPixels = parseInt(getStyleProp(timeline, 'left'), 10);
var toShift = Math.pow(speed,3)*updateInterval;
var newLeft = leftPixels - toShift;
if (newLeft >= -1400 && newLeft <= 0) {
timeline.style.left = newLeft + 'px';
}
}
(getStyleProp is a simple utility function for getting the computed left attribute if it hasn't been explicitly set which I copied from this answer):
// Utility function to grab style properties when unset
function getStyleProp(elem, prop){
if(window.getComputedStyle)
return window.getComputedStyle(elem, null).getPropertyValue(prop);
else if(elem.currentStyle) return elem.currentStyle[prop]; //IE
}
What I've tried
So, with all of those basics out of the way, I've tried:
Removing some of the CSS transitions that create the scalloped effect
Using one image instead of six
Adjusting left in a loop, one pixel at a time, instead of in small jumps
Removing any of the contained text and their transitions.
And removing any contained li's in the nav--this solved the problem, but I'm not sure why/how that would be causing the observed jitter
Thanks!
Turns out the original jitter was because of the overhead of using the saturate transform in CSS. I found a far better solution using requestAnimationFrame.
I have a website page and I've added to the body of the page touch events.
More exactly for swipe right and swipe left. Since the event listener is added to the body of the page and I have added event.preventDefault(); i can't scroll the page any more.
How can i scroll the page in the browser ?
P.S. The code is pure javascript / library agnostic.
Edit #1. This site viewed in mobile seems to do it http://swipejs.com/ . It slides the tabs right to left and back as well as scroll the website. I just can't seen in the code how :(
Use iscroll plugin. it's help to you.
see example : http://cubiq.org/dropbox/iscroll4/examples/simple/
Unfortunately there is no easy answer. The best way is to build smart gesture recognizers. Or use something like this (for Safari Mobile):
http://mud.mitplw.com/JSGestureRecognizer/#single-gesture
You will notice that when you are touching a gesture recognizer, there is no scrolling. However, you could make the callback of a recognizer scroll the page.
Wondering why it only says it supports Safari mobile? That's because Safari mobile has its own set of touch events. However, you can use it as a start and try to add support for other platforms.
I have the same problem that swiping without "preventDefault()". Because I want to achieve a pulltorefresh's effect, I can only prevent the pulldown event but not pullup. The code like this:
function touchBindMove(evt){
//evt.preventDefault();
try{
var deviceHeight = window.innerHeight;
var touch = evt.touches[0]; //获取第一个触点
var x = Number(touch.pageX); //页面触点X坐标
var y = Number(touch.pageY); //页面触点Y坐标
//记录触点初始位置
if((y - offsetStart) > 0 && document.body.scrollTop == 0){
evt.preventDefault();
var page = document.getElementsByClassName('tweet-page')[0];
var rate = 0;
end = x;
offsetEnd = y;
rate = (offsetEnd - offsetStart) / (2 * deviceHeight);
//tool.print(rate);
easing.pullMotion(page, rate);
}
}catch(e){
alert(e.message);
}
}
"y - offsetStart" judges whether the event is pulldown and "document.body.scrollTop == 0" judges the scrollbar is in the middle or not.
Maybe it can help you a little bit.
I want to provide the user with the experience of scrolling through content, but I would like to load the content dynamically so the content in their viewing area is what they would expect, but there is no data above or below what they are looking at. For performance reasons, I don't want that data loaded. So when they scroll down new data gets loaded into their view, and data previously in their view is discarded. Likewise when scrolling up. The scroll bar should represent their location within the entire content though, so using "infinite scrolling" or "lazy loading" does not look like what I need.
My solution may be that I need to re-architect things. As of now, my project is a hex-viewer that allows you to drop a binary file onto it. I create html elements for every byte. This causes performance issues when you end up with a 1MB file (1,000,000+ DOM elements). One solution would be to not use DOM elements/byte but I think this will make other features harder, so I'd like to just not display as many DOM elements at once.
Make a div, set overflow to scroll or auto. As user scrolls you can change the content of the div.
You could look at yahoo mail (the JavaScript based one) to see how they do it (they add rows with email as you scroll).
You don't necessarily need custom scroll bars.
You could look for some code here for custom scroll bars:
http://www.java2s.com/Code/JavaScript/GUI-Components/Scrolltextwithcustomscollbar.htm
or here:
http://www.dyn-web.com/code/scroll/
I'm looking for an answer to this question as well so I'll share where I'm at with it.
I have a large amount of content I want to display vertically and have the user scroll through it. I can load it all into the DOM and scroll normally but that initial creation phase is horribly slow and scrolling can awfully slow also. Also, I will dynamically add to it as I stream more data in.
So I want the same thing which is to be able to dynamically populate and update a non-scrolling area with content. I want to make it seem as if the user is scrolling through that content and have a model (which has lots of data) that is kept off the DOM until it would be seen.
I figure I'll use a queue concept for managing the visible DOM elements. I'd store queueHeadIndex and queueTailIndex to remember what off-DOM elements are shown in the DOM. When the user scrolls down, I'd work out what whether the head of queue falls off the screen and if it does update queueHeadIndex and remove it's DOM element. Secondly I'd then work out whether I need to update queueTailIndex and add a new element to the DOM. For the elements currently in the DOM I'd need to move them (not sure if they need animation here or not yet).
UPDATE:
I've found this which seems to have some promise http://jsfiddle.net/GdsEa/
My current thinking is that there are two parts to the problem.
Firstly, I think I want to disable scrolling and have some sort of virtual scrolling. I've just started looking at http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/ for this. This would capture all the events and enable me to programmatically adjust what's currently visible etc. but the browser wouldn't actually be scrolling anything. This seems to provide mouse wheel driven scrolling.
Secondly, I think I need to display a scroll bar. I've had a look at http://codepen.io/chriscoyier/pen/gzBsA and I'm searching around more for something that looks more native. I just want it to visually display where the scroll is and allow the user to adjust the scroll position by dragging the scroller.
Stackoverflow is insisting I paste code so here is some code from that codepen link above
var elem = document.getElementById('scroll-area'),
track = elem.children[1],
thumb = track.children[0],
height = parseInt(elem.offsetHeight, 10),
cntHeight = parseInt(elem.children[0].offsetHeight, 10),
trcHeight = parseInt(track.offsetHeight, 10),
distance = cntHeight - height,
mean = 50, // For multiplier (go faster or slower)
current = 0;
elem.children[0].style.top = current + "px";
thumb.style.height = Math.round(trcHeight * height / cntHeight) + 'px';
var doScroll = function (e) {
// cross-browser wheel delta
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// (1 = scroll-up, -1 = scroll-down)
if ((delta == -1 && current * mean >= -distance) || (delta == 1 && current * mean < 0)) {
current = current + delta;
}
// Move element up or down by updating the `top` value
elem.children[0].style.top = (current * mean) + 'px';
thumb.style.top = 0 - Math.round(trcHeight * (current * mean) / cntHeight) + 'px';
e.preventDefault();
};
if (elem.addEventListener) {
elem.addEventListener("mousewheel", doScroll, false);
elem.addEventListener("DOMMouseScroll", doScroll, false);
} else {
elem.attachEvent("onmousewheel", doScroll);
}
I imagine I'll have one class that listens to scroll events by either the virtual scroll method or the ui and then updates the ui scroller and the ui of the content I'm managing.
Anyway, I'll update this if I find anything more useful.
I think avoiding using DOM elements/byte is going to be the easier solution for me than creating a fake scrolling experience.
UPDATE: I ultimately solved this as explained here: Javascript "infinite" scrolling for finite content?
You're taking about using some serious javascript, specifically AJAX and JSON type elements. There is no easy answer to your questions. You'd need to do a lot of R&D on the subject.
Simple, I just would like to have it so when a user is dragging an item and they reach the very bottom or top of the viewport (10px or so), the page (about 3000px long) gently scrolls down or up, until they move their cursor (and thus the item being dragged) out of the region.
An item is an li tag which uses jquery to make the list items draggable. To be specific:
../jquery-ui-1.8.14.custom.min.js
http://code.jquery.com/jquery-1.6.2.min.js
I currently use window.scrollBy(x=0,y=3) to scroll the page and have the variables of:
e.pageY ... provides absolute Y-coordinates of cursor on page (not relative to screen)
$.scrollTop() ... provides offset from top of page (when scroll bar is all the way up, it is 0)
$.height()... provides the height of viewable area in the user's browser/viewport
body.offsetHeight ... height of the entire page
How can I achieve this and which event best accommodates this (currently its in mouseover)?
My ideas:
use a an if/else to check if it is in top region or bottom, scroll up if e.pageY is showing it is in the top, down if e.page& is in bottom, and then calling the $('li').mouseover() event to iterate through...
Use a do while loop... this has worked moderately well actually, but is hard to stop from scrolling to far. But I am not sure how to control the iterations....
My latest attempt:
('li').mouseover(function(e) {
totalHeight = document.body.offsetHeight;
cursor.y = e.pageY;
var papaWindow = window;
var $pxFromTop = $(papaWindow).scrollTop();
var $userScreenHeight = $(papaWindow).height();
var iterate = 0;
do {
papaWindow.scrollBy(0, 2);
iterate++;
console.log(cursor.y, $pxFromTop, $userScreenHeight);
}
while (iterate < 20);
});
Works pretty well now, user just needs to "jiggle" the mouse when dragging items sometimes to keep scrolling, but for scrolling just with mouse position its pretty solid. Here is what I finally ended up using:
$("li").mouseover(function(e) {
e = e || window.event; var cursor = { y: 0 }; cursor.y = e.pageY; //Cursor YPos
var papaWindow = parent.window;
var $pxFromTop = $(papaWindow).scrollTop();
var $userScreenHeight = $(papaWindow).height();
if (cursor.y > (($userScreenHeight + $pxFromTop) / 1.25)) {
if ($pxFromTop < ($userScreenHeight * 3.2)) {
papaWindow.scrollBy(0, ($userScreenHeight / 30));
}
}
else if (cursor.y < (($userScreenHeight + $pxFromTop) * .75)) {
papaWindow.scrollBy(0, -($userScreenHeight / 30));
}
}); //End mouseover()
This won't work as the event only fires while you're mouse is over the li.
('li').mouseover(function(e) { });
You need to be able to tell the position of the mouse relative to the viewport when an item is being dragged. When the users starts to drag an item attach an 'mousemove' event to the body and then in that check the mouse position and scroll when necessary.
$("body").on("mousemove", function(event) {
// Check mouse position - scroll if near bottom or top
});
Dont forget to remove your event when the user stops dragging.
$("body").off("mousemove", function(event) {
// Check mouse position - scroll if near bottom or top
});
This may not be exactly what you want, but it might help. It will auto-scroll when the mouse is over the 'border of the screen' (a user defined region). Say you have a 40px wide bar on the right of the screen, if the mouse reaches the first 1px, it will start scrolling. Each px you move into it, the speed will increase. It even has a nice easing animation.
http://www.smoothdivscroll.com/v1-2.htm
I get a weekly newsletter (email) from CodeProject, and it had some stuff that certainly looks like it will solve my problem... hopefully this can help others:
http://johnpolacek.github.com/scrollorama/ -- JQuery based and animates the scroll
https://github.com/IanLunn/jQuery-Parallax -- JQuery based, similar to above
http:// remysharp. com/2009/01/26/element-in-view-event-plugin/ -- JQuery, detects whether an element is currently in view of the user (super helpful for this issue!)
Also the site in #2 had some interesting code:
var windowHeight = $window.height();
var navHeight = $('#nav').height() / 2;
var windowCenter = (windowHeight / 2);
var newtop = windowCenter - navHeight;
//ToDo: Find a way to use these vars and my original ones to determine scroll regions