Игра "Memory" в стиле Игры в кальмара

Модификатор позволяет создать игру "Memory" с таймером и анимацией после игры в стиле "Игры в кальмара"
Инструкция по настройке
Шаблон страницы
Как добавить шаблон
ВИДЕО В РАЗРАБОТКЕ
70727545
Для корректной работы модификаций подключите библиотеку jQuery:
Настройки сайта -> Еще -> Подключить jQuery на страницах сайта
jQuery
В настройках профиля на Тильде
  1. Включаем галочку "Участвовать в тестировании новых функций"
1 шаг
Создаем Zero Block и задаем ему класс uc-game
  1. Добавляем html-элементы для карточек
  2. Задаем им одинаковые размеры
  3. Объединяем все элементы в группу
  4. Указываем параметру Group значение Object (beta)
  5. Задаем группе класс flip-board
  6. Указываем параметру Flex значение Auto
  7. В html-элементы добавляем код
2 шаг
Скопировать код
Настраиваем код в html-элементах
Для каждой пары карточек:
  1. Меняем порядковый номер в data-card=
  2. Меняем ссылку на обложку-рубашку
  3. Меняем ссылку на обложку-картинку
3 шаг
В том же Zero блоке настраиваем таймер
1. Добавляем текстовый элемент
2. Задаем ему класс tsec
4 шаг
Создаем Zero блок, который будет показываться при успешном прохождении игры
1. Задаем блоку класс uc-succes
2. Добавляем любые необходимые элементы на блок
5 шаг
Создаем Zero блок, который будет показываться при провале (истечении таймера)
1. Задаем блоку класс uc-lose
2. Добавляем любые необходимые элементы на блок
6 шаг
Создаем блок T123 и копируем в него код
7 шаг
Скопировать код
Настраиваем код в блоке T123
1. На 3 строке заменяем значение цвета заливки экрана при проигрыше
2. На 32 строке заменяем 25 на необходимое количество секунд в таймере (на прохождение игры).
Учитывайте время проигрывания прелоадера, если добавили его.
8 шаг
<!-- GM02 - Модификация для Тильды. Игра "Memory" в стиле Игры в кальмара https://mod.tistols.com/memory-squid -->
    <div class="flip-card" data-card="1">
        <div class="flip-card-inner">
            <div class="flip-card-front" style="background-image: url('https://static.tildacdn.com/tild3139-6530-4334-b039-346633313433/1331312.svg');">
                
            </div>
            <div class="flip-card-back" style="background-image: url('https://static.tildacdn.com/tild6236-3533-4837-b835-343134383139/Frame_10.svg');">
                
            </div>
        </div>
    </div>
<!-- GM02 - Модификация для Тильды. Игра "Memory" в стиле Игры в кальмара https://mod.tistols.com/memory-squid -->
    <!-- Все стили и SVG для заливки -->
<style>
:root { --pathBg:#FF0000 }
.shape-overlays { width: 100vw; height: 100vh; pointer-events: none; position: fixed; top: 0; left: 0; z-index: 1; opacity: 0; transition: opacity 0.5s; }
.shape-overlays .shape-overlays__path { fill: var(--pathBg) }
.shape-overlays__blue { width: 100vw; height: 100vh; pointer-events: none; position: absolute; top: 0; left: 0 }
.hidden { opacity: 0; pointer-events: none; transition: opacity 1.5s ease; }
.visible { opacity: 1; pointer-events: auto; transition: opacity 1.5s ease; }
.closepopup { cursor: pointer; transition: transform 0.2s ease, opacity 0.2s ease; }
.closepopup:hover { transform: scale(1.1); opacity: 0.8; }
.flip-card { width: 100%; height: 100%; perspective: 1000px; position: absolute; top: 0; left: 0; }
.flip-card-inner { position: relative; width: 100%; height: 100%; transition: transform 0.6s; transform-style: preserve-3d; cursor: pointer; }
.flip-card-front, .flip-card-back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-family: Arial, sans-serif; color: white; }
.flip-card-front { background-size: cover; background-position: center; }
.flip-card-front p { margin-top: auto; margin-bottom: 20px; }
.flip-card-back { background-size: cover; transform: rotateY(180deg); text-align: center; }
.flip-card-back p { max-width: 90% }
.flip-card.flipped .flip-card-inner { transform: rotateY(180deg); }
.uc-succes,
.uc-lose {
    display: none;
}
.textlose {
    position: fixed;
    z-index: 1000!important;
}
</style>
<svg class="shape-overlays" viewBox="0 0 100 100" preserveAspectRatio="none" style="position:fixed;top:0;left:0;z-index:999;"><path class="shape-overlays__path" d="M 0 0 V 100 C 10 100 10 100 20 100 C 30 100 30 100 40 100 C 50 100 50 100 60 100 C 70 100 70 100 80 100 C 90 100 90 100 100 100 V 0 H 0"></path></svg>

