diff --git a/index.html b/index.html index c588c6f..25f83a0 100644 --- a/index.html +++ b/index.html @@ -10,13 +10,14 @@ + @@ -74,6 +191,7 @@ О нас Меню + Собрать кофе Instagram Как нас найти @@ -84,8 +202,72 @@

Кофе, за которым возвращаются

Свежая обжарка, тихие углы и выпечка каждое утро — кофейня рядом с домом.

- Смотреть меню - Как нас найти + Собрать свой кофе + Смотреть меню +
+ + + +
+
+
+

Соберите свой кофе

+

Сначала — настроение подберёт базовый рецепт. Дальше — настраивайте под себя: итоговая цена меняется на ходу.

+
+ +
+ + + +
+ +
@@ -226,5 +408,6 @@ + diff --git a/script.js b/script.js new file mode 100644 index 0000000..e7e57e0 --- /dev/null +++ b/script.js @@ -0,0 +1,247 @@ +const PRODUCTS = { + base: [ + { id: "espresso", label: "Эспрессо", price: 900, hasMilk: false, drink: true }, + { id: "americano", label: "Американо", price: 1100, hasMilk: false, drink: true }, + { id: "latte", label: "Латте (база)", price: 900, hasMilk: true, drink: true }, + { id: "mocha", label: "Мока", price: 1100, hasMilk: true, drink: true }, + { id: "herb", label: "Травяной чай", price: 800, hasMilk: false, drink: true }, + ], + milk: [ + { id: "none", label: "Без молока", price: 0 }, + { id: "milk", label: "Коровье", price: 100 }, + { id: "oat", label: "Овсяное", price: 300 }, + { id: "almond", label: "Миндальное", price: 300 }, + { id: "coconut", label: "Кокосовое", price: 350 }, + ], + syrup: [ + { id: "none", label: "Без сиропа", price: 0 }, + { id: "vanilla", label: "Ваниль", price: 300 }, + { id: "caramel", label: "Карамель", price: 300 }, + { id: "lavender", label: "Лаванда", price: 350 }, + { id: "honey", label: "Мёд", price: 350 }, + ], + size: [ + { id: "s", label: "250 мл", price: 0 }, + { id: "m", label: "350 мл", price: 300 }, + { id: "l", label: "450 мл", price: 500 }, + ], + temp: [ + { id: "hot", label: "Горячий", price: 0 }, + { id: "iced", label: "На льду", price: 200 }, + ], +}; + +const BASE_COLORS = { + espresso: "var(--kt-ai-fg)", + americano: "var(--kt-ai-fg-muted)", + latte: "var(--kt-ai-primary-subtle)", + mocha: "var(--kt-ai-border-strong)", + herb: "var(--kt-ai-chart-green)", +}; +const MILK_COLORS = { + none: "transparent", + milk: "var(--kt-ai-bg)", + oat: "var(--kt-ai-bg-soft)", + almond: "var(--kt-ai-bg-hover)", + coconut: "var(--kt-ai-bg-elevated)", +}; +const SYRUP_COLORS = { + none: "transparent", + vanilla: "var(--kt-ai-chip-orange-bg)", + caramel: "var(--kt-ai-chip-orange-fg)", + lavender: "var(--kt-ai-chip-purple-bg)", + honey: "var(--kt-ai-chip-orange-bg)", +}; + +const MOODS = { + wake: { base: "espresso", milk: "none", syrup: "none", size: "s", temp: "hot" }, + soft: { base: "latte", milk: "milk", syrup: "vanilla", size: "m", temp: "hot" }, + light: { base: "herb", milk: "none", syrup: "honey", size: "m", temp: "hot" }, +}; + +const state = { base: null, milk: "none", syrup: "none", size: "m", temp: "hot" }; + +const $ = (id) => document.getElementById(id); +const byId = (group, id) => (PRODUCTS[group] || []).find((p) => p.id === id); +const fmt = (price) => price.toLocaleString("ru-RU") + " \u20B8"; + +function renderGroup(group) { + const wrap = $("group-" + group); + wrap.innerHTML = ""; + PRODUCTS[group].forEach((opt) => { + const label = document.createElement("label"); + label.className = "opt"; + + const input = document.createElement("input"); + input.type = "radio"; + input.name = "opt-" + group; + input.value = opt.id; + input.checked = state[group] === opt.id; + input.addEventListener("change", () => { + state[group] = opt.id; + update(); + }); + + const name = document.createElement("span"); + name.className = "opt-label"; + name.textContent = opt.label; + + label.appendChild(input); + label.appendChild(name); + if (opt.price > 0) { + const price = document.createElement("span"); + price.className = "opt-price"; + price.textContent = "+" + fmt(opt.price); + label.appendChild(price); + } + wrap.appendChild(label); + }); +} + +function selected(group) { + return byId(group, state[group]); +} + +function priceTotal() { + if (!state.base) return 0; + const b = selected("base"); + const m = selected("milk"); + const s = selected("syrup"); + const z = selected("size"); + const t = selected("temp"); + return ( + b.price + + (b.hasMilk ? m.price : 0) + + (b.drink ? s.price : 0) + + z.price + + t.price + ); +} + +function paintCup() { + const layers = $("layers"); + const ice = $("iceGroup"); + const steam = $("steam"); + layers.innerHTML = ""; + ice.innerHTML = ""; + if (!state.base) return; + + const b = selected("base"); + const m = selected("milk"); + const s = selected("syrup"); + const z = selected("size"); + const t = selected("temp"); + + const fillFactor = z.id === "s" ? 0.45 : z.id === "m" ? 0.72 : 0.9; + const innerTop = 52; + const innerH = 158; + const fillH = Math.round(innerH * fillFactor); + const fillY = innerTop + (innerH - fillH); + + const widthBySize = { s: 88, m: 124, l: 148 }; + const width = widthBySize[z.id]; + const cupX = 100 - width / 2; + [ $("cupInner"), $("cupBody") ].forEach((el) => { + el.setAttribute("width", width); + el.setAttribute("x", cupX); + }); + + const NS = "http://www.w3.org/2000/svg"; + const addRect = (x, width, y, h, color, rx) => { + const rect = document.createElementNS(NS, "rect"); + rect.setAttribute("x", x); + rect.setAttribute("width", width); + rect.setAttribute("y", Math.max(innerTop, y)); + rect.setAttribute("height", h); + rect.setAttribute("fill", color); + rect.setAttribute("class", "part"); + if (rx) rect.setAttribute("rx", rx); + layers.appendChild(rect); + return rect; + }; + const innerX = cupX + 5; + const innerW = width - 10; + let y = fillY + fillH; + + if (t.id === "iced") { + if (b.hasMilk && m.id !== "none") { y -= fillH * 0.35; addRect(innerX, innerW, y, fillH * 0.35, MILK_COLORS[m.id]); } + y -= fillH * 0.45; addRect(innerX, innerW, y, fillH * 0.45, BASE_COLORS[b.id]); + if (b.drink && s.id !== "none") { y -= fillH * 0.2; addRect(innerX, innerW, y, fillH * 0.2, SYRUP_COLORS[s.id]); } + for (let i = 0; i < 3; i++) { + const cube = document.createElementNS(NS, "rect"); + cube.setAttribute("x", cupX + 10 + i * (innerW - 20) / 3); + cube.setAttribute("y", fillY + 6); + cube.setAttribute("width", Math.round((innerW - 20) / 3 - 8)); + cube.setAttribute("height", "22"); + cube.setAttribute("rx", "4"); + cube.setAttribute("fill", "rgba(190,222,240,0.9)"); + cube.setAttribute("stroke", "rgba(255,255,255,0.7)"); + cube.setAttribute("stroke-width", "2"); + cube.setAttribute("class", "part"); + ice.appendChild(cube); + } + } else { + addRect(innerX, innerW, fillY, fillH * 0.45, BASE_COLORS[b.id]); + if (b.hasMilk && m.id !== "none") addRect(innerX, innerW, fillY + fillH * 0.45, fillH * 0.3, MILK_COLORS[m.id]); + if (b.drink && s.id !== "none") addRect(innerX, innerW, fillY + fillH * 0.75, fillH * 0.25, SYRUP_COLORS[s.id]); + } + + steam.setAttribute("opacity", t.id === "hot" ? "1" : "0"); +} + +function buildName(b, m, s, z, t) { + const tempWord = t.id === "iced" ? "Ледяной" : "Горячий"; + const sizeWord = z.id === "s" ? "маленький" : z.id === "m" ? "средний" : "большой"; + const bits = [tempWord, sizeWord.toLowerCase()]; + if (b.hasMilk && m.id !== "none") bits.push(m.label.toLowerCase()); + if (b.drink && s.id !== "none") bits.push("сироп " + s.label.toLowerCase()); + bits.push(b.label.toLowerCase()); + return bits.join(", "); +} + +function update() { + paintCup(); + if (!state.base) return; + const b = selected("base"); + const m = selected("milk"); + const s = selected("syrup"); + const z = selected("size"); + const t = selected("temp"); + + $("buildName").textContent = buildName(b, m, s, z, t); + + const lines = ["Основа: " + b.label, "Размер: " + z.label, "Подача: " + t.label]; + if (b.hasMilk) lines.splice(1, 0, "Молоко: " + m.label); + if (b.drink) lines.splice(2, 0, "Сироп: " + s.label); + $("buildSummary").innerHTML = lines.map((l) => "" + l + "").join(""); + + const total = priceTotal(); + const base = b.price; + $("buildPrice").innerHTML = "Итого " + fmt(total) + + (total > base ? " (база " + fmt(base) + " + " + fmt(total - base) + ")" : ""); +} + +function renderAll() { + Object.keys(PRODUCTS).forEach(renderGroup); +} + +function applyMood(moodId) { + const preset = MOODS[moodId]; + if (!preset) return; + Object.assign(state, preset); + renderAll(); + update(); + document.querySelectorAll(".mood-btn").forEach((btn) => { + const active = btn.dataset.mood === moodId; + btn.style.borderColor = active ? "var(--kt-ai-primary)" : ""; + btn.style.background = active ? "var(--kt-ai-primary-subtle)" : ""; + }); + $("builderCard").hidden = false; + $("builderCard").scrollIntoView({ behavior: "smooth", block: "nearest" }); +} + +document.querySelectorAll(".mood-btn").forEach((btn) => { + btn.addEventListener("click", () => applyMood(btn.dataset.mood)); +}); + +renderAll();