How to scroll always on same offset? - javascript

I have a titanium scrollView. If i make scroll action, the content is moving depending on the force of my finger scroll action. I need to make it scroll for defined value always (i.e. 400 pixels). How can i realize this?
I've tried it with $.scrollTo() and scrollingEnabled = false, but it's really not so smoothy.
Any other proposals?
ADDED
here is video of similar idea (sorry for quality)

If you want to do a kind of scrollableView, but vertical (the user can pull the scrollView, if the pull is longer than a threshold, the scrollView switch to the next vertical view, if not, the scrollView returns to its current vertical view), you can do it by using the "scroll" and "dragend" events. Here's an example for a drag down, and a scrollView with two 400px views stacked vertically:
var scrollView.top = -400; //Size of the view
var threshold = -100; // Threshold to pull for changing view
var changingPageTriggered = false;
var pulling = false;
scrollView.addEventListener('scroll',function(e) {
var offset = e.y;
if (offset <= threshold && !pulling) {
pulling = true;
changingPageTriggered = true;
} else {
pulling = false;
changingPageTriggered = false;
}
});
scrollView.addEventListener('dragend',function(e) {
if (pulling && changingPageTriggered) {
changingPageTriggered = false;
pulling = false;
changePage()
}
});
function changePage() {
scrollView.scrollTo(0,-400)
scrollView.top = 0
}
I used something similar in a project but I didn't try the code above. You'll need to tweak it and add support for the drag up, it should get you started.

Related

How to prevent Read More/Read less button to jumping to the bottom?

I've created a button for Read More/Read Less functionality but when I'm clicking on the show less it jumps to the bottom. Could you please tell me how to fix this?...it should go to the same position...I'm using oxygen builder (code for this [ https://codepen.io/nick7961/pen/qByYMXZ?editors=0010
])
One way of doing this is to grab the current scroll y value and divide it by the body height to get the scroll position as a percentage. You'll have to do this in the event listener, before changes are made. In my function, setScroll, you can get the new body height and multiply it by the percentage you grabbed earlier, to keep the scroll in the same relative position.
button.addEventListener('click', () => {
const defaultValue = {
element: arrowIcon,
currentIcon: 'fa-chevron-down',
newIcon: 'fa-chevron-up',
};
//show content
if (initial.showAllContent){
showButton(buttonShowLess);
showButton(buttonShowMore, false);
content.classList.remove('gradientContent', 'maxContentHeight');
}else{
let relativeScroll = window.scrollY / document.body.clientHeight;
showButton(buttonShowLess, false);
showButton(buttonShowMore);
defaultValue.currentIcon = 'fa-chevron-up';
defaultValue.newIcon = 'fa-chevron-down';
content.classList.add('gradientContent', 'maxContentHeight');
setScroll(relativeScroll);
}
changeIcon(defaultValue);
initial.showAllContent = !initial.showAllContent;
});
function setScroll(relativeScroll) {
let scrollValue = document.body.clientHeight * relativeScroll;
window.scroll(0, scrollValue);
}
If you wanted to bounce the user back to the top, you could simply use:
window.scroll(0, 0);

How to implement canvas panning with Fabric.js

