1006 lines
27 KiB
HTML
1006 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Pizza</title>
|
||
<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=Bebas+Neue&family=Outfit:wght@300;500;600&display=swap" rel="stylesheet" />
|
||
<style>
|
||
:root {
|
||
--bg: #1a1510;
|
||
--paper: #f4ede4;
|
||
--accent: #c45c26;
|
||
--muted: #8a7f72;
|
||
}
|
||
|
||
*, *::before, *::after {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html, body {
|
||
margin: 0;
|
||
min-height: 100%;
|
||
height: 100%;
|
||
background: var(--bg);
|
||
color: var(--paper);
|
||
font-family: "Outfit", system-ui, sans-serif;
|
||
}
|
||
|
||
body.preview-open {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.page {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.pizza-row {
|
||
flex: 1 1 50%;
|
||
min-height: 0;
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: stretch;
|
||
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
|
||
}
|
||
|
||
.pizza-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.pizza-row--image-right .text-col {
|
||
order: 1;
|
||
}
|
||
|
||
.pizza-row--image-right .img-col {
|
||
order: 2;
|
||
}
|
||
|
||
.pizza-row--image-left .text-col {
|
||
order: 2;
|
||
}
|
||
|
||
.pizza-row--image-left .img-col {
|
||
order: 1;
|
||
}
|
||
|
||
.text-col,
|
||
.img-col {
|
||
flex: 1 1 50%;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: clamp(1rem, 3vw, 2.5rem);
|
||
}
|
||
|
||
.text-col {
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: flex-start;
|
||
background: linear-gradient(135deg, rgba(26, 21, 16, 0.97) 0%, #231c15 100%);
|
||
}
|
||
|
||
.pizza-row--image-right .text-col {
|
||
padding-right: clamp(0.75rem, 2vw, 1.5rem);
|
||
}
|
||
|
||
.pizza-row--image-left .text-col {
|
||
padding-left: clamp(0.75rem, 2vw, 1.5rem);
|
||
}
|
||
|
||
.img-col {
|
||
position: relative;
|
||
background: radial-gradient(ellipse 80% 70% at 50% 50%, #2a2219 0%, var(--bg) 75%);
|
||
}
|
||
|
||
.pizza-row--image-right .img-col {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.pizza-row--image-left .img-col {
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.img-col--clickable {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.img-col--clickable:focus-visible {
|
||
outline: 2px solid var(--accent);
|
||
outline-offset: 4px;
|
||
}
|
||
|
||
.pizza-frame {
|
||
position: relative;
|
||
width: min(100%, 55vh);
|
||
height: min(100%, 55vh);
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.pizza-frame img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
display: block;
|
||
animation: pizza-spin 48s linear infinite;
|
||
will-change: transform;
|
||
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.pizza-frame:hover img {
|
||
animation-play-state: paused;
|
||
}
|
||
|
||
@keyframes pizza-spin {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
h1 {
|
||
font-family: "Bebas Neue", Impact, sans-serif;
|
||
font-size: clamp(2.5rem, 8vw, 5rem);
|
||
line-height: 0.95;
|
||
letter-spacing: 0.02em;
|
||
margin: 0 0 0.5rem;
|
||
color: var(--paper);
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: clamp(0.85rem, 1.8vw, 1rem);
|
||
font-weight: 500;
|
||
color: var(--accent);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.18em;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.details {
|
||
font-weight: 300;
|
||
font-size: clamp(0.9rem, 1.6vw, 1.05rem);
|
||
line-height: 1.55;
|
||
color: var(--muted);
|
||
max-width: 28em;
|
||
}
|
||
|
||
.details strong {
|
||
color: var(--paper);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.price {
|
||
margin-top: 1.25rem;
|
||
font-size: clamp(1.1rem, 2vw, 1.35rem);
|
||
font-weight: 600;
|
||
color: var(--paper);
|
||
}
|
||
|
||
/* ——— Preview ——— */
|
||
.preview {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 100;
|
||
display: none;
|
||
flex-direction: column;
|
||
background: var(--bg);
|
||
padding: clamp(0.75rem, 2vw, 1.5rem);
|
||
}
|
||
|
||
.preview.is-visible {
|
||
display: flex;
|
||
}
|
||
|
||
.preview__grid {
|
||
flex: 1;
|
||
min-height: 0;
|
||
display: grid;
|
||
grid-template-columns: minmax(9rem, 0.85fr) minmax(0, 3.2fr) minmax(9rem, 0.85fr);
|
||
grid-template-rows: 1fr auto;
|
||
grid-template-areas:
|
||
"left center right"
|
||
"left bottom bottom";
|
||
gap: clamp(0.5rem, 2vw, 1.25rem);
|
||
align-items: start;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.preview__grid {
|
||
grid-template-columns: 1fr;
|
||
grid-template-rows: auto auto 1fr auto;
|
||
grid-template-areas:
|
||
"left"
|
||
"center"
|
||
"right"
|
||
"bottom";
|
||
}
|
||
|
||
.preview-left {
|
||
padding-top: 2.5rem;
|
||
}
|
||
}
|
||
|
||
.preview-left {
|
||
grid-area: left;
|
||
padding-right: 0.5rem;
|
||
}
|
||
|
||
.preview-center {
|
||
grid-area: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 0;
|
||
position: relative;
|
||
}
|
||
|
||
.preview-right {
|
||
grid-area: right;
|
||
justify-self: end;
|
||
text-align: right;
|
||
width: 100%;
|
||
max-width: 16rem;
|
||
}
|
||
|
||
.preview-bottom {
|
||
grid-area: bottom;
|
||
justify-self: end;
|
||
align-self: end;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
padding-top: 0.5rem;
|
||
}
|
||
|
||
.preview__back {
|
||
position: absolute;
|
||
top: clamp(0.5rem, 2vw, 1rem);
|
||
left: clamp(0.5rem, 2vw, 1rem);
|
||
z-index: 2;
|
||
background: rgba(26, 21, 16, 0.85);
|
||
border: 1px solid rgba(244, 237, 228, 0.2);
|
||
color: var(--paper);
|
||
font-family: inherit;
|
||
font-size: 0.9rem;
|
||
padding: 0.45rem 0.85rem;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.preview__back:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.preview-name {
|
||
font-family: "Bebas Neue", Impact, sans-serif;
|
||
font-size: clamp(2rem, 5vw, 3.5rem);
|
||
line-height: 1;
|
||
margin: 0 0 1rem;
|
||
color: var(--paper);
|
||
}
|
||
|
||
.preview-label {
|
||
font-size: 0.75rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.12em;
|
||
color: var(--accent);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.ingredient-list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.ingredient-list li {
|
||
font-size: clamp(0.9rem, 1.4vw, 1rem);
|
||
font-weight: 300;
|
||
color: var(--muted);
|
||
padding: 0.35rem 0;
|
||
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
|
||
}
|
||
|
||
.ingredient-list li:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.macro-list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.macro-list li {
|
||
font-size: clamp(0.85rem, 1.3vw, 0.95rem);
|
||
padding: 0.4rem 0;
|
||
color: var(--muted);
|
||
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
|
||
}
|
||
|
||
.macro-list li strong {
|
||
display: block;
|
||
color: var(--paper);
|
||
font-weight: 500;
|
||
font-size: 0.7rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
margin-bottom: 0.15rem;
|
||
}
|
||
|
||
.carousel-wrap {
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: min(92vmin, 760px);
|
||
aspect-ratio: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.carousel-stage {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.carousel-layer {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
will-change: transform;
|
||
}
|
||
|
||
.carousel-layer img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
filter: drop-shadow(0 16px 40px rgba(0, 0, 0, 0.5));
|
||
}
|
||
|
||
.carousel-btn {
|
||
position: fixed;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 50%;
|
||
border: 1px solid rgba(244, 237, 228, 0.25);
|
||
background: rgba(26, 21, 16, 0.85);
|
||
color: var(--paper);
|
||
font-size: 1.35rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 110;
|
||
}
|
||
|
||
.carousel-btn:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.carousel-btn--prev {
|
||
left: env(safe-area-inset-left, 0);
|
||
right: auto;
|
||
}
|
||
|
||
.carousel-btn--next {
|
||
right: env(safe-area-inset-right, 0);
|
||
left: auto;
|
||
}
|
||
|
||
.preview-timer {
|
||
width: 100%;
|
||
max-width: min(92vmin, 760px);
|
||
margin-top: 0.75rem;
|
||
}
|
||
|
||
.preview-timer[hidden] {
|
||
display: none !important;
|
||
}
|
||
|
||
.preview-timer__label {
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.06em;
|
||
text-transform: uppercase;
|
||
color: var(--muted);
|
||
margin-bottom: 0.4rem;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.preview-timer__sec {
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--paper);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.preview-timer__track {
|
||
height: 8px;
|
||
border-radius: 999px;
|
||
background: rgba(244, 237, 228, 0.1);
|
||
overflow: hidden;
|
||
border: 1px solid rgba(244, 237, 228, 0.12);
|
||
}
|
||
|
||
.preview-timer__fill {
|
||
height: 100%;
|
||
width: 0%;
|
||
border-radius: inherit;
|
||
background: linear-gradient(90deg, #8b3d18, var(--accent));
|
||
box-shadow: 0 0 12px rgba(196, 92, 38, 0.35);
|
||
}
|
||
|
||
.cart-combo {
|
||
display: inline-flex;
|
||
align-items: stretch;
|
||
border: 1px solid rgba(244, 237, 228, 0.22);
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
background: rgba(35, 28, 21, 0.85);
|
||
}
|
||
|
||
.cart-combo__qty {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0.25rem 0.35rem;
|
||
border-right: 1px solid rgba(244, 237, 228, 0.15);
|
||
min-width: 3.25rem;
|
||
}
|
||
|
||
.qty-arrow {
|
||
width: 100%;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--paper);
|
||
font-size: 0.7rem;
|
||
line-height: 1;
|
||
padding: 0.2rem 0.5rem;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.qty-arrow:hover:not(:disabled) {
|
||
color: var(--accent);
|
||
}
|
||
|
||
.qty-arrow:disabled {
|
||
opacity: 0.35;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.qty-value {
|
||
font-weight: 600;
|
||
font-size: 1.15rem;
|
||
line-height: 1.2;
|
||
padding: 0.15rem 0;
|
||
min-height: 1.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-cart {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.65rem 1.15rem;
|
||
border: none;
|
||
border-radius: 0;
|
||
background: var(--accent);
|
||
color: var(--bg);
|
||
font-family: inherit;
|
||
font-weight: 600;
|
||
font-size: 0.95rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-cart:hover {
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.btn-cart svg {
|
||
width: 1.1em;
|
||
height: 1.1em;
|
||
}
|
||
|
||
.preview-hint {
|
||
font-size: 0.75rem;
|
||
color: var(--muted);
|
||
margin-top: 0.75rem;
|
||
text-align: center;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main class="page" id="home">
|
||
<section class="pizza-row pizza-row--image-right" aria-label="Pizza Margherita">
|
||
<div class="text-col">
|
||
<p class="subtitle">Klassiker</p>
|
||
<h1>Margherita</h1>
|
||
<p class="details">
|
||
<strong>Tomate, Mozzarella, Basilikum.</strong> Unser Teig über Nacht, Ofen aus Stein.
|
||
Sanft, säuerlich, klar — wie Neapel, nur auf deinem Bildschirm.
|
||
</p>
|
||
<p class="price">ab 9,50 €</p>
|
||
</div>
|
||
<div
|
||
class="img-col img-col--clickable"
|
||
role="button"
|
||
tabindex="0"
|
||
data-pizza-index="0"
|
||
aria-label="Margherita – Vorschau öffnen"
|
||
>
|
||
<div class="pizza-frame">
|
||
<img src="magarita.png" alt="" width="800" height="800" />
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="pizza-row pizza-row--image-left" aria-label="Pizza Salami">
|
||
<div
|
||
class="img-col img-col--clickable"
|
||
role="button"
|
||
tabindex="0"
|
||
data-pizza-index="1"
|
||
aria-label="Salami – Vorschau öffnen"
|
||
>
|
||
<div class="pizza-frame">
|
||
<img src="salami.png" alt="" width="800" height="800" />
|
||
</div>
|
||
</div>
|
||
<div class="text-col">
|
||
<p class="subtitle">Beliebt</p>
|
||
<h1>Salami</h1>
|
||
<p class="details">
|
||
<strong>Tomate, Mozzarella, luftgetrocknete Salami.</strong> Würzig, saftig, mit leichtem
|
||
Paprika-Touch. Für alle, die es etwas kräftiger mögen.
|
||
</p>
|
||
<p class="price">ab 11,00 €</p>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<div
|
||
class="preview"
|
||
id="preview"
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-labelledby="preview-title"
|
||
hidden
|
||
>
|
||
<button type="button" class="preview__back" id="preview-close">← Zurück</button>
|
||
|
||
<div class="preview__grid">
|
||
<div class="preview-left">
|
||
<p class="preview-label">Pizza</p>
|
||
<h2 class="preview-name" id="preview-title">Margherita</h2>
|
||
<p class="preview-label">Zutaten</p>
|
||
<ul class="ingredient-list" id="preview-ingredients"></ul>
|
||
</div>
|
||
|
||
<div class="preview-center">
|
||
<div class="carousel-wrap">
|
||
<button type="button" class="carousel-btn carousel-btn--prev" id="carousel-prev" aria-label="Vorheriges Bild">‹</button>
|
||
<div class="carousel-stage" id="carousel-stage">
|
||
<div class="carousel-layer" id="carousel-layer-a">
|
||
<img src="" alt="" width="800" height="800" />
|
||
</div>
|
||
<div class="carousel-layer" id="carousel-layer-b">
|
||
<img src="" alt="" width="800" height="800" />
|
||
</div>
|
||
</div>
|
||
<button type="button" class="carousel-btn carousel-btn--next" id="carousel-next" aria-label="Nächstes Bild">›</button>
|
||
</div>
|
||
<div
|
||
class="preview-timer"
|
||
id="preview-timer"
|
||
role="timer"
|
||
aria-label="Zeit bis zum nächsten Bildwechsel"
|
||
hidden
|
||
>
|
||
<div class="preview-timer__label">
|
||
<span>Nächster Wechsel</span>
|
||
<span class="preview-timer__sec" id="preview-timer-sec" aria-hidden="true">5 s</span>
|
||
</div>
|
||
<div class="preview-timer__track">
|
||
<div class="preview-timer__fill" id="preview-timer-fill"></div>
|
||
</div>
|
||
</div>
|
||
<p class="preview-hint">Pfeile: andere Pizza · Zurück: Übersicht</p>
|
||
</div>
|
||
|
||
<div class="preview-right">
|
||
<p class="preview-label">Nährwerte (pro Pizza)</p>
|
||
<ul class="macro-list" id="preview-macros"></ul>
|
||
</div>
|
||
|
||
<div class="preview-bottom">
|
||
<div class="cart-combo" role="group" aria-label="Menge und Warenkorb">
|
||
<div class="cart-combo__qty">
|
||
<button type="button" class="qty-arrow" id="qty-up" aria-label="Menge erhöhen">▲</button>
|
||
<span class="qty-value" id="qty-num">1</span>
|
||
<button type="button" class="qty-arrow" id="qty-down" aria-label="Menge verringern">▼</button>
|
||
</div>
|
||
<button type="button" class="btn-cart" id="btn-add-cart">
|
||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||
<path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49A1.003 1.003 0 0 0 20 4H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
|
||
</svg>
|
||
In den Warenkorb
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
const IMAGE_SWAP_MS = 5000;
|
||
const PREVIEW_SLIDE_MS = 650;
|
||
|
||
const pizzas = [
|
||
{
|
||
name: "Margherita",
|
||
image: "magarita.png",
|
||
imageCut: "magaritta angeschnitten.png",
|
||
ingredients: [
|
||
"Tomatensauce San Marzano",
|
||
"Fior di Latte Mozzarella",
|
||
"Frisches Basilikum",
|
||
"Olivenöl extra vergine",
|
||
"Hefeteig 48h",
|
||
],
|
||
macros: [
|
||
{ label: "Energie", value: "724 kcal" },
|
||
{ label: "Fett", value: "26 g" },
|
||
{ label: "davon gesättigt", value: "11 g" },
|
||
{ label: "Kohlenhydrate", value: "84 g" },
|
||
{ label: "Eiweiß", value: "28 g" },
|
||
{ label: "Salz", value: "2,1 g" },
|
||
],
|
||
},
|
||
{
|
||
name: "Salami",
|
||
image: "salami.png",
|
||
imageCut: "salami angeschnitten.png",
|
||
ingredients: [
|
||
"Tomatensauce",
|
||
"Mozzarella",
|
||
"Luftgetrocknete Salami",
|
||
"Oregano",
|
||
"Olivenöl",
|
||
"Hefeteig 48h",
|
||
],
|
||
macros: [
|
||
{ label: "Energie", value: "812 kcal" },
|
||
{ label: "Fett", value: "34 g" },
|
||
{ label: "davon gesättigt", value: "14 g" },
|
||
{ label: "Kohlenhydrate", value: "82 g" },
|
||
{ label: "Eiweiß", value: "32 g" },
|
||
{ label: "Salz", value: "2,8 g" },
|
||
],
|
||
},
|
||
];
|
||
|
||
let currentIndex = 0;
|
||
let quantity = 1;
|
||
let previewImageTimer = null;
|
||
let previewSwapGen = 0;
|
||
let previewPhaseFull = true;
|
||
let previewNextFromBottom = true;
|
||
let previewTimerRaf = null;
|
||
|
||
function encodeAssetPath(path) {
|
||
return path.split("/").map(encodeURIComponent).join("/");
|
||
}
|
||
|
||
function clearPreviewImageTimer() {
|
||
if (previewImageTimer !== null) {
|
||
clearTimeout(previewImageTimer);
|
||
previewImageTimer = null;
|
||
}
|
||
stopTimerSlider();
|
||
}
|
||
|
||
function stopTimerSlider() {
|
||
if (previewTimerRaf !== null) {
|
||
cancelAnimationFrame(previewTimerRaf);
|
||
previewTimerRaf = null;
|
||
}
|
||
var fill = document.getElementById("preview-timer-fill");
|
||
if (fill) fill.style.width = "0%";
|
||
}
|
||
|
||
function startTimerSlider(expectedGen) {
|
||
stopTimerSlider();
|
||
var fill = document.getElementById("preview-timer-fill");
|
||
var secEl = document.getElementById("preview-timer-sec");
|
||
if (!fill) return;
|
||
var start = performance.now();
|
||
function tick(now) {
|
||
if (expectedGen !== previewSwapGen) return;
|
||
var elapsed = now - start;
|
||
var p = Math.min(100, (elapsed / IMAGE_SWAP_MS) * 100);
|
||
fill.style.width = p + "%";
|
||
if (secEl) {
|
||
var left = Math.max(0, IMAGE_SWAP_MS - elapsed);
|
||
var s = Math.ceil(left / 1000);
|
||
if (s <= 0) s = 0;
|
||
secEl.textContent = s + " s";
|
||
}
|
||
if (p >= 100) {
|
||
previewTimerRaf = null;
|
||
return;
|
||
}
|
||
previewTimerRaf = requestAnimationFrame(tick);
|
||
}
|
||
previewTimerRaf = requestAnimationFrame(tick);
|
||
}
|
||
|
||
const preview = document.getElementById("preview");
|
||
const previewTimerEl = document.getElementById("preview-timer");
|
||
const previewTitle = document.getElementById("preview-title");
|
||
const previewIngredients = document.getElementById("preview-ingredients");
|
||
const previewMacros = document.getElementById("preview-macros");
|
||
const carouselLayerA = document.getElementById("carousel-layer-a");
|
||
const carouselLayerB = document.getElementById("carousel-layer-b");
|
||
const carouselImgA = carouselLayerA.querySelector("img");
|
||
const carouselImgB = carouselLayerB.querySelector("img");
|
||
const btnPrev = document.getElementById("carousel-prev");
|
||
const btnNext = document.getElementById("carousel-next");
|
||
const btnClose = document.getElementById("preview-close");
|
||
const qtyNum = document.getElementById("qty-num");
|
||
const qtyUp = document.getElementById("qty-up");
|
||
const qtyDown = document.getElementById("qty-down");
|
||
const btnAddCart = document.getElementById("btn-add-cart");
|
||
|
||
function resetPreviewLayers() {
|
||
[carouselLayerA, carouselLayerB].forEach(function (layer) {
|
||
layer.style.transition = "none";
|
||
layer.style.transform = "";
|
||
layer.style.zIndex = "";
|
||
});
|
||
}
|
||
|
||
function setupPreviewLoop() {
|
||
clearPreviewImageTimer();
|
||
previewSwapGen++;
|
||
const gen = previewSwapGen;
|
||
const p = pizzas[currentIndex];
|
||
previewPhaseFull = true;
|
||
previewNextFromBottom = true;
|
||
|
||
resetPreviewLayers();
|
||
|
||
carouselImgA.alt = "Pizza " + p.name;
|
||
carouselImgB.alt = "Pizza " + p.name;
|
||
|
||
if (!p.imageCut) {
|
||
if (previewTimerEl) previewTimerEl.hidden = true;
|
||
carouselImgA.src = encodeAssetPath(p.image);
|
||
carouselLayerA.style.transform = "translateY(0)";
|
||
carouselLayerA.style.zIndex = "2";
|
||
carouselImgB.removeAttribute("src");
|
||
carouselLayerB.style.transform = "translateY(110%)";
|
||
carouselLayerB.style.zIndex = "1";
|
||
return;
|
||
}
|
||
|
||
if (previewTimerEl) previewTimerEl.hidden = false;
|
||
|
||
carouselImgA.src = encodeAssetPath(p.image);
|
||
carouselImgB.src = encodeAssetPath(p.imageCut);
|
||
|
||
carouselLayerA.style.transform = "translateY(0)";
|
||
carouselLayerA.style.zIndex = "2";
|
||
carouselLayerB.style.transform = "translateY(110%)";
|
||
carouselLayerB.style.zIndex = "1";
|
||
|
||
previewImageTimer = setTimeout(function () {
|
||
runPreviewImageSwap(gen);
|
||
}, IMAGE_SWAP_MS);
|
||
startTimerSlider(gen);
|
||
}
|
||
|
||
function runPreviewImageSwap(expectedGen) {
|
||
if (expectedGen !== previewSwapGen) return;
|
||
const p = pizzas[currentIndex];
|
||
if (!p || !p.imageCut) return;
|
||
|
||
stopTimerSlider();
|
||
|
||
var outgoing =
|
||
carouselLayerA.style.zIndex === "2" ? carouselLayerA : carouselLayerB;
|
||
var incoming =
|
||
outgoing === carouselLayerA ? carouselLayerB : carouselLayerA;
|
||
var outgoingImg = outgoing.querySelector("img");
|
||
var incomingImg = incoming.querySelector("img");
|
||
|
||
var nextFull = !previewPhaseFull;
|
||
var nextPath = nextFull ? p.image : p.imageCut;
|
||
incomingImg.src = encodeAssetPath(nextPath);
|
||
incomingImg.alt = "Pizza " + p.name;
|
||
|
||
var fromBottom = previewNextFromBottom;
|
||
previewNextFromBottom = !previewNextFromBottom;
|
||
|
||
incoming.style.transition = "none";
|
||
outgoing.style.transition = "none";
|
||
incoming.style.transform = fromBottom
|
||
? "translateY(110%)"
|
||
: "translateY(-110%)";
|
||
outgoing.style.transform = "translateY(0)";
|
||
incoming.style.zIndex = "2";
|
||
outgoing.style.zIndex = "1";
|
||
|
||
outgoing.offsetHeight;
|
||
|
||
var ease = "cubic-bezier(0.4, 0, 0.2, 1)";
|
||
incoming.style.transition =
|
||
"transform " + PREVIEW_SLIDE_MS / 1000 + "s " + ease;
|
||
outgoing.style.transition =
|
||
"transform " + PREVIEW_SLIDE_MS / 1000 + "s " + ease;
|
||
|
||
function onEnd(ev) {
|
||
if (ev.propertyName !== "transform") return;
|
||
if (expectedGen !== previewSwapGen) return;
|
||
incoming.removeEventListener("transitionend", onEnd);
|
||
previewPhaseFull = nextFull;
|
||
outgoing.style.transition = "none";
|
||
outgoing.style.transform = "translateY(110%)";
|
||
outgoing.style.zIndex = "1";
|
||
incoming.style.zIndex = "2";
|
||
previewImageTimer = setTimeout(function () {
|
||
runPreviewImageSwap(expectedGen);
|
||
}, IMAGE_SWAP_MS);
|
||
startTimerSlider(expectedGen);
|
||
}
|
||
|
||
incoming.addEventListener("transitionend", onEnd);
|
||
|
||
requestAnimationFrame(function () {
|
||
requestAnimationFrame(function () {
|
||
if (expectedGen !== previewSwapGen) return;
|
||
incoming.style.transform = "translateY(0)";
|
||
outgoing.style.transform = "translateY(110%)";
|
||
});
|
||
});
|
||
}
|
||
|
||
function renderPizza() {
|
||
const p = pizzas[currentIndex];
|
||
previewTitle.textContent = p.name;
|
||
|
||
previewIngredients.innerHTML = p.ingredients
|
||
.map(function (ing) {
|
||
return "<li>" + ing + "</li>";
|
||
})
|
||
.join("");
|
||
|
||
previewMacros.innerHTML = p.macros
|
||
.map(function (m) {
|
||
return "<li><strong>" + m.label + "</strong>" + m.value + "</li>";
|
||
})
|
||
.join("");
|
||
|
||
setupPreviewLoop();
|
||
}
|
||
|
||
function openPreview(index) {
|
||
currentIndex = Math.max(0, Math.min(pizzas.length - 1, index));
|
||
quantity = 1;
|
||
qtyNum.textContent = String(quantity);
|
||
updateQtyButtons();
|
||
renderPizza();
|
||
preview.hidden = false;
|
||
preview.classList.add("is-visible");
|
||
document.body.classList.add("preview-open");
|
||
btnClose.focus();
|
||
}
|
||
|
||
function closePreview() {
|
||
clearPreviewImageTimer();
|
||
if (previewTimerEl) previewTimerEl.hidden = true;
|
||
previewSwapGen++;
|
||
resetPreviewLayers();
|
||
preview.classList.remove("is-visible");
|
||
preview.hidden = true;
|
||
document.body.classList.remove("preview-open");
|
||
}
|
||
|
||
function updateQtyButtons() {
|
||
qtyDown.disabled = quantity <= 1;
|
||
qtyUp.disabled = quantity >= 99;
|
||
}
|
||
|
||
document.querySelectorAll(".img-col--clickable").forEach(function (el) {
|
||
el.addEventListener("click", function () {
|
||
const idx = parseInt(el.getAttribute("data-pizza-index"), 10);
|
||
openPreview(idx);
|
||
});
|
||
el.addEventListener("keydown", function (e) {
|
||
if (e.key === "Enter" || e.key === " ") {
|
||
e.preventDefault();
|
||
const idx = parseInt(el.getAttribute("data-pizza-index"), 10);
|
||
openPreview(idx);
|
||
}
|
||
});
|
||
});
|
||
|
||
btnPrev.addEventListener("click", function () {
|
||
currentIndex = (currentIndex - 1 + pizzas.length) % pizzas.length;
|
||
renderPizza();
|
||
});
|
||
|
||
btnNext.addEventListener("click", function () {
|
||
currentIndex = (currentIndex + 1) % pizzas.length;
|
||
renderPizza();
|
||
});
|
||
|
||
btnClose.addEventListener("click", closePreview);
|
||
|
||
document.addEventListener("keydown", function (e) {
|
||
if (!preview.classList.contains("is-visible")) return;
|
||
if (e.key === "Escape") closePreview();
|
||
});
|
||
|
||
qtyUp.addEventListener("click", function () {
|
||
if (quantity < 99) {
|
||
quantity++;
|
||
qtyNum.textContent = String(quantity);
|
||
updateQtyButtons();
|
||
}
|
||
});
|
||
|
||
qtyDown.addEventListener("click", function () {
|
||
if (quantity > 1) {
|
||
quantity--;
|
||
qtyNum.textContent = String(quantity);
|
||
updateQtyButtons();
|
||
}
|
||
});
|
||
|
||
btnAddCart.addEventListener("click", function () {
|
||
const p = pizzas[currentIndex];
|
||
alert(
|
||
p.name +
|
||
" × " +
|
||
quantity +
|
||
" — (Demo: hier käme der echte Warenkorb.)"
|
||
);
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|