175 lines
6.7 KiB
JavaScript
175 lines
6.7 KiB
JavaScript
/* desfoto.de - progressive enhancement only. */
|
|
(function () {
|
|
'use strict';
|
|
|
|
/* Mobile navigation */
|
|
var toggle = document.querySelector('[data-nav-toggle]');
|
|
var nav = document.getElementById('site-nav');
|
|
if (toggle && nav) {
|
|
toggle.addEventListener('click', function () {
|
|
var open = nav.classList.toggle('is-open');
|
|
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
});
|
|
nav.addEventListener('click', function (event) {
|
|
if (event.target.closest('a') && window.innerWidth < 832) {
|
|
nav.classList.remove('is-open');
|
|
toggle.setAttribute('aria-expanded', 'false');
|
|
}
|
|
});
|
|
}
|
|
|
|
/* Click-to-load YouTube (youtube-nocookie) */
|
|
document.querySelectorAll('[data-video-id]').forEach(function (facade) {
|
|
facade.addEventListener('click', function () {
|
|
var id = facade.getAttribute('data-video-id');
|
|
var iframe = document.createElement('iframe');
|
|
iframe.setAttribute('src', 'https://www.youtube-nocookie.com/embed/' + encodeURIComponent(id) +
|
|
'?autoplay=1&rel=0&modestbranding=1&playsinline=1');
|
|
iframe.setAttribute('title', facade.getAttribute('aria-label') || 'Video');
|
|
iframe.setAttribute('loading', 'eager');
|
|
iframe.setAttribute('allow', 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture');
|
|
iframe.setAttribute('allowfullscreen', '');
|
|
iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
|
|
var shell = facade.parentElement;
|
|
shell.replaceChild(iframe, facade);
|
|
iframe.focus();
|
|
});
|
|
});
|
|
|
|
/* Contact form -> local mail draft, nothing is transmitted by this page */
|
|
var form = document.querySelector('[data-contact-form]');
|
|
if (form) {
|
|
var status = form.querySelector('[data-contact-status]');
|
|
form.addEventListener('submit', function (event) {
|
|
event.preventDefault();
|
|
var data = new FormData(form);
|
|
var name = (data.get('name') || '').toString().trim();
|
|
var email = (data.get('email') || '').toString().trim();
|
|
var topic = (data.get('topic') || '').toString().trim();
|
|
var date = (data.get('date') || '').toString().trim();
|
|
var message = (data.get('message') || '').toString().trim();
|
|
if (!name || !email || !message) {
|
|
if (status) {
|
|
status.hidden = false;
|
|
status.textContent = 'Bitte Name, E-Mail und Nachricht ausfüllen.';
|
|
status.style.color = '#95330F';
|
|
}
|
|
return;
|
|
}
|
|
var subject = 'Anfrage über desfoto.de: ' + topic;
|
|
var body = 'Name: ' + name + '\nE-Mail: ' + email + '\nBereich: ' + topic +
|
|
(date ? '\nWunschtermin: ' + date : '') + '\n\n' + message + '\n';
|
|
window.location.href = 'mailto:info@dennyschulz.de?subject=' +
|
|
encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
|
|
if (status) {
|
|
status.hidden = false;
|
|
status.textContent = 'Dein Mailprogramm sollte sich mit dem fertigen Entwurf öffnen.';
|
|
status.style.color = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
/* Motion layer: reveals, scroll progress, parallax and pointer spotlights.
|
|
Everything degrades to a static page and is disabled for reduced motion. */
|
|
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
/* Anything inside [data-stagger] reveals one item after another. */
|
|
Array.prototype.forEach.call(document.querySelectorAll('[data-stagger]'), function (group) {
|
|
var step = parseFloat(group.getAttribute('data-stagger')) || 0.08;
|
|
Array.prototype.forEach.call(group.children, function (child, index) {
|
|
if (!child.classList.contains('reveal')) {
|
|
child.classList.add('reveal');
|
|
}
|
|
child.style.setProperty('--d', (index * step).toFixed(2) + 's');
|
|
});
|
|
});
|
|
|
|
var items = document.querySelectorAll('.reveal');
|
|
var revealAll = function () {
|
|
Array.prototype.forEach.call(items, function (item) { item.classList.add('is-in'); });
|
|
};
|
|
if (reduce || !('IntersectionObserver' in window)) {
|
|
revealAll();
|
|
} else {
|
|
try {
|
|
var fired = 0;
|
|
var observer = new IntersectionObserver(function (entries) {
|
|
entries.forEach(function (entry) {
|
|
if (entry.isIntersecting) {
|
|
fired = 1;
|
|
entry.target.classList.add('is-in');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.06 });
|
|
Array.prototype.forEach.call(items, function (item) { observer.observe(item); });
|
|
/* Safety net: if the browser never reports a box that is on screen, drop the
|
|
effect entirely instead of leaving content hidden. */
|
|
window.setTimeout(function () {
|
|
if (fired) { return; }
|
|
var onScreen = false;
|
|
Array.prototype.forEach.call(items, function (item) {
|
|
var box = item.getBoundingClientRect();
|
|
if (box.top < window.innerHeight && box.bottom > 0) { onScreen = true; }
|
|
});
|
|
if (onScreen) {
|
|
observer.disconnect();
|
|
revealAll();
|
|
}
|
|
}, 1600);
|
|
} catch (error) {
|
|
revealAll();
|
|
}
|
|
}
|
|
|
|
if (reduce) {
|
|
return;
|
|
}
|
|
|
|
var progress = document.querySelector('[data-progress]');
|
|
var parallax = Array.prototype.slice.call(document.querySelectorAll('[data-parallax]'));
|
|
var ticking = false;
|
|
|
|
function onScroll() {
|
|
var doc = document.documentElement;
|
|
var max = doc.scrollHeight - window.innerHeight;
|
|
var ratio = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0;
|
|
if (progress) {
|
|
progress.style.setProperty('--scroll', ratio.toFixed(4));
|
|
}
|
|
if (parallax.length) {
|
|
var view = window.innerHeight;
|
|
parallax.forEach(function (element) {
|
|
var rect = element.getBoundingClientRect();
|
|
if (rect.bottom < -240 || rect.top > view + 240) {
|
|
return;
|
|
}
|
|
var speed = parseFloat(element.getAttribute('data-parallax')) || 0.12;
|
|
var offset = (rect.top + rect.height / 2 - view / 2) * speed;
|
|
element.style.setProperty('--py', offset.toFixed(1) + 'px');
|
|
});
|
|
}
|
|
ticking = false;
|
|
}
|
|
|
|
function requestScroll() {
|
|
if (!ticking) {
|
|
ticking = true;
|
|
window.requestAnimationFrame(onScroll);
|
|
}
|
|
}
|
|
|
|
window.addEventListener('scroll', requestScroll, { passive: true });
|
|
window.addEventListener('resize', requestScroll);
|
|
onScroll();
|
|
|
|
/* Pointer-following highlight on the media cards. */
|
|
Array.prototype.forEach.call(document.querySelectorAll('.spot'), function (card) {
|
|
card.addEventListener('pointermove', function (event) {
|
|
var rect = card.getBoundingClientRect();
|
|
card.style.setProperty('--mx', ((event.clientX - rect.left) / rect.width * 100).toFixed(1) + '%');
|
|
card.style.setProperty('--my', ((event.clientY - rect.top) / rect.height * 100).toFixed(1) + '%');
|
|
});
|
|
});
|
|
}());
|