I have a Fabric.js canvas and I want to implement the full-canvas panning that software packages usually do with a "hand" tool. It's when you press one of the mouse buttons, then move over the canvas while holding the mouse button and the visible portion of the canvas changes accordingly.
You can see in this video what I want to achieve.
In order to implement this functionality I wrote the following code:
$(canvas.wrapperEl).on('mousemove', function(evt) {
if (evt.button == 2) { // 2 is the right mouse button
canvas.absolutePan({
x: evt.clientX,
y: evt.clientY
});
}
});
But it doesn't work. You can see in this video what happens.
How can I modify my code in order:
For panning to work like in the first video?
For the event handler to consume the event? It should prevent the context menu from appearing when the user presses or releases the right mouse button.
An easy way to pan a Fabric canvas in response to mouse movement is to calculate the cursor displacement between mouse events and pass it to relativePan.
Observe how we can use the screenX and screenY properties of the previous mouse event to calculate the relative position of the current mouse event:
function startPan(event) {
if (event.button != 2) {
return;
}
var x0 = event.screenX,
y0 = event.screenY;
function continuePan(event) {
var x = event.screenX,
y = event.screenY;
fc.relativePan({ x: x - x0, y: y - y0 });
x0 = x;
y0 = y;
}
function stopPan(event) {
$(window).off('mousemove', continuePan);
$(window).off('mouseup', stopPan);
};
$(window).mousemove(continuePan);
$(window).mouseup(stopPan);
$(window).contextmenu(cancelMenu);
};
function cancelMenu() {
$(window).off('contextmenu', cancelMenu);
return false;
}
$(canvasWrapper).mousedown(startPan);
We start panning on mousedown and continue panning on mousemove. On mouseup, we cancel the panning; we also cancel the mouseup-cancelling function itself.
The right-click menu, also known as the context menu, is cancelled by returning false. The menu-cancelling function also cancels itself. Thus, the context menu will work if you subsequently click outside the canvas wrapper.
Here is a page demonstrating this approach:
http://michaellaszlo.com/so/fabric-pan/
You will see three images on a Fabric canvas (it may take a moment or two for the images to load). You'll be able to use the standard Fabric functionality. You can left-click on the images to move them around, stretch them, and rotate them. But when you right-click within the canvas container, you pan the whole Fabric canvas with the mouse.
I have an example on Github using fabric.js Canvas panning: https://sabatinomasala.github.io/fabric-clipping-demo/
The code responsible for the panning behaviour is the following: https://github.com/SabatinoMasala/fabric-clipping-demo/blob/master/src/classes/Panning.js
It's a simple extension on the fabric.Canvas.prototype, which enables you to toggle 'drag mode' on the canvas as follows:
canvas.toggleDragMode(true); // Start panning
canvas.toggleDragMode(false); // Stop panning
Take a look at the following snippet, documentation is available throughout the code.
const STATE_IDLE = 'idle';
const STATE_PANNING = 'panning';
fabric.Canvas.prototype.toggleDragMode = function(dragMode) {
// Remember the previous X and Y coordinates for delta calculations
let lastClientX;
let lastClientY;
// Keep track of the state
let state = STATE_IDLE;
// We're entering dragmode
if (dragMode) {
// Discard any active object
this.discardActiveObject();
// Set the cursor to 'move'
this.defaultCursor = 'move';
// Loop over all objects and disable events / selectable. We remember its value in a temp variable stored on each object
this.forEachObject(function(object) {
object.prevEvented = object.evented;
object.prevSelectable = object.selectable;
object.evented = false;
object.selectable = false;
});
// Remove selection ability on the canvas
this.selection = false;
// When MouseUp fires, we set the state to idle
this.on('mouse:up', function(e) {
state = STATE_IDLE;
});
// When MouseDown fires, we set the state to panning
this.on('mouse:down', (e) => {
state = STATE_PANNING;
lastClientX = e.e.clientX;
lastClientY = e.e.clientY;
});
// When the mouse moves, and we're panning (mouse down), we continue
this.on('mouse:move', (e) => {
if (state === STATE_PANNING && e && e.e) {
// let delta = new fabric.Point(e.e.movementX, e.e.movementY); // No Safari support for movementX and movementY
// For cross-browser compatibility, I had to manually keep track of the delta
// Calculate deltas
let deltaX = 0;
let deltaY = 0;
if (lastClientX) {
deltaX = e.e.clientX - lastClientX;
}
if (lastClientY) {
deltaY = e.e.clientY - lastClientY;
}
// Update the last X and Y values
lastClientX = e.e.clientX;
lastClientY = e.e.clientY;
let delta = new fabric.Point(deltaX, deltaY);
this.relativePan(delta);
this.trigger('moved');
}
});
} else {
// When we exit dragmode, we restore the previous values on all objects
this.forEachObject(function(object) {
object.evented = (object.prevEvented !== undefined) ? object.prevEvented : object.evented;
object.selectable = (object.prevSelectable !== undefined) ? object.prevSelectable : object.selectable;
});
// Reset the cursor
this.defaultCursor = 'default';
// Remove the event listeners
this.off('mouse:up');
this.off('mouse:down');
this.off('mouse:move');
// Restore selection ability on the canvas
this.selection = true;
}
};
// Create the canvas
let canvas = new fabric.Canvas('fabric')
canvas.backgroundColor = '#f1f1f1';
// Add a couple of rects
let rect = new fabric.Rect({
width: 100,
height: 100,
fill: '#f00'
});
canvas.add(rect)
rect = new fabric.Rect({
width: 200,
height: 200,
top: 200,
left: 200,
fill: '#f00'
});
canvas.add(rect)
// Handle dragmode change
let dragMode = false;
$('#dragmode').change(_ => {
dragMode = !dragMode;
canvas.toggleDragMode(dragMode);
});
<div>
<label for="dragmode">
Enable panning
<input type="checkbox" id="dragmode" name="dragmode" />
</label>
</div>
<canvas width="300" height="300" id="fabric"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.15/fabric.min.js"></script>
Not sure about FabricJS, but it could be in such a way:
for it to work like in the first video:
By making use of CSS cursor property, toggling it on mousedown and mouseup events using javascript.
the event handler consume the event (prevent the context menu from appearing, when the user releases the right mouse button):
Using javascript we return false on contextmenu event
CODE: with a little problem ( * )
using jQuery JS Fiddle 1
$('#test').on('mousedown', function(e){
if (e.button == 2) {
// if right-click, set cursor shape to grabbing
$(this).css({'cursor':'grabbing'});
}
}).on('mouseup', function(){
// set cursor shape to default
$(this).css({'cursor':'default'});
}).on('contextmenu', function(){
//disable context menu on right click
return false;
});
Using raw javascript JS Fiddle 2
var test = document.getElementById('test');
test.addEventListener('mousedown', function(e){
if (e.button == 2) {
// if right-click, set cursor shape to grabbing
this.style.cursor = 'grabbing';
}
});
test.addEventListener('mouseup', function(){
// set cursor shape to default
this.style.cursor = 'default';
});
test.oncontextmenu = function(){
//disable context menu on right click
return false;
}
( * ) Problem:
The above snippets works as it should but there's a cross-browser issue, if you check the above fiddles in Firefox - or Opera -you'll see the correct behavior, when checked in Chrome and IE11 - didn't checked it with Edge or Safari - I found that Chrome and IE don't support the grabbing cursor shape, so in the above code snippets, change the cursor lines into this:
jQuery: $(this).css({'cursor':'move'}); JS Fiddle 3
Raw javascript: this.style.cursor = 'move'; JS Fiddle 4
Now we have a working code but without the hand cursor. but there is the following solution:-
SOLUTIONS:
Chrome and Safari support grab and grabbing with the -webkit- prefix like:
$(this).css({'cursor': '-webkit-grabbing'});
but you need to make browser sniffing first, if Firefox then the default and standard code, if Chrome and Safari then with the -webkit- prefix, and this still makes IE out of the game.
Have a look at this example, test it with Chrome, Safari, Firefox, Opera and IE you can see that cursor: url(foo.bar) works and supported in ALL browsers.
Chrome, Safari, Firefox and Opera shows the yellow smile image smiley.gif, but IE shows the red ball cursor url(myBall.cur).
So I think you can make use of this, and a grabbing hand image like this
Or this:
You can use an image like the above, a png or gif format with all browsers except IE which supports .cur, so you need to find a way to convert it into a .cur. Google search shows many result of convert image to cur
Note that, although this cursor:url(smiley.gif),url(myBall.cur),auto; - with fallback support separated by comma, works well in the W3Schools example shown above, I couldn't get it to work the same way in javascript, I tried $(this).css({'cursor': 'grabbing, move'}); but it didn't work.
I also tried doing it as CSS class
.myCursor{ cursor: grabbing, -webkit-grabbing, move; }
Then with jQuery $(this).addClass('myCursor'); but no avail either.
So you still need to make browser sniffing whether you are going the second solution or a hybrid fix of the both solutions, this is my code which I've used couple times to detect browser and it worked well at the time of this post but you porbabely won't need the "Mobile" and "Kindle" parts.
// Detecting browsers
$UA = navigator.userAgent;
if ($UA.match(/firefox/i)) {
$browser = 'Firefox';
} else if ($UA.indexOf('Trident') != -1 && $UA.indexOf('MSIE') == -1) {
$browser = 'MSIE';
} else if ($UA.indexOf('MSIE') != -1) {
$browser = 'MSIE';
} else if ($UA.indexOf('OPR/') != -1) {
$browser = 'Opera';
} else if ($UA.indexOf("Chrome") != -1) {
$browser = 'Chrome';
} else if ($UA.indexOf("Safari")!=-1) {
$browser = 'Safari';
}
if($UA.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Nokia|Mobile|Opera Mini/i)) {
$browser = 'Mobile';
}else if($UA.match(/KFAPWI/i)){
$browser = 'Kindle';
}
console.log($browser);
Resources:
https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
http://www.w3schools.com/cssref/pr_class_cursor.asp
https://css-tricks.com/almanac/properties/c/cursor/
Google images search of grabbing hand cursor
i have made an example on jsfiddle , that we can actually drag the whole canvas with all its objects, into a parent div, like the picture,and i will try to explain it step by step.
First of all i download the drag library jquery.dradscroll.js, you can find it on the net. This is a small js file that with little changes it can helps us complete the task.
download link: http://www.java2s.com/Open-Source/Javascript_Free_Code/jQuery_Scroll/Download_jquery_dragscroll_Free_Java_Code.htm
create the container that holds our canvas.
<div class="content">
<canvas id="c" width="600" height="700" ></canvas>
</div>
little css
.content{
overflow:auto;
width:400px;
height:400px;
}
javascript:
a. create the canvas.
b. make default cursor , when it is over canvas , open hand
canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';
c. override the __onMouseDown function , for change to the closedhand cursor( at end).
fabric.Canvas.prototype.__onMouseDown = function(e){
// accept only left clicks
var isLeftClick = 'which' in e ? e.which === 1 : e.button === 1;
if (!isLeftClick && !fabric.isTouchSupported) {
return;
}
if (this.isDrawingMode) {
this._onMouseDownInDrawingMode(e);
return;
}
// ignore if some object is being transformed at this moment
if (this._currentTransform) {
return;
}
var target = this.findTarget(e),
pointer = this.getPointer(e, true);
// save pointer for check in __onMouseUp event
this._previousPointer = pointer;
var shouldRender = this._shouldRender(target, pointer),
shouldGroup = this._shouldGroup(e, target);
if (this._shouldClearSelection(e, target)) {
this._clearSelection(e, target, pointer);
}
else if (shouldGroup) {
this._handleGrouping(e, target);
target = this.getActiveGroup();
}
if (target && target.selectable && !shouldGroup) {
this._beforeTransform(e, target);
this._setupCurrentTransform(e, target);
}
// we must renderAll so that active image is placed on the top canvas
shouldRender && this.renderAll();
this.fire('mouse:down', { target: target, e: e });
target && target.fire('mousedown', { e: e });
if(!canvas.getActiveObject() || !canvas.getActiveGroup()){
flag=true;
//change cursor to closedhand.cur
canvas.defaultCursor = 'url("http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur") 15 15, crosshair';
}//end if
override the __onMouseUp event ,to change back the cursor to openhand.
fabric.Canvas.prototype.__onMouseUp = function(e){
if(flag){
canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';
flag=false;
}
};
You initialize the dragScroll() to work on the content that hosts the canvas:
$('.content').dragScroll({});
Some small changes on the jquery.dragScroll.js file, so as to understand when to drag the canvas and when not. On mousedown() event we add an if statement to check whether we have an active object or group.If true ,no canvas drag.
$($scrollArea).mousedown(function (e) {
if (canvas.getActiveObject() || canvas.getActiveGroup()) {
console.log('no drag');return;
} else {
console.log($('body'));
if (typeof options.limitTo == "object") {
for (var i = 0; i < options.limitTo.length; i++) {
if ($(e.target).hasClass(options.limitTo[i])) {
doMousedown(e);
}
}
} else {
doMousedown(e);
}
}
});
on mousedown event we grab the DOM element (.content) and get the top & left position
function doMousedown(e) {
e.preventDefault();
down = true;
x = e.pageX;
y = e.pageY;
top = e.target.parentElement.parentElement.scrollTop; // .content
left = e.target.parentElement.parentElement.scrollLeft;// .content
}
If we dont want to have the scrollbars visible:
.content{
overflow:hidden;
width:400px;
height:400px;
}
There is a small problem though,jsfiddle, accepts only https libraries ,so it blocks fabricjs, except if you add it from 'https://rawgit.com/kangax/fabric.js/master/dist/fabric.js', but again it still blocks it some times (at least on my chrome and mozilla).
jsfiddle example : https://jsfiddle.net/tornado1979/up48rxLs/
you may have better luck ,than me, on your browser , but it would definitelly work on your live app.
Anyway,
i hope helps ,good luck.
I know this has already been answered, but I redid the pen created in this answer using the new version of fabricjs (4.5.0)
Pen: https://codepen.io/201flaviosilva/pen/GROLbQa
In this case, I used the middle button in the mouse to pan :)
// Enable mouse middle button
canvas.fireMiddleClick = true;
// Mouse Up Event
if (e.button === 2) currentState = STATE_IDLE;
// Mouse Down Event
if (e.button === 2) currentState = STATE_PANNING;
:)

