Reputation: 261
I'm trying to make a feature for my website which allows user to horizontally scroll div content using mousemove and touchmove events (it's similar to Apple AppStore any app Screenshots section). I'm doing this by setting a negative margin-left property for first child. Everything is working except that when user on mobile device touches screen or on desktop moves cursor into parent div, it's content not scrolling from where currently is but it's "jumping". I tried to do this by adding current margin-left value to new pageX value, but it's not working. What I'm doing wrong? Or maybe it's not possible to do this feature this way?
JS/Babel code:
class Collage {
constructor(selector) {
this.selector = $(selector);
this.children = this.selector.children(':first-child');
this.selector.on('mousemove touchmove', this.onMove.bind(this));
}
onMove(event) {
const marginLeft = parseFloat(this.children.css('margin-left'));
let mouseX = event.pageX || -event.touches[0].pageX || -event.changedTouches[0].pageX;
const margin = calculateMargin(mouseX, 1);
this.children.css('margin-left', margin);
function calculateMargin(value, speed) {
let val = -value*speed;
return val <= 0 ? val : 0;
}
}
}
SCSS code:
.collage {
overflow: hidden;
max-height: 300px;
white-space: nowrap;
font-size: 0;
img {
display: inline-block;
&:first-of-type {
margin-left: -20%;
}
}
}
My fiddle
Thanks in advance for your answers.
Upvotes: 0
Views: 3698
Reputation: 261
I handle with this problem. With Matthew Levy's answer I figured out that it can be done more easier. So I put whole content into nested container and my CSS (SCSS) code looks like below:
.collage {
overflow: hidden;
-webkit-overflow-scrolling: touch;
width: 100%;
max-height: 300px;
font-size: 0;
.wrapper {
width: 100%;
height: 100%;
overflow: auto;
padding: 0;
white-space: nowrap;
}
img {
display: inline-block;
&:first-of-type {
// transition prevent jumping when user move cursor into container
transition: margin .8s;
}
}
}
With this code I don't need a touchmove event in JS. So now my JS code looks like below (btw, I optimalized this code in regard to the last one):
class Collage {
constructor(selector) {
this.selector = $(selector);
this.child = this.selector.children();
const offsetHeight = this.child[0].offsetHeight;
const clientHeight = this.child[0].clientHeight;
// hide scrollbar - it can be done with CSS but in various browsers scrollbar may has various size so using JS to deal with this is safier
this.selector.css('margin-top', -(offsetHeight - clientHeight));
this.child.css('margin-top', offsetHeight - clientHeight);
this.child.on('mousemove', this.onMove.bind(this));
this.mouseX = 0;
this.marginLeft = 0;
this.selectorWidth = this.selector.width();
this.selectorLeft = this.selector.position().left;
}
onMove(event) {
// calculate new margin value in percentages
const calculateMargin = () => parseInt((100 * (this.mouseX - this.selectorLeft)) / this.selectorWidth);
this.mouseX = event.pageX;
// limit a new value between 0/100%
this.marginLeft += (Math.min(Math.max(calculateMargin(), 0), 100) - this.marginLeft);
this.child.children(':first-child').css('margin-left', `-${this.marginLeft}%`);
}
}
Working fiddle
Upvotes: 1
Reputation: 96
Can you just get what you want without Javascript by creating a second wrapper element around the images?
<div class="collage">
<div class="wrapper">
<img /> ....
</div>
</div>
Then CSS
.collage {
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
width: 100%;
display: block;
position: relative;
}
.wrapper {
width: 10000px;
/* set width to something large
or set with Javascript to the calculated width of your images */
}
Upvotes: 1