I'm having issue with creating loop inside carousel so it will go back to first card after reaching last one on a click event - rightButton.
So far carousel stops when reach last card.
const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card)
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
const cardCount = carousel.querySelectorAll("[data-target='card']").length;
let offset = 0;
const maxX = -((cardCount / 3) * carouselWidth +
(cardMarginRight * (cardCount / 3)) -
carouselWidth - cardMarginRight);
leftButton.addEventListener("click", function() {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
})
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
})
.wrapper {
height: 100px;
width: 432px;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.button-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.carousel {
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
position: absolute;
left: 0;
transition: all 1s ease;
}
.card {
background: black;
min-width: 100px;
height: 100px;
margin-right: 1rem;
display: inline-block;
}
span {
color:#ffffff;
}
<div class="wrapper">
<ul class="carousel" data-target="carousel">
<li class="card" data-target="card"><span>1</span></li>
<li class="card" data-target="card"><span>2</span></li>
<li class="card" data-target="card"><span>3</span></li>
<li class="card" data-target="card"><span>4</span></li>
<li class="card" data-target="card"><span>5</span></li>
<li class="card" data-target="card"><span>6</span></li>
<li class="card" data-target="card"><span>7</span></li>
<li class="card" data-target="card"><span>8</span></li>
<li class="card" data-target="card"><span>9</span></li>
</ul>
<div class="button-wrapper">
<button data-action="slideLeft">L</button>
<button data-action="slideRight">R</button>
</div>
</div>
Code available on jsfiddle:
https://jsfiddle.net/2qv6mpb1/
Is there a chance that someone could point me in a proper direction on how to achieve that? I
You need to handle when your offset is equal to the maxX, and reset the offset back to zero.
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
} else {
offset = 0;
carousel.style.transform = `translateX(${offset}px)`;
}
})
This will use fixed widths with a gap of 10px (see CSS) - (to make it responsive you should modify the px used in JS to translate in % steps).
Also, it will work for any number of .Carousel elements on the page.
Also, simplify the HTML markup as per below, which is more consistent with the CSS for a better modular methodology
const Carousel = (EL) => {
const CARDS = EL.querySelector(".Carousel-cards");
const PREV = EL.querySelector(".Carousel-prev");
const NEXT = EL.querySelector(".Carousel-next");
const w = EL.offsetWidth;
const d = CARDS.offsetWidth - w; // widths diff
let x = 0;
const anim = (dir) => {
x += w * dir;
x = Math.min(d, Math.max(0, x));
CARDS.style.transform = `translateX(-${x}px)`;
};
PREV.addEventListener("click", () => anim(-1))
NEXT.addEventListener("click", () => anim(+1))
};
document.querySelectorAll(".Carousel").forEach(Carousel);
.Carousel {
height: 100px;
width: 430px; /* (100px * 4) + (10px * 3gap) */
position: relative;
overflow: hidden;
margin: 0 auto;
}
.Carousel-nav {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.Carousel-cards {
position: absolute;
left: 0;
margin: 0;
padding: 0;
list-style: none;
display: flex;
transition: transform 1s ease;
gap: 10px;
}
.Carousel-cards > * {
background: black;
min-width: 100px;
height: 100px;
}
span {
color: #ffffff;
}
<div class="Carousel">
<ul class="Carousel-cards">
<li><span>1</span></li>
<li><span>2</span></li>
<li><span>3</span></li>
<li><span>4</span></li>
<li><span>5</span></li>
<li><span>6</span></li>
<li><span>7</span></li>
<li><span>8</span></li>
<li><span>9</span></li>
</ul>
<div class="Carousel-nav">
<button class="Carousel-prev">L</button>
<button class="Carousel-next">R</button>
</div>
</div>
There's more to improve, i.e: makes no sense to have buttons if the content does not require animating, or one of the buttons depending if a direction is completed.
Related
I wanted to make a 3d rotating carousel like in the stack snippet, but required is a view from inside like in the image below. How can i achieve this?
window.addEventListener('load', () => {
var carousels = document.querySelectorAll('.carousel');
for (var i = 0; i < carousels.length; i++) {
carousel(carousels[i]);
}
});
function carousel(root) {
var
figure = root.querySelector('figure'),
nav = root.querySelector('nav'),
images = figure.children,
n = images.length,
gap = root.dataset.gap || 0,
bfc = 'bfc' in root.dataset,
theta = 2 * Math.PI / n,
currImage = 0;
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
window.addEventListener('resize', () => {
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
});
setupNavigation();
function setupCarousel(n, s) {
var apothem = s / (2 * Math.tan(Math.PI / n));
figure.style.transformOrigin = `50% 50% ${- apothem}px`;
for (var i = 0; i < n; i++) {
images[i].style.padding = `${gap}px`;
}
for (i = 1; i < n; i++) {
images[i].style.transformOrigin = `50% 50% ${- apothem}px`;
images[i].style.transform = `rotateY(${i * theta}rad)`;
}
if (bfc) {
for (i = 0; i < n; i++) {
images[i].style.backfaceVisibility = 'hidden';
}
}
rotateCarousel(currImage);
}
function setupNavigation() {
nav.addEventListener('click', onClick, true);
function onClick(e) {
e.stopPropagation();
var t = e.target;
if (t.tagName.toUpperCase() != 'BUTTON') {
return;
}
if (t.classList.contains('next')) {
currImage++;
}
else {
currImage--;
}
rotateCarousel(currImage);
}
}
function rotateCarousel(imageIndex) {
figure.style.transform = `rotateY(${imageIndex * -theta}rad)`;
}
}
body {
margin: 0;
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
h1 {
text-align: center;
margin-bottom: 1.5em;
}
h2 {
text-align: center;
color: #555;
margin-bottom: 0;
}
.carousel {
padding: 20px;
perspective: 500px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.carousel>* {
flex: 0 0 auto;
}
.carousel figure {
margin: 0;
width: 40%;
transform-style: preserve-3d;
transition: transform 0.5s;
}
.carousel figure img {
width: 100%;
box-sizing: border-box;
padding: 0 0px;
}
.carousel figure img:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}
.carousel nav {
display: flex;
justify-content: center;
margin: 20px 0 0;
}
.carousel nav button {
flex: 0 0 auto;
margin: 0 5px;
cursor: pointer;
color: #333;
background: none;
border: 1px solid;
letter-spacing: 1px;
padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>
<div class="carousel" data-gap="80">
<figure>
<img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
<img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
<img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
<img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
<img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
<img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
<img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
<img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
</figure>
<nav>
<button class="nav prev">Prev</button>
<button class="nav next">Next</button>
</nav>
</div>
The carousel needs to be rotated and translated in the opposite direction to make it an inner rotation view.
The transform-origin sets the center of the carousel. The first two numbers are x and y, which just center the carousel on the screen. The third number is z which determines whether the carousel is moved towards the screen or away from the screen. The negative sign on apothem is removed to pull it out of the screen so the center becomes closer to the camera and achieves the inner carousel. backface-visibility needs to be always set to hidden because there might be images behind the camera now. Then to fix the rotation direction with the next button, * -theta is changed to positive.
window.addEventListener('load', () => {
var carousels = document.querySelectorAll('.carousel');
for (var i = 0; i < carousels.length; i++) {
carousel(carousels[i]);
}
});
function carousel(root) {
var
figure = root.querySelector('figure'),
nav = root.querySelector('nav'),
images = figure.children,
n = images.length,
gap = root.dataset.gap || 0,
theta = 2 * Math.PI / n,
currImage = 0;
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
window.addEventListener('resize', () => {
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
});
setupNavigation();
function setupCarousel(n, s) {
var apothem = s / (2 * Math.tan(Math.PI / n));
figure.style.transformOrigin = `50% 50% ${apothem}px`;
for (var i = 0; i < n; i++) {
images[i].style.padding = `${gap}px`;
}
for (i = 1; i < n; i++) {
images[i].style.transformOrigin = `50% 50% ${apothem}px`;
images[i].style.transform = `rotateY(${i * theta}rad)`;
}
rotateCarousel(currImage);
}
function setupNavigation() {
nav.addEventListener('click', onClick, true);
function onClick(e) {
e.stopPropagation();
var t = e.target;
if (t.tagName.toUpperCase() != 'BUTTON') {
return;
}
if (t.classList.contains('next')) {
currImage++;
}
else {
currImage--;
}
rotateCarousel(currImage);
}
}
function rotateCarousel(imageIndex) {
figure.style.transform = `rotateY(${imageIndex * theta}rad)`;
}
}
body {
margin: 0;
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
h1 {
text-align: center;
margin-bottom: 1.5em;
}
h2 {
text-align: center;
color: #555;
margin-bottom: 0;
}
.carousel {
padding: 20px;
perspective: 500px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.carousel>* {
flex: 0 0 auto;
}
.carousel figure {
margin: 0;
width: 40%;
transform-style: preserve-3d;
transition: transform 0.5s;
}
.carousel figure img {
width: 100%;
box-sizing: border-box;
padding: 0 0px;
backface-visibility: hidden;
}
.carousel figure img:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}
.carousel nav {
display: flex;
justify-content: center;
margin: 20px 0 0;
}
.carousel nav button {
flex: 0 0 auto;
margin: 0 5px;
cursor: pointer;
color: #333;
background: none;
border: 1px solid;
letter-spacing: 1px;
padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>
<div class="carousel" data-gap="80">
<figure>
<img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
<img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
<img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
<img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
<img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
<img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
<img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
<img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
</figure>
<nav>
<button class="nav prev">Prev</button>
<button class="nav next">Next</button>
</nav>
</div>
As I'm trying to make a carousel that rotates I wonder how to move the cards rotated and it scales from a minimum of 0.8(left and right cards) and a maximum scale of 1(center card) when the user keeps swiping
Scale:
left = 0.8
center = 1
right = 0.8
I'm trying to solve on how to rotate them using transform and z-index properties. The cards will also rotate however I'm still trying to make a formula on how to make a function that makes the cards rotate
Any alternative solutions are accepted The animation is
similar to this carousel from codepen however it doesn't swipe Carousel Rotate
const CONTAINER_FLEX = document.querySelector('.container-flex');
const items = document.querySelectorAll('.item');
let touchStartX = 0;
let touchMoveX = 0;
let count = 0;
let current_translate = 0;
let previous_translate = 0;
CONTAINER_FLEX.addEventListener('touchstart', (event) => {
touchStartX = event.touches[0].pageX;
});
CONTAINER_FLEX.addEventListener('touchmove', (event) => {
touchMoveX = event.touches[0].pageX;
current_translate = previous_translate + (touchMoveX - touchStartX);
console.log(current_translate);
items[1].style.transform = `translateX(${current_translate}px)`;
});
CONTAINER_FLEX.addEventListener('touchend', () => {
current_translate = touchMoveX - touchStartX;
previous_translate = current_translate;
});
*,
::before,
::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.5;
background-color: #131b24;
}
.main-container {
padding: 30px 0;
height: 300px;
width: 900px;
border-top: 1px solid #444;
border-bottom: 1px solid #444;
overflow: hidden;
background-color: white;
}
.container-flex {
height: 100%;
display: flex;
transition: transform 400ms cubic-bezier(0.165, 0.84, 0.44, 1);
}
.item {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
min-width: 300px;
max-width: 300px;
}
.item h1 {
font-size: 40px;
color: white;
}
/* ITEMS */
.item-1 {
background-color: #2c3e50;
transform: translateX(100px);
z-index: 1;
}
.item-2 {
background-color: #3498db;
z-index: 2;
}
.item-3 {
background-color: #1abc9c;
transform: translateX(-100px);
z-index: 1;
}
<div class="main-container">
<div class="container-flex" id="container-flex">
<div class="item item-1">
<h1>1</h1>
</div>
<div class="item item-2">
<h1>2</h1>
</div>
<div class="item item-3">
<h1>3</h1>
</div>
</div>
</div>
Here is a working example https://jsfiddle.net/4ue5sgm9/3/
I wonder how to move the cards when it goes from 0 to 200 however
Have all your cards in an array.
var cards = [ thing1, thing2, thing3];
Use % - modulus operator, it's the secret for cycling back to the beginning
cardIndex = (cardIndex + 1) % cards.length
Copied from the chat. I made it verbose for clarity
scrollLeft() {
nextIndex = items.indexOf(displayed[2])
nextIndex = ++nextIndex % items.length-1
displayed = items[nextIndex]
nextIndex = ++nextIndex % items.length-1;
displayed.push( items[nextIndex] );
nextIndex = ++nextIndex % items.length-1;
displayed.push( items[nextIndex] )
}
P.S. Write and iterator for items array. An iterator stops after the last item. An iterator is why "for (x in thisArray)" works. BUT write the next() function to return "% this.length-1" instead => now you have a never-ending looper. All that code just above goes away.
I am trying to make connect4 HTML game and I know I will be better off using canvas elements instead of a grids of divs but is it possible to make transition translate type of css animation when moving HTML elements around like this (using appendChild)
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if(rowNum==1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
})
#main {
left:100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>
Click on the red dot to toggle position of ball
You can use animationend to check when the animation end and move the ball element between the divs
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener('animationend', () => {
ball.classList.remove("animate-ball");
ball.style.animation = "";
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
});
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
ball.style.animation = "MoveDown 1s linear";
} else {
ball.style.animation = "MoveUp 1s linear";
}
ball.classList.add("animate-ball");
})
#main {
left: 100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
position: relative;
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
.animate-ball {
animation-iteration-count: 1;
position: absolute;
bottom: 0;
}
#keyframes MoveUp {
0% {
top: -50px;
}
100% {
top: -100px;
}
}
#keyframes MoveDown {
0% {
top: 0;
}
100% {
top: 50px;
}
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>
I have a page where I was using JS - specifically window.pageYOffset - and HTML data to change the inner HTML of the h1 footer, use l1 links to scroll the page to each section, and to add classes to each li when I reached the top of each section.work-page.
However, after I implemented CSS scroll points and added the div.container over the scrollable sections my javascript stopped working. Specifically when I set the overflow-y: scroll.
Basically when I made the div.container overflow-y: scroll; the doWork function stopped working and I can't figure out why.
^^^^ div.container in CSS
const doWork = function () {
const p01Tag = document.getElementById("p01")
const p02Tag = document.getElementById("p02")
const p03Tag = document.getElementById("p03")
const p04Tag = document.getElementById("p04")
const container = document.querySelector("div.container")
const sections = document.querySelectorAll("section.work-page")
const clientTag = document.querySelector("h2.about")
document.addEventListener("scroll", function () {
const pixels = window.pageYOffset
console.log(pixels)
sections.forEach(section => {
if(section.offsetTop - 400 <= pixels) {
clientTag.innerHTML = section.getAttribute("data-client")
if (section.hasAttribute("data-seen-1")) {
p01Tag.classList.add("move")
} else {
p01Tag.classList.remove("move")
}
if (section.hasAttribute("data-seen-2")) {
p02Tag.classList.add("move")
} else {
p02Tag.classList.remove("move")
}
if (section.hasAttribute("data-seen-3")) {
p03Tag.classList.add("move")
} else {
p03Tag.classList.remove("move")
}
if (section.hasAttribute("data-seen-4")) {
p04Tag.classList.add("move")
} else {
p04Tag.classList.remove("move")
}
}
})
})
// scrolling between projects ============================
function smoothScroll(target, duration) {
const targetTag = document.querySelector(target);
let targetPosition = targetTag.getBoundingClientRect().top;
const startPosition = window.pageYOffset;
let startTime = null;
function animation(currentTime) {
if(startTime === null ) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const run = ease(timeElapsed, startPosition, targetPosition, duration);
window.scrollTo(0,run);
if (timeElapsed < duration) requestAnimationFrame(animation)
}
function ease(t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
}
requestAnimationFrame(animation)
}
p01Tag.addEventListener("click", function() {
smoothScroll('section.fn-up', 800)
})
p02Tag.addEventListener("click", function() {
smoothScroll('section.cameron', 800)
})
p03Tag.addEventListener("click", function() {
smoothScroll('section.truax', 800)
})
p04Tag.addEventListener("click", function() {
smoothScroll('section.romero', 800)
})
}
doWork()
const doInfo = function () {
const toggleTag = document.querySelector("a.contact")
const sectionTag = document.querySelector("section.info-page")
toggleTag.addEventListener("click", function () {
sectionTag.classList.toggle("open")
if (sectionTag.classList.contains("open")) {
toggleTag.innerHTML = "Close"
} else {
toggleTag.innerHTML = "Info"
}
})
}
doInfo()
html {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*, *:before, *:after {
-webkit-box-sizing: inherit;
-moz-box-sizing: inherit;
box-sizing: inherit;
}
body {
font-family: 'IBM Plex Mono', monospace;
font-size: 14px;
background-color: #050505;
color: #ffffff;
line-height: 1.1;
}
header {
width: 100%;
z-index: 3;
position: fixed;
top: 0;
left: 0;
padding-top: 40px;
padding-left: 40px;
padding-right: 40px;
}
.contact {
float: right;
}
ul {
font-family: 'IBM Plex Mono', Arial;
font-size: 14px;
}
p {
margin-bottom: 50px;
}
/* Info page -------------------- */
section.info-page {
z-index: 2;
position: fixed;
top: -100vh;
left: 0;
display: flex;
margin-top: 100px;
margin-left: 40px;
margin-right: 40px;
width: 100vw;
min-height: 100vh;
max-width: 100vw;
transition: top 0.5s;
}
section.info-page.open {
top: 0;
}
/* Work page ------------------------*/
div.container {
top: 0;
left: 0;
max-width: 100vw;
max-height: 100vh;
/* WHEN WE ADD THIS OVERFLOW SETTING IN ORDER TO GET THE CSS SCROLL SNAP POINTS TO WORK IT BREAKS THE JAVASCRIPT */
/* overflow-y: scroll; */
scroll-snap-type: y mandatory;
position: relative;
z-index: 1;
}
div.work-info {
width: 13vw;
top: 0;
left: 0;
height: 100vh;
position: fixed;
z-index: 2;
padding-right: 80px;
display: flex;
align-items: center;
margin-left: 40px;
}
div.work-info li {
padding-bottom: 30px;
transition: transform 0.3s;
}
div.work-info li.move {
transform: translateX(15px);
}
footer {
width: 100%;
z-index: 1;
position: fixed;
bottom: 0;
left: 0;
padding-left: 40px;
padding-right: 40px;
padding-bottom: 40px;
color: #979797;
}
section.work-page {
scroll-snap-align: start;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
position: relative;
}
section.work-page img {
max-width: 60vw;
}
<body>
<!-- hidden modal that runs off of the info.js script -->
<section class="info-page">
<h1>
Hello
</h1>
</section>
<header>
<a class="contact" href="#">Info</a>
</header>
<!-- objects that get new classes with javascript on pageYOffset -->
<div class="work-info">
<ul>
<li id="p01" data-number="FN-UP Magazine">01</li>
<li id="p02" data-number="Cameron Tidball-Sciullo">02</li>
<li id="p03" data-number="Jacob Truax">03</li>
<li id="p04" data-number="Alexander Romero">04</li>
</ul>
</div>
<!-- scollable sections using the scroll points and triggering the pageYOffset -->
<div class="container">
<section class="work-page fn-up" data-client="FN-UP Magazine" data-seen-1="yes">
<div class="content">
<img src="lib/fn-up.png">
</div>
</section>
<section class="work-page cameron" data-client="Cameron Tidball-Sciullo" data-seen-2="yes">
<div class="content">
<img src="lib/alex.png">
</div>
</section>
<section class="work-page truax" data-client="Jacob Truax" data-seen-3="yes">
<div class="content">
<img src="lib/old.png">
</div>
</section>
<section class="work-page romero" data-client="Alexander Romero" data-seen-4="yes">
<div class="content">
<img src="lib/alex.png">
</div>
</section>
</div>
<footer class="footer">
<h2 class="about">FN-UP Magazine</h2>
</footer>
</body>
You have added a event listener to the page's Document object.
document.addEventListener("scroll", function () {
Then you calculate the number of pixels the document is currently scrolled along the vertical axis using window.pageYOffset.
const pixels = window.pageYOffset
When you set the CSS attribute overflow-y to scroll in the div.container element, new scrollbars appears on the window. According to MDN:
scroll
Content is clipped if necessary to fit the padding box. Browsers display scrollbars whether or not any content is actually clipped. (This prevents scrollbars from appearing or disappearing when the content changes.) Printers may still print overflowing content.
From that moment on, you are not scrolling the document, you are scrolling div.container. That won't trigger you scroll event.
You need to bound the event to the div element:
const container = document.querySelector("div.container")
container.addEventListener("scroll", function () {
And, instead of calculating how much document has scrolled, get the scrollTop property of the div.container:
const pixels = container.scrollTop
You need to make the same changes in whatever part of the code that involves the above calculations. In smoothScroll():
// const startPosition = window.pageYOffset;
const startPosition = container.scrollTop;
// window.scrollTo(0,run);
container.scrollTo(0,run);
I miss 1% to finish my script, I just don't know how to do it :D
When you hover over the target to the left, you can see the image will scroll. But after clicking on a new image it won't. I then have to resize the window to make it work again. How to fix that? Below is my code but for a working example, here's a CodePen
(function($) {
// virables
var layoutContainer = '.container';
var layoutTarget = '#target';
var layoutTargetIMG = '#target img';
var layoutIMG = '.container .gallery .item img';
var layoutIMGFirst = '.container .gallery .item:first-child img';
// Add first image to target
$(layoutIMGFirst).clone().appendTo(layoutTarget);
// Add image to target when click on gallery image
$(layoutIMG).click(function() {
$(this).closest(layoutContainer).find(layoutTarget).empty();
$(this).clone().appendTo(
$(this).closest(layoutContainer).find(layoutTarget)
);
});
// Image scroll on hover
// This won't work after clicking on an image unless resizing the browser
$(window).resize(function() {
// If i remove this it won't work on the start image.
// Any other solution?
setTimeout(function() {
$('#target img').each(function() {
var itemHeight = $('#target').outerHeight();
var imgHeight = $(this).outerHeight();
// Work out what percentage the image is of the item and remove 100% from that
var topHeight = (imgHeight / itemHeight) * 100 - 100;
//Make the animation speed proptional to that ratio
var animationSpeed = (imgHeight / itemHeight) / 1; //change 2 to tweak the speed
$(this).css({
transition: 'all ease ' + animationSpeed + 's'
});
$(this).mouseleave(function() {
$(this).css({
top: '0'
});
})
// The 'top' property of the image needs
// to be set as as a percentage of the parent
$(this).mouseenter(function(e) {
$(this).css({
top: '-' + topHeight + '%',
});
})
});
}, 200);
});
$(document).ready(function() {
setTimeout(function() { // Add delay after resize so function will load
$(window).triggerHandler('resize');
}, 200);
});
})(jQuery);
.container {
display: flex;
flex-flow: row wrap;
align-items: flex-start;
margin-left: -40px;
max-width: 1000px;
background: lightblue;
padding: 20px;
.column {
flex: 1;
min-width: 30%;
margin-left: 40px;
.target {
height: 400px;
background: pink;
position: relative;
overflow: hidden;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
}
.cta {
display: flex;
a {
background: lightgreen;
width: 50%;
padding: 16px 8px;
;
text-align: center;
justify-content: center;
text-decoration: none;
&:last-child {
background: orange;
}
}
}
.gallery {
display: flex;
flex-flow: row wrap;
margin-left: -4px;
.item {
flex: 1;
margin-left: 4px;
position: relative;
overflow: hidden;
&::before {
content: '';
padding-top: 80%;
display: block;
}
img {
position: absolute;
min-width: 100%;
min-height: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
}
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="column">
<div id="target" class="target"></div>
<div class="cta">
SE DEMO
KØB LAYOUT
</div>
</div>
<div class="column">
<div class="gallery">
<div class="item">
<img src="https://picsum.photos/600/1200" alt="">
</div>
<div class="item">
<img src="https://picsum.photos/500/1600" alt="">
</div>
<div class="item">
<img src="https://picsum.photos/400/2000" alt="">
</div>
</div>
</div>
</div>
Just change this
$(layoutIMG).click(function() {
$(this).closest(layoutContainer).find(layoutTarget).empty();
$(this).clone().appendTo(
$(this).closest(layoutContainer).find(layoutTarget)
);
});
to
$(layoutIMG).click(function() {
$(this).closest(layoutContainer).find(layoutTarget).empty();
$(this).clone().appendTo(
$(this).closest(layoutContainer).find(layoutTarget)
);
$(window).triggerHandler('resize'); // added this line
});