synchronising two divs scroll is not smooth in iOS

--> Please goto Edit part of this Question
I want to synchronise scroll bar of two divs and this is how I am doing it
var div1 = document.getElementById('element1'),
div2 = document.getElementById('element2');
div1.addEventListener('touchmove', scrolled, false);
div2.addEventListener('touchmove', scrolled, false);
function getscrollTop(node) {
return node.pageYOffset || node.scrollTop;
}
function scrolled() {
var node = this, scrollTop = getscrollTop(node);
var percentage = scrollTop / (node.scrollHeight - node.clientHeight);
var other = document.getElementById({
"element1": "element2",
"element2": "element1"
}[node.id]);
other.scrollTop = percentage * (other.scrollHeight - other.clientHeight);
};
Fiddle -> used scroll instead touchmove
But the problem is it is flickering in low end devices and would like to make it smooth in event low end devices.
Edit
I have used below code to smoothen the scrolling
var children = document.querySelectorAll('.scrolldiv');
var getscrollTop = function(node) {
return node.pageYOffset || node.scrollTop;
}, toInt = function(n) {
return Math.round(Number(n));
};
window.setInterval(function() {
var scrollTop = getscrollTop(children[0]);
var percentage = scrollTop / (children[0].scrollHeight - children[0].clientHeight);
var oscrollTop = percentage * (children[1].scrollHeight - children[1].clientHeight);
// console.log(1);
children[1].scrollTop = toInt(oscrollTop);
}, 2);
It is smoother in Desktop browsers but in iOS browser, when setting second DIv's scroll it is jerking, jerking in the sense setting scrollTop once scrolling is completed, not while scrolling.
If you round your scroll value numbers to integers then this problem goes away :
http://jsfiddle.net/2Cj4S/15/
I just used a rounding function :
function toInt(n){ return Math.round(Number(n)); };
and this seems to have fixed it. Double values really confused GUI widgets like scrollbars, and 2D drawing.
I don't see why you have to calculate a new percentage here, value which you hand over to the second scroll.. that's probably the reason for the jerking.. instead you could simply take the scroll value from the first scroll and assign it directly to the other scroll.. This will remove the jerky-ness in the other scroll.. and synchronising them..
I just added the following line to the bottom of your scrolled function..
other.scrollTop = getscrollTop(node);
The modified function:-
function scrolled() {
var node = this,
scrollTop = getscrollTop(node);
var id = node.id;
var percentage = getscrollTop(node) / (node.scrollHeight - node.clientHeight);
var other = document.getElementById({
"element1": "element2",
"element2": "element1"
}[id]);
var oscrollTop = percentage * (other.scrollHeight - other.clientHeight)
//other.scrollTop = oscrollTop;
//Please note that I have commented out the above line.. and added the following line
other.scrollTop = getscrollTop(node);
};
I hope this the behaviour you were hoping for, i tested it out on jsfiddle, both scrolls are well synchronised.

