xiaowang/assets/js/app.js
2022-09-09 15:19:54 +02:00

369 lines
13 KiB
JavaScript

import Plyr from 'plyr';
import CircleType from 'circletype';
// ----------------------------------------------------------------------------
// DATA
// ----------------------------------------------------------------------------
const root = document.documentElement;
// HOME NAVIGATION DISPLAY //
const navEl = document.querySelector('.home__nav');
const navLinks = document.querySelectorAll('.home__nav-link');
const navImage = document.querySelector('.home__nav-image');
const circleTypes = [];
// VIDEO PLAYERS //
const galleryVideos = document.querySelectorAll('.gallery__video');
let videoPlayers = [];
// HEADER BAR ANIMATION //
const headerBar = document.querySelector('.header-bar');
const headerLogo = document.querySelector('.header-bar__logo');
let scrollOffset = 0;
// NAVIGATION MENU ANIMATION //
const headerNavBtn = document.querySelector('.header-bar__nav-button');
const headerNavMenu = document.querySelector('.header-bar__nav-menu');
const headerNavMenuListEls = document.querySelectorAll('.header-bar__nav-menu li');
const headerNavOverlay = document.querySelector('.header-bar__nav-overlay');
const touchPosition = {
initialX: null,
initialY: null
};
// ----------------------------------------------------------------------------
// UTILS
// ----------------------------------------------------------------------------
// Convert rem to pixels by getting font-size CSS property
function convertRemToPixels(rem) {
let fontSize = parseFloat(window.getComputedStyle(document.body).getPropertyValue('font-size'));
return rem * fontSize;
}
// ----------------------------------------------------------------------------
// LOGIC
// ----------------------------------------------------------------------------
// HOME NAVIGATION DISPLAY //
// Calculate navigation grid inner width
function calculateNavGridInnerWidth() {
if (root && navImage) {
let navGridGapProperty = window.getComputedStyle(root).getPropertyValue('--home-nav-gap');
let navGridGap = parseFloat(navGridGapProperty.slice(0, -3));
return navImage.getBoundingClientRect().width + (convertRemToPixels(navGridGap) * 2);
}
}
// Calculate navigation grid inner diagonal
function calculateNavGridInnerDiagonal() {
return calculateNavGridInnerWidth() * Math.sqrt(2);
}
// Create new instance of CircleType for each navigation link, set radius and direction
function curveNavLinks() {
if (navLinks.length !== 0 && circleTypes) {
for (let i = 0; i < (navLinks.length - 2); i++) {
circleTypes[i] = new CircleType(navLinks[i]);
circleTypes[i].radius(calculateNavGridInnerWidth() / 2);
}
for (let i = (navLinks.length - 2); i < navLinks.length; i++) {
circleTypes[i] = new CircleType(navLinks[i]);
circleTypes[i].dir(-1);
circleTypes[i].radius(calculateNavGridInnerWidth() / 2);
}
}
}
// Set --home-nav-item-translation CSS property
function setNavItemTranslationProperty() {
let navItemTranslation = (calculateNavGridInnerDiagonal() - calculateNavGridInnerWidth()) / 2;
if (root) {
root.style.setProperty('--home-nav-item-translation', navItemTranslation + 'px');
}
}
// Set navigation grid display depending on number of links
function setNavGridDisplay() {
if (navEl && navLinks.length !== 0) {
if (navLinks.length === 2) {
navEl.classList.add('home__nav--2-items');
} else if (navLinks.length === 3) {
navEl.classList.add('home__nav--3-items');
} else if (navLinks.length === 4) {
navEl.classList.add('home__nav--4-items');
}
for (let i = 0; i < navLinks.length; i++) {
navLinks[i].classList.add('home__nav-link--visible');
}
}
}
// Edit navigation links radius and translation on window resize event
function editNavLinksOnResize() {
window.addEventListener('resize', function(e) {
if (navLinks.length !== 0 && circleTypes) {
for (let i = 0; i < navLinks.length; i++) {
circleTypes[i].radius(calculateNavGridInnerWidth() / 2);
}
}
setNavItemTranslationProperty();
});
}
// Add wave up animation to a single letter and remove it on animationend event
function waveNavLetterUp(navLetter) {
navLetter.classList.add('home__nav-letter--wave-up');
navLetter.addEventListener('animationend', function(e) {
navLetter.classList.remove('home__nav-letter--wave-up');
});
}
// Add wave down animation to a single letter and remove it on animationend event
function waveNavLetterDown(navLetter) {
navLetter.classList.add('home__nav-letter--wave-down');
navLetter.addEventListener('animationend', function(e) {
navLetter.classList.remove('home__nav-letter--wave-down');
});
}
// Add wave up animation to every letters of a single link with a slight delay
function waveNavLinkUp(navLink) {
let navLinkLetters = navLink.querySelectorAll('span');
let animationDelay = 0;
for (let i = 0; i < navLinkLetters.length; i++) {
setTimeout(waveNavLetterUp, animationDelay, navLinkLetters[i]);
animationDelay += 50;
}
}
// Add wave down animation to every letters of a single link with a slight delay
function waveNavLinkDown(navLink) {
let navLinkLetters = navLink.querySelectorAll('span');
let animationDelay = 0;
for (let i = 0; i < navLinkLetters.length; i++) {
setTimeout(waveNavLetterDown, animationDelay, navLinkLetters[i]);
animationDelay += 50;
}
}
// Add wave (up or down) animation to every letters of each link on mouseover event
function waveNavLinksOnHover() {
if (navLinks.length !== 0) {
for (let i = 0; i < (navLinks.length - 2); i++) {
navLinks[i].addEventListener('mouseover', function(e) {
waveNavLinkUp(navLinks[i]);
});
}
for (let i = (navLinks.length - 2); i < navLinks.length; i++) {
navLinks[i].addEventListener('mouseover', function(e) {
waveNavLinkDown(navLinks[i]);
});
}
}
}
// VIDEO PLAYERS //
// Set up Plyr video players
function setUpVideoPlayers() {
if (galleryVideos.length !== 0) {
videoPlayers = Plyr.setup(galleryVideos, {
controls: [
'play-large',
'play',
'progress',
'current-time',
'duration',
'mute',
'volume',
'fullscreen'
],
i18n: {
play: 'Lire',
pause: 'Mettre sur pause',
seek: 'Parcourir la piste audio',
currentTime: 'Temps écoulé depuis le début de la piste audio',
duration: 'Durée de la piste audio',
volume: 'Ajuster le volume',
mute: 'Couper le son',
unmute: 'Activer le son',
enterFullscreen: 'Activer le mode plein écran',
exitFullscreen: 'Quitter le mode plein écran'
}
});
}
}
// Toggle videos full screen mode on Plyr enterfullscreen and exitfullscreen media events
function toggleVideosFullScreen() {
if (videoPlayers.length !== 0) {
for (let i = 0; i < videoPlayers.length; i++) {
videoPlayers[i].on('enterfullscreen', function(e) {
galleryVideos[i].classList.add('gallery__video--full-screen');
});
videoPlayers[i].on('exitfullscreen', function(e) {
galleryVideos[i].classList.remove('gallery__video--full-screen');
});
}
}
}
// HEADER BAR ANIMATION //
// Toggle header bar depending on scroll offset
function toggleHeaderBar() {
if (headerBar && headerLogo) {
let headerBarHeight = headerBar.getBoundingClientRect().height;
if (window.pageYOffset > headerBarHeight) { // Scroll down past header bar height
headerBar.classList.add('header-bar--fixed');
headerLogo.classList.add('header-bar__logo--small');
} else if (window.pageYOffset <= 0) {
headerBar.classList.remove('header-bar--fixed');
headerLogo.classList.remove('header-bar__logo--small');
headerBar.classList.remove('header-bar--visible');
}
if (headerBar.classList.contains('header-bar--fixed')) {
if (scrollOffset < window.pageYOffset) { // Scroll down
headerBar.classList.remove('header-bar--visible');
} else if (scrollOffset > window.pageYOffset) { // Scroll up
headerBar.classList.add('header-bar--visible');
}
scrollOffset = window.pageYOffset;
}
}
}
// NAVIGATION MENU ANIMATION //
// Toggle header navigation
function toggleHeaderNav() {
if (headerNavBtn && headerNavMenu) {
headerNavBtn.classList.toggle('header-bar__nav-button--custom-focus');
headerNavMenu.classList.toggle('header-bar__nav-menu--visible');
headerNavOverlay.classList.toggle('header-bar__nav-overlay--visible');
document.body.classList.toggle('body--hidden-overflow-y');
}
}
// Open header navigation
function openHeaderNav() {
if (headerNavBtn && headerNavMenu) {
headerNavBtn.classList.add('header-bar__nav-button--custom-focus');
headerNavMenu.classList.add('header-bar__nav-menu--visible');
headerNavOverlay.classList.add('header-bar__nav-overlay--visible');
document.body.classList.add('body--hidden-overflow-y');
}
}
// Close header navigation
function closeHeaderNav() {
if (headerNavBtn && headerNavMenu) {
headerNavBtn.classList.remove('header-bar__nav-button--custom-focus');
headerNavMenu.classList.remove('header-bar__nav-menu--visible');
headerNavOverlay.classList.remove('header-bar__nav-overlay--visible');
document.body.classList.remove('body--hidden-overflow-y');
}
}
// Toggle header navigation on button click event
function toggleHeaderNavOnClick() {
headerNavBtn.addEventListener('click', toggleHeaderNav);
}
// Close header navigation on document click event
function closeHeaderNavOnClick() {
document.addEventListener('click', function(e) {
if (headerNavBtn.classList.contains('header-bar__nav-button--custom-focus') && headerNavMenu.classList.contains('header-bar__nav-menu--visible') && !headerNavBtn.contains(e.target) && !headerNavMenu.contains(e.target)) {
closeHeaderNav(e);
if (e.cancelable) {
e.preventDefault();
}
}
});
}
// Open header navigation on menu list focusin event
function openHeaderNavBeforeFocus() {
if (headerNavMenuListEls) {
for (let i = 0; i < headerNavMenuListEls.length; i++) {
headerNavMenuListEls[i].addEventListener('focusin', openHeaderNav);
}
}
}
// Close header navigation on menu list focusout event
function closeHeaderNavAfterFocus() {
if (headerNavMenuListEls) {
for (let i = 0; i < headerNavMenuListEls.length; i++) {
headerNavMenuListEls[i].addEventListener('focusout', closeHeaderNav);
}
}
}
// Close header navigation on touchstart and touchmove events (swipe right)
function closeHeaderNavOnSwipe() {
headerNavMenu.addEventListener('touchstart', function(e) {
if (headerNavBtn.classList.contains('header-bar__nav-button--custom-focus') && headerNavMenu.classList.contains('header-bar__nav-menu--visible') && touchPosition.initialX === null && touchPosition.initialY === null) {
touchPosition.initialX = event.touches[0].clientX;
touchPosition.initialY = event.touches[0].clientY;
}
}, {passive: true});
headerNavMenu.addEventListener('touchmove', function(e) {
if (headerNavBtn.classList.contains('header-bar__nav-button--custom-focus') && headerNavMenu.classList.contains('header-bar__nav-menu--visible') && touchPosition.initialX !== null && touchPosition.initialY !== null) {
touchPosition.currentX = event.touches[0].clientX;
touchPosition.currentY = event.touches[0].clientY;
touchPosition.diffX = touchPosition.initialX - touchPosition.currentX;
touchPosition.diffY = touchPosition.initialY - touchPosition.currentY;
if (Math.abs(touchPosition.diffX) > Math.abs(touchPosition.diffY)) {
if (touchPosition.diffX < 0) {
closeHeaderNav(e);
}
}
touchPosition.initialX = null;
touchPosition.initialY = null;
}
}, {passive: true});
}
// ----------------------------------------------------------------------------
// PROGRAM
// ----------------------------------------------------------------------------
// Enable CSS :active pseudo-class in Safari Mobile
document.addEventListener("touchstart", function() {},false);
// HOME NAVIGATION DISPLAY //
window.addEventListener('load', function() {
curveNavLinks();
setNavItemTranslationProperty();
setNavGridDisplay();
editNavLinksOnResize();
waveNavLinksOnHover();
});
// VIDEO PLAYERS //
setUpVideoPlayers();
toggleVideosFullScreen();
// HEADER BAR ANIMATION //
document.addEventListener('scroll', function() {
toggleHeaderBar();
});
// NAVIGATION MENU ANIMATION //
toggleHeaderNavOnClick();
closeHeaderNavOnClick();
openHeaderNavBeforeFocus();
closeHeaderNavAfterFocus();
closeHeaderNavOnSwipe();