I am trying to have it so a my div element, set to "overflow:auto" will scroll when the user is dragging an element.
Dragging the element works, and so does retrieving the proper mouse data (such as the x/y position upon initial drag (onDragStart) and also the current x/y position when the mouse is moving.
I realize my logic for the scrolling probably is off, but I am more just trying to get the div element to scroll at all. Any insight as to what is wrong would be greatly appreciated...Material Design is also being used.
Note: I am using ng-metadata which ports Angular 1 to look like Angular 2, really any guidance in either Angular 1 or Angular 2 would be helpful.
#autobind
onDragStart({clientX, clientY}) {
this.initY = clientY;
if (this.isDragging) return;
document.addEventListener('mousemove', this.dragListener = (e) => {
e.preventDefault();
if (Math.max(Math.abs(e.clientX - clientX), Math.abs(e.clientY - clientY)) > 3) {
document.removeEventListener('mousemove', this.dragListener);
document.addEventListener('mousemove', this.onDrag);
this.dragListener = this.onDrag;
this.fileService.dragSource = this.file;
// onDrag needs to be called before Angular can set the proper classes
this._element.toggleClass('dragging', this.isDragging);
this._element.toggleClass('bulk-dragging', this.inBulkDragOp);
this.onDragScroll(e);
this.onDrag(e);
this.drag.emit();
this._scope.$applyAsync();
}
});
}
#autobind
onDrag(e) {
console.log("Dragging...");
console.log(e);
var currentY = e.clientY;
var range = 100; // Range of 100px before scrolling begins... May need tweaking if too responsive
if (currentY > this.initY + range) {
console.log("SCROLL DOWN");
window.scrollTo(0, 500);
} else if (currentY < this.initY - range) {
console.log("SCROLL UP");
}
e.preventDefault();
this._element.css('transform', `translate(${e.clientX - this._element[0].clientWidth / 2}px, ${e.clientY - this._element[0].clientHeight / 2}px)`);
}
I had my container tag set to a height of 100% which seemed to break the functionality of the scroll. Removing it fixed the problem.
Related
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);
Sorry for bad english.
Here's link to site.
My task is to create site with no scroll. When user clicks to right part of screen car starts to move forward. When it reaches middle of screen - car stops and fixed content-block starts to move in opossite direction. if user moves cursor to left side of screen (while holding mouse button clicked) car should move backward.
Desktop version works as expected. But mobile version is slow. (it's not exactly slow, it's not as smooth as desktop i guess)
What can i do fix this problem?
On touchstart event i get event.pageX value to check what part of screen user touched. (so i would know what direction car should move) and store this value in variable "mousePos". Then i call setInterval with movement function
On touchend event i clear interval to stop car from moving.
On touchmove i will rewrite "mousePos" var with new event.pageX value. For example: user clicked, car starts to move, if user moved cursor to left i will use this var to check direction and turn car back.
In mouseMove function i will check car position and decide what action should be done - either move car or move background and i'll check if it reached start of end points
events:
$(document).on('mousedown touchstart', '.mouse-click', function(event){
clicking = true;
mousePos = event.pageX;
if ( event.target.className == 'helper' ) {
showModal();
} else {
moveStuff = setInterval(mouseMove, 1);
}
});
$(document).on('mouseup touchend', function(){
clicking = false;
carLights.removeClass('blink');
clearInterval(moveStuff);
})
$(document).on('mousemove touchmove', '.mouse-click', function(event){
if(clicking == false) {
return;
} else {
mousePos = event.pageX;
}
});
function:
function mouseMove() {
let offset = parseInt( mainContent.css('left') );
let offsetCar = parseInt( car.css('left') );
if ( mousePos > middleScreen ) {
carLights.removeClass('blink');
if ( offset < - ( contentWidth ) ) {
return;
} else {
rotateWheelsForward();
if ( offsetCar < middleScreen ) {
car.css('left', (offsetCar + mouseSpeed) + 'px');
} else {
mainContent.css('left', (offset - mouseSpeed) + 'px');
}
}
} else if ( mousePos < middleScreen ) {
carLights.addClass('blink');
if ( offset > 0 ) {
carLights.removeClass('blink');
return;
} else {
rotateWheelsBackward();
if ( offsetCar > minCarLeft ) {
car.css('left', (offsetCar - mouseSpeed) + 'px');
} else {
mainContent.css('left', (offset + mouseSpeed) + 'px');
}
}
}
}
So how can i make movement smoother in mobile? (i use iphone 5s safari, but tested in iphone 6, still works bad)
What changes should i implement to this code?
I suggest to use transform rather than position eg: left, top cause thats really affect layer reflow-repaint. Use requestAnimationFrame (wisely) to perform smooth event animation like scroll, mouseup, or any other event.
Then use will-change: transform; to element which will "transformed" on the future. This will creates new layer and prepare for the element changes later.
In my case, relative position impact reflow or green flash on the rendering tool chrome. So I prefer use fixed/absolute position to prevent this.
Here's some great article for you to get Leaner, Meaner, Faster Animations with requestAnimationFrame and how to achieving 60 fps animations with css
Hope this help ;)
I am working with HTML 5 Drag and Drop API, and implementing sortable list and auto scroll. For some of features, I would like to able to detect which part of element was grabbed
Here is the illustration
Any help would be appreciated, thanks
You can use the computed padding+width of the element and the offsetX property of the MouseEvent to calculate the selected region.
yourElement.addEventListener('mousedown', function onDragStart(event){
let width = parseInt(window.getComputedStyle(yourElement).getPropertyValue('width'));
let padding = parseInt(window.getComputedStyle(yourElement).getPropertyValue('padding-left'));
let position = event.offsetX;
let middle = (width / 2) + padding;
if (position <= middle) {
console.log('left');
} else {
console.log('right');
}
});
See this jsfiddle for an example: https://jsfiddle.net/c23Lu6gy/28/
I wrote this code to add swipe function for an image slider. The slider is working correctly.
However when i perform a right or left swipe there is some vertical scrolling which is distracting and annoying.
I'm storing the reference to touchstart in the touch object.
And on touchend event, if vertical distance (lenY) is more than 50, i trigger preventDefault on the touchstart.
This isn't working.
Simplest option is to call preventDefault directly on touchStart. But the image slider occupies a large part of the mobile screen making scrolling down the page tricky.
I need to pass the lenY (vertical distance) to the touch start handler to prevent default action.
function triggerTouch() {
"use strict";
var tZone = document.getElementById('sl-m'),
touch = {},
startX = 0,
startY = 0,
endX = 0,
endY = 0;
if (tZone) {
tZone.addEventListener('touchstart', function (e) {
startX = e.changedTouches[0].screenX;
startY = e.changedTouches[0].screenY;
// store reference to touch event
touch.start = e;
}, false);
tZone.addEventListener('touchend', function (e) {
endX = e.changedTouches[0].screenX;
endY = e.changedTouches[0].screenY;
var lenX = Math.abs(endX - startX);
var lenY = Math.abs(endY - startY);
// check if user intended to scroll down
if (lenY < 50 && lenX > 50) {
touch.start.preventDefault();
e.preventDefault();
swipe(tZone, startX, endX);
}
}, false);
}
}
Since i haven't got an answer i am posting my own answer, hoping someone can provide the correct implementation.
I ended up using the css overflow property to temporarily disable vertical scroll.
This works perfectly though there is a small side effect. Once you swipe through the image slider, the scroll is disabled.
A swipe upwards is required to restore scroll to the page. Its not noticeable but i still want to figure the right way.
var touch = {};
window.onload = function () {
"use strict";
document.body.addEventListener("touchstart", touchHandler);
document.body.addEventListener("touchend", touchHandler);
};
function touchHandler(e) {
"use strict";
var el = e.target;
if (el.parentNode.id === "sl-m") {
if (e.type === "touchstart") {
touch.startX = e.changedTouches[0].screenX;
touch.startY = e.changedTouches[0].screenY;
} else {
touch.endX = e.changedTouches[0].screenX;
touch.endY = e.changedTouches[0].screenY;
touch.lenX = Math.abs(touch.endX - touch.startX);
touch.lenY = Math.abs(touch.endY - touch.startY);
if (touch.lenY < 20) {
// disable scroll
document.body.style.overflowY = "hidden";
// do swipe related stuff
swipe(el.parentNode);
} else {
// enable scroll if swipe was not intended
document.body.style.overflowY = "scroll";
}
}
} else {
// keep scroll enabled if touch is outside the image slider
document.body.style.overflowY = "scroll";
}
}
I want to share the solution that works for me. The above solution did not work on ios. I am sorry for my English. I do not know english.
function stop(e){
e=e || event;
e.preventDefault;
}
window.onscroll=stop(); //-->Yes, we will use it ..
For example, where you will use;
function move(event){
var finish=event.touches[0].clientX;
var verticalFinish=event.touches[0].clientY;
var diff=finish-strt;
var verticalDiff=verticalStrt-verticalFinish;
var f;
if(diff<0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=verticalDiff+widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[y].style.left=f+"px";
slayt[y].style.transition="none";
window.onscroll=stop(); //-->we used it here :)
}
else if(diff>0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=diff-widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[z].style.left=f+"px";
slayt[z].style.transition="none";
window.onscroll=stop();//-->we used it here :)
}
}
but there is a small problem. cancels if there is another function related to scrolling. return true; it does not work. I also write twice if I have a function related to the slider inside and outside the touchend.
function end(event){
//"touchend" related codes...
//bla bla
window.onscroll=function(){m=window.pageYOffset;console.log(m);if(m>=850)
{buton.style.display="block";}else{buton.style.display="none";}}
}
If it is useful, I will be happy...
Update :
I typed wrong. I want to fix. Actually, the scroll event cannot be canceled unfortunately. So the event we canceled above, scroll is not a vertical scroll event. All events.
window.onscroll=stop(); // ==>improper use
stop(); // ==> actually - Correct usage
It just needs to be written so stop().
html,
body {
overflow: hidden;
}
Did you try this?
I've got a home-made slider made from jQuery UI's draggable() function:
$("#petrolGauge .fuelBar .slider").draggable({
containment: "parent",
axis: "x",
drag:function(){
updValues();
},
start:function(){
$(this).css("background-color","#666");
},
stop:function(){
//checkForm();
$(this).css("background-color","#AAA");
}
});
This is for the following markup:
<div id="petrolGauge">
<input id="endPet" name="endPet" type="hidden" value="0">
How much fuel was left in the tank when you were finished? (Use the slider) <b>(~<span class="petLeft">0</span>%)</b>
<span class="mandatory">*</span><br />
<div class="fuelBar">
<div title="Drag" class="slider"></div>
</div>
This works a treat, when I click on the slider. But I'd like it so that when I click the fuel bar (the slider's parent) the slider not only starts dragging but also jumps to the cursor. I've achieved it by doing this:
$("#petrolGauge .fuelBar").on("mousedown",function(e){
slider = $("#petrolGauge .fuelBar .slider");
left = e.pageX-($(this).offset().left)-(slider.width()/2);
updValues();
slider.css("left",left).trigger(e);
});
Two problems with this:
Firstly, when clicking on the parent I get a couple of second's delay before the slider starts to drag? I've tried and tested this in Chrome and IE and both do it. Secondly if the cursor is less than half of the slider's width away from the edge of the parent, the slider will move to the outside of the parent. Wouldn't be hard to fix this with a couple of checking, but was wondering if there was another way? I'm suprised that draggable() doesn't have any parameters for this to be honest. I didn't want to use slider() if I could help it but if it's the only way, then it's the only way.
Here's a fiddle to work with.
The reason you get the delay is because you use .trigger() inside the .on() event which creates a big loop. As a result the loop slows down the moving process.
$("#petrolGauge .fuelBar").click(function (e) { // use click instead of mousedown
slider = $("#petrolGauge .fuelBar .slider");
left = e.pageX - ($(this).offset().left) - (slider.width() / 2);
if(left > 570) { left = 570; } else if(left < 0) { left = 0; }
// it looks like a draggable bug due to the manual position change, so use a small check
slider.css("left", left); // change the position first
updValues(); // then calculate and update the div
// no need to trigger the event a second time because it will loop until jQuery exceeds it's trigger limit.
});
Here's an updated FIDDLE
Updated answer
To make .slider move accordingly to the mouse movement when not directly dragged, bind a mousemove event to the mousedown and unbind it when mouseup. Then in .mousemove() you change the position of .slider.
var move = function (e) {
left = e.pageX - ($('#petrolGauge .fuelBar').offset().left) - (slider.width() / 2);
if (left > 570) {
left = 570;
} else if (left < 0) {
left = 0;
}
slider.css("left", left);
updValues();
};
var slider = $("#petrolGauge .fuelBar .slider");
$("#petrolGauge .fuelBar").mousedown(function (e) {
e.preventDefault();
left = e.pageX - ($(this).offset().left) - (slider.width() / 2);
if (left > 570) {
left = 570;
} else if (left < 0) {
left = 0;
}
slider.css("left", left)
$(this).bind('mousemove', move);
updValues();
}).mouseup(function () {
$(this).unbind('mousemove');
});