How to Smoothly Prevent a Fixed Element from Scrolling Past A Specified Element

Essentially what I want to do is keep my blog posts' meta information on the screen at all times. As it is, the meta info (title, author, etc.) is displayed to the left of the post content, and I have it set up where the meta information stays on screen smoothly when I scroll down. However, I'm having an issue:
I can't get it to smoothly not scroll over the #comments DIV. It either overlaps or is jumpy, depending on how I tweak the code.
Here is the JS function I'm using:
function brazenlyScroll() {
var element = jQuery(".single-post .headline_area");
var top = element.offset().top - 50;
var elementHeight = 26 + element.height();
var maxTop = jQuery("#comments").offset().top - elementHeight;
var scrollHandler = function() {
if (jQuery(document).width() > 1035) {
var scrollTop = jQuery(window).scrollTop();
if (scrollTop<top) {
element.css({position:"relative",top:""})
} else if (scrollTop>maxTop) {
element.css({position:"absolute",top:(maxTop+"px")})
} else {
element.css({position:"fixed",top:"50px"})
}
}
}
jQuery(window).scroll(scrollHandler);
jQuery(window).resize(scrollHandler);
scrollHandler();
}
That code is included via an external JS file and is called at the bottom of the page. You can see all of this in action here: http://www.rickbeckman.org/dumber-and-dumber-and-dumber/
Any help would be greatly appreciated.
You can make the comments div shrink to right by giving it a 300px padding when meta block reaches maxTop.
I just tested ur code and was able to fix the overlapping by changing 26 to a bigger number, say about 60.
var elementHeight = 26 + element.height();
Hope this helps.

