<div class="sub-navigation-tiles">
<div class="sub-navigation-tiles__section">
<a class="navigation-tile efl-branding" href="/" title="Courses">
<div class="navigation-tile__bg-image" style="background-image: url(/assets/example-content/visit-england-store.jpg);"></div>
<div class="navigation-tile__title">Courses</div>
</a>
<a class="navigation-tile efl-branding" href="/" title="Sessions">
<div class="navigation-tile__bg-image" style="background-image: url(/assets/example-content/visit-england-store.jpg);"></div>
<div class="navigation-tile__title">Sessions</div>
</a>
<a class="navigation-tile efl-branding" href="/" title="Articles">
<div class="navigation-tile__bg-image" style="background-image: url(/assets/example-content/visit-england-store.jpg);"></div>
<div class="navigation-tile__title">Articles</div>
</a>
<a class="navigation-tile efl-branding" href="/" title="LMS">
<div class="navigation-tile__bg-image" style="background-image: url(/assets/example-content/visit-england-store.jpg);"></div>
<div class="navigation-tile__title">LMS</div>
</a>
<a class="navigation-tile efl-branding" href="/" title="Community">
<div class="navigation-tile__bg-image" style="background-image: url(/assets/example-content/visit-england-store.jpg);"></div>
<div class="navigation-tile__title">Community</div>
</a>
</div>
</div>
No notes defined.
{
"sub-navigation-title": false,
"tiles": [
{
"title": "Courses",
"link": "/",
"alttext": "Courses"
},
{
"title": "Sessions",
"link": "/",
"alttext": "Sessions"
},
{
"title": "Articles",
"link": "/",
"alttext": "Articles"
},
{
"title": "LMS",
"link": "/",
"alttext": "LMS"
},
{
"title": "Community",
"link": "/",
"alttext": "Community"
}
]
}
/** SCROLLING PARALLAX FUNCTIONALITY
* Creates a horizontal parallax effect on image child elements
* Takes the following options:
* scrollContainer: the parent element that scrolls
* items: the items that should should have a parallaxed image
* minXPosition / maxX position: The distance which the image should be able to travel
*
* * */
export default class HorizontalScrollParallax {
constructor({
scrollContainer,
itemElements,
maxXPosition = 20,
minXPosition = -20,
}) {
this.maxXPosition = maxXPosition;
this.minXPosition = minXPosition;
this.scrollContainer = scrollContainer;
this.items = Array.from(itemElements).map(item => {
const img = item.querySelector('img');
// Adding a hack to stop jumpiness from debouncing the animation
// TODO: This could be replaced with something like VirtualScroll
// https://github.com/ayamflow/virtual-scroll
// TODO: This check for `img` is a stopgap to allow the JS to continue running if
// an image is missing. But, this may require more thought about what to do with the
// parallax when there are images missing.
if (img) {
img.style.transition = `transformx 0.1s`;
}
const { clientWidth, offsetLeft } = item;
const bounds = item.getBoundingClientRect();
// The positions at which the item should stop animating
// We are going to calculate an x position based on how far it is between these walls
const leftWall = bounds.left - window.innerWidth;
const rightWall = bounds.right;
return { bounds, clientWidth, offsetLeft, img, leftWall, rightWall };
});
this.parallaxRunning = false;
}
parallax() {
const lastScrollPosition = this.scrollContainer.scrollLeft;
window.requestAnimationFrame(() => {
const itemsInView = this.items.filter(
item =>
lastScrollPosition + window.innerWidth >= item.bounds.left &&
lastScrollPosition < item.bounds.right
);
itemsInView.forEach(({ leftWall, rightWall, img }) => {
// Calculate how far the item is along the screen
const percentage =
((lastScrollPosition - leftWall) * 100) / (rightWall - leftWall);
// The distance which the image should be able to travel
const x =
(percentage / 100) * (this.minXPosition - this.maxXPosition) +
this.maxXPosition;
if (img) {
// eslint-disable-next-line no-param-reassign
img.style.transform = `translateX(${x}px) `;
}
});
});
}
initParallax() {
if (this.parallaxRunning) {
return;
}
this.parallaxRunning = true;
this.parallax();
this.scrollContainer.addEventListener('scroll', () => this.parallax());
}
destroyParallaxScrolling() {
if (!this.parallaxRunning) {
return;
}
this.parallaxRunning = false;
this.scrollContainer.removeEventListener('scroll', () => this.parallax());
Array.from(this.items).forEach(item => {
// eslint-disable-next-line no-param-reassign
item.img.style.transform = `translateX(0)`;
});
}
}
import debounce from 'lodash.debounce';
import HorizontalScrollParallax from './scrolling-parallax';
export default ({ parentElement, maxScreenWidth = 820 }) => {
const tiles = parentElement.querySelectorAll('.navigation-tile');
// Set up the parallex
const tilesList = new HorizontalScrollParallax({
scrollContainer: parentElement,
itemElements: tiles,
});
if (parentElement.clientWidth < maxScreenWidth) {
tilesList.initParallax();
}
// Create the parallax when smaller than the max screensize
// Destroy any running parallax when the screen is bigger
window.addEventListener(
'resize',
debounce(() => {
if (parentElement.clientWidth >= maxScreenWidth) {
tilesList.destroyParallaxScrolling();
return;
}
tilesList.initParallax();
}, 250)
);
};
.sub-navigation-tiles {
overflow-x: auto;
max-width: 100vw;
::-webkit-scrollbar {
width: 1px;
}
&__section {
column-gap: 2rem;
display: flex;
overflow-x: scroll;
padding-bottom: 0.5rem;
padding-left: 1.8rem;
.navigation-tile {
flex-shrink: 0;
&:focus {
outline: 3px solid $blue-accent3;
}
}
}
@media screen and (min-width: $mq-medium) {
overflow-x: unset;
&__section {
display: flex;
column-gap: 3.6rem;
justify-content: center;
overflow-x: unset;
padding-left: 0;
.navigation-tile {
flex-shrink: initial;
}
}
}
}
<div class="sub-navigation-tiles">
{{#if sub-navigation-title}}
{{render '@optional-title'}}
{{/if}}
<div class="sub-navigation-tiles__section">
{{#each tiles}}
{{#if eflBranding}}
{{render '@navigation-tiles--efl' this merge="true"}}
{{else}}
{{render '@navigation-tiles--efl' this merge="true"}}
{{/if}}
{{/each}}
</div>
</div>