Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 98 additions & 4 deletions landing/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@
});
}

function showSlide(i) {
var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
var animating = false;

function mod(i, n) { return ((i % n) + n) % n; }

function setSlide(i) {
var list = activeSlides();
slideIndex = ((i % list.length) + list.length) % list.length;
slideIndex = mod(i, list.length);
var s = list[slideIndex];
slideImg.src = s.src;
slideCaption.textContent = (I18N[lang] || I18N.es)[s.cap] || "";
Expand All @@ -79,8 +84,96 @@
});
}

document.getElementById("slidePrev").addEventListener("click", function () { showSlide(slideIndex - 1); });
document.getElementById("slideNext").addEventListener("click", function () { showSlide(slideIndex + 1); });
// dir: +1 next, -1 previous. Without it the direction comes from the index.
function showSlide(i, dir) {
var list = activeSlides();
var target = mod(i, list.length);
if (dir === undefined) dir = target === slideIndex ? 1 : (target > slideIndex ? 1 : -1);
if (target === slideIndex || animating || reduceMotion.matches) {
setSlide(target);
slideImg.style.transition = "";
slideImg.style.transform = "";
slideImg.style.opacity = "";
return;
}
animating = true;
slideImg.style.transition = "transform 180ms ease, opacity 180ms ease";
slideImg.style.transform = "translateX(" + (-dir * 32) + "px)";
slideImg.style.opacity = "0.35";
window.setTimeout(function () {
setSlide(target);
slideImg.style.transition = "none";
slideImg.style.transform = "translateX(" + (dir * 32) + "px)";
void slideImg.offsetWidth;
slideImg.style.transition = "transform 200ms ease, opacity 200ms ease";
slideImg.style.transform = "translateX(0)";
slideImg.style.opacity = "1";
window.setTimeout(function () {
animating = false;
slideImg.style.transition = "";
slideImg.style.transform = "";
slideImg.style.opacity = "";
}, 220);
}, 180);
}

document.getElementById("slidePrev").addEventListener("click", function () { showSlide(slideIndex - 1, -1); });
document.getElementById("slideNext").addEventListener("click", function () { showSlide(slideIndex + 1, 1); });

// ---------- Swipe del slider (tap y arrastre) ----------
var slideViewport = document.querySelector(".slider-viewport");
var drag = null;
var suppressLightbox = false;

function resetSlideImage() {
slideImg.style.transition = "transform 200ms ease, opacity 200ms ease";
slideImg.style.transform = "translateX(0)";
slideImg.style.opacity = "1";
}

slideViewport.addEventListener("pointerdown", function (e) {
if (e.button !== 0 || animating) return;
drag = { x: e.clientX, y: e.clientY, dx: 0, active: false, id: e.pointerId };
});

slideViewport.addEventListener("pointermove", function (e) {
if (!drag || e.pointerId !== drag.id) return;
var dx = e.clientX - drag.x;
var dy = e.clientY - drag.y;
if (!drag.active) {
if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return;
if (Math.abs(dy) > Math.abs(dx)) { drag = null; return; }
drag.active = true;
slideViewport.setPointerCapture(e.pointerId);
slideImg.style.transition = "none";
}
drag.dx = dx;
slideImg.style.transform = "translateX(" + dx + "px)";
slideImg.style.opacity = String(Math.max(0.55, 1 - Math.abs(dx) / 600));
e.preventDefault();
});

function endDrag() {
if (!drag) return;
var d = drag;
drag = null;
if (!d.active) return;
suppressLightbox = true;
window.setTimeout(function () { suppressLightbox = false; }, 300);
var threshold = Math.min(70, slideViewport.clientWidth * 0.18);
if (Math.abs(d.dx) > threshold) {
var dir = d.dx < 0 ? 1 : -1;
showSlide(slideIndex + dir, dir);
} else {
resetSlideImage();
}
}

slideViewport.addEventListener("pointerup", endDrag);
slideViewport.addEventListener("pointercancel", function () {
if (drag && drag.active) resetSlideImage();
drag = null;
});

document.addEventListener("keydown", function (e) {
if (lightbox.open) return;
Expand All @@ -102,6 +195,7 @@

// ---------- Lightbox ----------
slideImg.addEventListener("click", function () {
if (suppressLightbox) { suppressLightbox = false; return; }
lightboxImg.src = slideImg.src;
lightboxImg.alt = slideImg.alt;
lightbox.showModal();
Expand Down
8 changes: 4 additions & 4 deletions landing/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="styles.css?v=4" />
<link rel="stylesheet" href="styles.css?v=5" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
Expand Down Expand Up @@ -174,12 +174,12 @@ <h2 class="reveal" data-i18n="shots.title">Un vistazo a la interfaz</h2>
<p class="lead reveal" data-i18n="shots.lead">Capturas reales de la app, en los dos idiomas y los dos temas.</p>
<div class="slider reveal" id="slider">
<div class="slider-viewport">
<img id="slideImg" src="assets/shots/main-es-light.webp" alt="Ventana principal de NextSync" data-i18n-alt="shots.alt" width="1800" height="1200" />
<img id="slideImg" src="assets/shots/main-es-light.webp" alt="Ventana principal de NextSync" data-i18n-alt="shots.alt" width="1800" height="1200" draggable="false" />
</div>
<button class="slider-arrow prev" id="slidePrev" aria-label="Captura anterior" data-i18n-aria="shots.prev">&#10094;</button>
<button class="slider-arrow next" id="slideNext" aria-label="Captura siguiente" data-i18n-aria="shots.next">&#10095;</button>
<div class="slider-thumbs" id="slideThumbs" role="tablist"></div>
<p class="slider-caption" id="slideCaption"></p>
<p class="slider-caption" id="slideCaption" aria-live="polite"></p>
</div>
</div></section>

Expand Down Expand Up @@ -260,6 +260,6 @@ <h2 class="reveal" data-i18n="about.title">Por qué existe</h2>
<script async data-goatcounter="https://stats.nextsync.cloudless.club/count" src="https://stats.nextsync.cloudless.club/count.js"></script>
<script defer src="https://stat.domatix.com/script.js" data-website-id="0fd3377e-8bba-4651-96e4-82faee5896be"></script>
<script src="i18n.js?v=4"></script>
<script src="app.js?v=4"></script>
<script src="app.js?v=5"></script>
</body>
</html>
8 changes: 5 additions & 3 deletions landing/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,9 @@ a:hover { text-decoration: underline; }
.slider-viewport {
border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
border: 1px solid var(--card-border); cursor: zoom-in;
touch-action: pan-y;
}
.slider-viewport img { display: block; width: 100%; height: auto; }
.slider-viewport img { display: block; width: 100%; height: auto; user-select: none; -webkit-user-drag: none; }
.slider-arrow {
position: absolute; top: 40%; transform: translateY(-50%);
width: 46px; height: 46px; border-radius: 50%; border: none; cursor: pointer;
Expand Down Expand Up @@ -323,7 +324,8 @@ dialog#lightbox img { display: block; max-width: 92vw; max-height: 86vh; }
.nav-github, .nav-actions .nav-github { display: inline-flex; }
.hero-install { display: none; }
.nav-actions { margin-left: auto; gap: 0.45rem; }
.slider-arrow.prev { left: 6px; }
.slider-arrow.next { right: 6px; }
/* Phones: swipe on the image and the thumbnails are the navigation.
Floating arrows would only cover the screenshot. */
.slider-arrow { display: none; }
.slider-thumbs button { width: 72px; }
}
Loading