Horizontal swipe gesture and vertical page scroll

I am building a mobile site and I have a slide show of images that allows sliding through images horizontally. The javascript library I'm using is bxslider. However, if one touches the slide show and wants to scroll the page up/down, the slide show blocks vertical scrolling and hence another section of the site must be touched.
Could someone please tell me how I could keep vertical scroll enabled (i.e, not allow the slideshow to block the normal scroll?)
Thanks!
Try this, Change the onTouchMove fn in the bxslider library to this
var onTouchMove = function (e) {
if (slider.settings.mode != 'fade') {
var orig = e.originalEvent;
var value = 0;
// if horizontal, drag along x axis
if (slider.settings.mode == 'horizontal')
{
var hchange = orig.changedTouches[0].pageX - slider.touch.start.x;
var vchange = orig.changedTouches[0].pageY - slider.touch.start.y;
if(Math.abs(hchange)>20 && Math.abs(hchange)>Math.abs(vchange))
{
value = slider.touch.originalPos.left + hchange;
setPositionProperty(value, 'reset', 0);
e.preventDefault();
}
// if vertical, drag along y axis
} else{
e.preventDefault();
var change = orig.changedTouches[0].pageY - slider.touch.start.y;
value = slider.touch.originalPos.top + change;
setPositionProperty(value, 'reset', 0);
}
}
}
If you goto the options page for the bxslider website, search for preventDefaultSwipeX, and preventDefaultSwipeY
Those are what you are looking for.

Categories