<script>
/* === КОНСТАНТЫ === */
const LOCAL_TIMER_DURATION = 25 * 1000; // 25 сек на игру (замените на 60*1000 для 1 мин)
const LS_KEY = 'uc-banner-timer-start';

let timerFinished = false;
let gameWon = false;

function getStartTime() {
  let start = localStorage.getItem(LS_KEY);
  if (!start) {
    start = Date.now();
    localStorage.setItem(LS_KEY, start);
  }
  return parseInt(start, 10);
}
function resetStartTime() {
  const now = Date.now();
  localStorage.setItem(LS_KEY, now);
  return now;
}

/* === ТАЙМЕР === */
function updateLocalTimer() {
  const startTime = getStartTime();
  const now = Date.now();
  let diff = LOCAL_TIMER_DURATION - (now - startTime);

  if (diff <= 0 && !timerFinished) {
    timerFinished = true;
    clearInterval(localTimerInterval);

    // Если игра не выиграна — показать провал
    if (!gameWon) {
      showFailureState();
    }
    // Скрыть баннер
    const zeroBlock = document.querySelector('.uc-tistols-banner');
    if (zeroBlock) {
      zeroBlock.classList.remove('visible');
      zeroBlock.classList.add('hidden');
    }
    return;
  }

  // Выводим оставшееся время (если нужно)
  const seconds = Math.max(0, Math.floor(diff / 1000));
  const minElem = document.querySelector('.tmin .tn-atom');
  const secElem = document.querySelector('.tsec .tn-atom');
  if (minElem) minElem.textContent = Math.floor(seconds / 60);
  if (secElem) secElem.textContent = seconds % 60;
}
// При первой загрузке страницы и каждом обновлении — сбрасываем таймер
resetStartTime();
const localTimerInterval = setInterval(updateLocalTimer, 1000);

/* === ИГРА MEMORY === */
$(window).on('load', function () {
    let flippedCards = [];
    let matchedCards = [];
    const totalCards = $('.flip-card').length;

    function flipCard($card) {
        if (flippedCards.length < 2 && !flippedCards.includes($card[0]) && !matchedCards.includes($card[0])) {
            $card.addClass('flipped');
            flippedCards.push($card[0]);
            if (flippedCards.length === 2) {
                checkForMatch();
            }
        }
    }

    function checkForMatch() {
        const [card1, card2] = flippedCards;
        const card1Type = $(card1).data('card');
        const card2Type = $(card2).data('card');
        if (card1Type === card2Type) {
            matchedCards.push(card1, card2);
            flippedCards = [];
            // Проверка, все ли карточки угаданы
            if (matchedCards.length === totalCards) {
                onGameSuccess();
            }
        } else {
            setTimeout(() => {
                $(card1).removeClass('flipped');
                $(card2).removeClass('flipped');
                flippedCards = [];
            }, 1000);
        }
    }

    function shuffleElements() {
        const $molecule = $('.flip-board .tn-molecule');
        if (!$molecule.length) return;
        const $elements = $molecule.children().toArray();
        $elements.sort(() => Math.random() - 0.5);
        $molecule.empty().append($elements);
    }
    shuffleElements();

    $(document).on('click', '.flip-card', function () {
        if (!timerFinished && !gameWon)
          flipCard($(this));
    });
});

/* === УСПЕХ: игра пройдена === */
function onGameSuccess() {
    gameWon = true;
    // Скрыть блок игры
    const gameBlock = document.querySelector('.uc-game');
    if (gameBlock) gameBlock.style.display = 'none';
    // Показать блок успеха
    const successBlock = document.querySelector('.uc-succes');
    if (successBlock) successBlock.style.display = 'block';
    // Отключить открытие попапа (если есть)
    const popupTrigger = document.querySelector('.flipсhik');
    if (popupTrigger) {
      popupTrigger.onclick = function(e){ e.stopPropagation(); return false; };
      popupTrigger.style.pointerEvents = 'none';
    }
    // Скрыть баннер (если нужен этот эффект)
    const zeroBlock = document.querySelector('.uc-tistols-banner');
    if (zeroBlock) {
      zeroBlock.classList.remove('visible');
      zeroBlock.classList.add('hidden');
    }
}

/* === ПРОВАЛ: время вышло === */
function showFailureState() {
    // Скрыть блок игры
    const gameBlock = document.querySelector('.uc-game');
    if (gameBlock) gameBlock.style.display = 'none';
    // Показать блок "lose"
    const loseBlock = document.querySelector('.uc-lose');
    if (loseBlock) loseBlock.style.display = 'block';
    // Запустить анимацию заливки
    runFillAnimation();
}

/* === АНИМАЦИЯ ЗАЛИВКИ === */
function runFillAnimation() {
    // Класс-аниматор (из вашего кода)
    const DURATION = 2400;
    const DELAY_POINTS_MAX = 1160;
    const DELAY_PER_PATH = 340;
    const points = 6;
    class J {
        constructor(e) {
            this.elm = e;
            this.path = e.querySelectorAll(".shape-overlays path");
            this.numPoints = points;
            this.duration = DURATION;
            this.delayPointsArray = [];
            this.delayPointsMax = DELAY_POINTS_MAX;
            this.delayPerPath = DELAY_PER_PATH;
            this.timeStart = Date.now();
            this.isOpened = false;
            this.isAnimating = false;
        }
        toggle() {
            this.isAnimating = true;
            const t = Math.random() * Math.PI * 2;
            for (let e = 0; e < this.numPoints; e++) {
                const n = e / (this.numPoints - 1) * Math.PI * 2;
                this.delayPointsArray[e] = (Math.sin(n + t) + 1) / 2 * this.delayPointsMax;
            }
            !this.isOpened ? this.open() : this.close();
        }
        open() {
            this.isOpened = true;
            this.elm.classList.add("is-opened");
            this.timeStart = Date.now();
            this.renderLoop();
        }
        close() {
            this.isOpened = false;
            this.elm.classList.remove("is-opened");
            this.timeStart = Date.now();
            this.renderLoop();
        }
        updatePath(t) {
            const e = [];
            for (let n = 0; n < this.numPoints; n++) {
                e[n] = 100 * this.constructor.cubicInOut(Math.min(Math.max(t - this.delayPointsArray[n], 0) / this.duration, 1));
            }
            let i = "";
            i += this.isOpened ? "M 0 0 V " + e[0] + " " : "M 0 " + e[0] + " ";
            for (let r = 0; r < this.numPoints - 1; r++) {
                const a = (r + 1) / (this.numPoints - 1) * 100;
                const o = a - 1 / (this.numPoints - 1) * 100 / 2;
                i += "C " + o + " " + e[r] + " " + o + " " + e[r + 1] + " " + a + " " + e[r + 1] + " ";
            }
            return i += this.isOpened ? "V 0 H 0" : "V 100 H 0";
        }
        render() {
            if (this.isOpened) {
                for (let t = 0; t < this.path.length; t++) {
                    this.path[t].setAttribute("d", this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * t)));
                }
            } else {
                for (let e = 0; e < this.path.length; e++) {
                    this.path[e].setAttribute("d", this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - e - 1))));
                }
            }
        }
        renderLoop() {
            this.render();
            if (Date.now() - this.timeStart < this.duration + this.delayPerPath * (this.path.length - 1) + this.delayPointsMax) {
                requestAnimationFrame(() => this.renderLoop());
            } else {
                this.isAnimating = false;
            }
        }
        static cubicInOut(t) {
            return t < 0.5 ? 4 * t * t * t : 0.5 * Math.pow(2 * t - 2, 3) + 1;
        }
    }
    // Запуск
    const svgElement = document.querySelector(".shape-overlays");
    if (svgElement) {
        svgElement.style.opacity = 1;
        if (!svgElement.jInstance) {
            svgElement.jInstance = new J(svgElement);
        }
        if (!svgElement.jInstance.isAnimating) {
            svgElement.jInstance.toggle();
        }
    }
}
</script>
Больше модификаций доступно по подписке
Полный доступ к 60+ модификациям
Обновления библиотеки
Оповещения о новых модификациях
Техподдержка в чате
Поддержка модов при изменениях в Tilda
Модуль подарочные карты тариф Lite на месяц
Доступ к ИИ-боту @tistolsaibot на год
Помощь в доработке модификации
Поддержка модов при изменениях в Tilda
Техподдержка в чате
Модуль smsmod.ru на год
Сертификат на разработку 3 модификаций
Модуль подарочные карты тариф Pro на месяц
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 60+ модификациям
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 14+ модификациям
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
Free
0 руб.
Месяц
Год
Pro
3 000 руб.
-50%
Club
18 000 руб.
-40%
Месяц
Год
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
Полный доступ к 60+ модификациям
Обновления библиотеки
Оповещения о новых модификациях
Техподдержка в чате
Поддержка модов при изменениях в Tilda
Модуль подарочные карты тариф Lite на месяц
Помощь в доработке модификации
Поддержка модов при изменениях в Tilda
Техподдержка в чате
Модуль smsmod.ru на месяц
Сертификат на разработку 1 модификаций
Модуль подарочные карты тариф Lite на месяц
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 60+ модификациям
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 14+ модификациям
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
Free
0 руб.
Месяц
Год
Pro
500 руб.
Club
2 500 руб.
Месяц
Год
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
Доступ к ИИ-боту @tistolsaibot на месяц
Вам также может быть интересно: