🎮ArcadeLab

Травник

by SparkDragon18
2010 lines43.6 KB
▶ Play
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Травник</title>

<style>
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600;700&family=IM+Fell+English+SC&display=swap');

*{
    box-sizing:border-box;
    -webkit-tap-highlight-color:transparent;
}

html,body{
    margin:0;
    width:100%;
    height:100%;
    overflow:hidden;
    background:#10130f;
    color:#fff;
    font-family:"Cormorant Garamond",Georgia,serif;
    touch-action:none;
}

button{
    font:inherit;
}

#forest{
    position:fixed;
    inset:0;
    width:100%;
    height:100%;
    object-fit:cover;
    z-index:0;
    user-select:none;
    animation:forestBreath 14s ease-in-out infinite alternate;
}

@keyframes forestBreath{
    from{transform:scale(1);}
    to{transform:scale(1.035);}
}

.scene-dark{
    position:fixed;
    inset:0;
    z-index:1;
    pointer-events:none;
    background:
        radial-gradient(
            ellipse at 50% 42%,
            rgba(74,92,52,.06),
            rgba(0,0,0,.65) 75%
        ),
        linear-gradient(
            to bottom,
            rgba(0,0,0,.16),
            rgba(0,0,0,.7)
        );
}

.scene-dark::after{
    content:"";
    position:absolute;
    inset:-10%;
    background:
        radial-gradient(
            ellipse at 25% 65%,
            rgba(190,220,170,.07),
            transparent 30%
        ),
        radial-gradient(
            ellipse at 75% 35%,
            rgba(220,210,150,.05),
            transparent 28%
        );
    animation:lightDrift 12s ease-in-out infinite alternate;
}

@keyframes lightDrift{
    from{
        transform:translateX(-2%) translateY(1%);
        opacity:.5;
    }
    to{
        transform:translateX(2%) translateY(-2%);
        opacity:1;
    }
}

/* =================================
   МЕНЮ
================================= */

#menu{
    position:relative;
    z-index:2;
    width:100%;
    height:100%;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    padding:16px 10px;
    animation:menuAppear .9s ease both;
}

@keyframes menuAppear{
    from{
        opacity:0;
        transform:translateY(18px);
    }
    to{
        opacity:1;
        transform:translateY(0);
    }
}

.title{
    margin:0 0 4px;
    font-family:"IM Fell English SC",serif;
    font-size:clamp(36px,7vw,64px);
    letter-spacing:4px;
    text-align:center;
    color:#efe2bf;
    text-shadow:
        0 3px 12px #000,
        0 0 20px rgba(225,207,151,.25);
    animation:titleFloat 5s ease-in-out infinite;
}

@keyframes titleFloat{
    0%,100%{transform:translateY(0);}
    50%{transform:translateY(-3px);}
}

.subtitle{
    margin:0 0 10px;
    color:#eadfbe;
    font-size:clamp(15px,3vw,20px);
    text-align:center;
    text-shadow:0 2px 8px #000;
    animation:softAppear 1.2s ease both;
}

@keyframes softAppear{
    from{opacity:0;}
    to{opacity:1;}
}

/* =================================
   КАРУСЕЛЬ
================================= */

.carousel{
    position:relative;
    width:min(1100px,100%);
    height:min(64vh,570px);
    overflow:hidden;
    border-radius:30px;
    touch-action:pan-y;
}

.carousel::before{
    content:"";
    position:absolute;
    left:50%;
    bottom:2%;
    width:min(900px,90%);
    height:28%;
    transform:translateX(-50%);
    background:radial-gradient(
        ellipse,
        rgba(0,0,0,.8),
        rgba(0,0,0,.28) 55%,
        transparent 75%
    );
    filter:blur(13px);
    z-index:0;
}

.books{
    position:absolute;
    left:50%;
    top:0;
    width:max-content;
    min-width:max-content;
    height:100%;
    display:flex;
    align-items:center;
    gap:28px;
    transform:translate3d(-50%,0,0);
    transition:
        transform .75s cubic-bezier(.16,.85,.2,1);
    will-change:transform;
}

.book{
    position:relative;
    flex:0 0 clamp(210px,45vw,330px);
    height:clamp(310px,58vh,470px);
    overflow:visible;
    border-radius:9px 16px 16px 9px;
    cursor:pointer;
    opacity:.43;
    transform:
        scale(.82)
        rotateY(0deg)
        translateY(18px);
    filter:
        brightness(.65)
        saturate(.72)
        drop-shadow(0 15px 18px rgba(0,0,0,.65));
    transition:
        transform .75s cubic-bezier(.16,.85,.2,1),
        opacity .55s ease,
        filter .55s ease;
    transform-style:preserve-3d;
    perspective:1000px;
}

.book::before{
    content:"";
    position:absolute;
    inset:-8px;
    border-radius:16px;
    background:
        radial-gradient(
            ellipse,
            rgba(225,204,146,.2),
            transparent 65%
        );
    opacity:0;
    filter:blur(12px);
    transition:opacity .6s ease;
    pointer-events:none;
}

.book.selected::before{
    opacity:1;
}

.book::after{
    content:"";
    position:absolute;
    top:2%;
    right:-8px;
    width:9px;
    height:96%;
    border-radius:0 8px 8px 0;
    background:
        repeating-linear-gradient(
            to bottom,
            #c9a970 0 2px,
            #80633d 3px 4px
        );
    opacity:.7;
    box-shadow:
        3px 5px 8px rgba(0,0,0,.45);
    z-index:4;
}

.book.selected{
    opacity:1;
    transform:
        scale(1)
        rotateY(0deg)
        translateY(0);
    filter:
        brightness(1)
        saturate(1)
        drop-shadow(0 22px 30px rgba(0,0,0,.8))
        drop-shadow(0 0 22px rgba(255,224,153,.28));
    z-index:5;
}

.book.selected:hover{
    transform:
        scale(1.025)
        rotateY(0deg)
        translateY(-4px);
}

.book.left{
    transform:
        scale(.82)
        rotateY(27deg)
        translateY(18px);
}

.book.right{
    transform:
        scale(.82)
        rotateY(-27deg)
        translateY(18px);
}

.book-cover-wrap{
    position:absolute;
    inset:0;
    overflow:hidden;
    border-radius:8px 15px 15px 8px;
    background:#392d20;
    box-shadow:
        inset 0 0 0 2px rgba(255,235,180,.18),
        inset 8px 0 18px rgba(0,0,0,.25);
}

.book-cover{
    width:100%;
    height:100%;
    display:block;
    object-fit:cover;
    object-position:center;
    border-radius:8px 15px 15px 8px;
    user-select:none;
    -webkit-user-drag:none;
    transition:
        transform .8s cubic-bezier(.16,.85,.2,1),
        filter .5s ease;
}

.book.herbal .book-cover{
    transform:scale(1.48);
    object-position:center center;
}

.book.herbal.selected .book-cover{
    transform:scale(1.62);
}

/* ГРИБНИК */

.book.mushroom .book-cover{
    transform:scale(1.04);
    object-position:center center;
}

.book.mushroom.selected .book-cover{
    transform:scale(1.08);
}

/* =================================
   НАЗВАНИЯ
================================= */

.book-name{
    position:absolute;
    left:50%;
    bottom:-43px;
    transform:translateX(-50%);
    min-width:170px;
    text-align:center;
    padding:8px 15px;
    border-radius:20px;
    background:rgba(15,12,8,.78);
    border:1px solid rgba(255,226,166,.28);
    color:#eadbb8;
    font-size:19px;
    letter-spacing:.6px;
    opacity:0;
    transition:
        opacity .45s ease,
        transform .45s ease;
    white-space:nowrap;
    pointer-events:none;
    box-shadow:
        0 5px 15px rgba(0,0,0,.35);
}

.book.selected .book-name{
    opacity:1;
    transform:
        translateX(-50%)
        translateY(-2px);
}

.swipe-hint{
    margin-top:7px;
    height:22px;
    color:rgba(255,240,205,.72);
    font-size:13px;
    letter-spacing:1.5px;
    text-shadow:0 2px 6px #000;
    animation:hintPulse 3s ease-in-out infinite;
}

@keyframes hintPulse{
    0%,100%{opacity:.55;}
    50%{opacity:1;}
}

/* =================================
   ТОЧКИ
================================= */

.dots{
    display:flex;
    justify-content:center;
    gap:9px;
    margin-top:10px;
}

.dot{
    width:8px;
    height:8px;
    border-radius:50%;
    background:rgba(255,240,205,.3);
    transition:
        width .45s ease,
        background .45s ease,
        box-shadow .45s ease;
}

.dot.active{
    width:25px;
    border-radius:8px;
    background:#ead09a;
    box-shadow:
        0 0 9px rgba(255,220,150,.45);
}

/* =================================
   КНОПКИ
================================= */

#openButton,
#backButton{
    border:
        1px solid rgba(255,232,175,.4);
    color:#f5e6c4;
    background:
        linear-gradient(
            180deg,
            rgba(94,71,40,.95),
            rgba(44,31,19,.95)
        );
    border-radius:15px;
    padding:13px 28px;
    min-width:220px;
    font-size:19px;
    cursor:pointer;
    box-shadow:
        0 8px 18px rgba(0,0,0,.5),
        inset 0 1px rgba(255,255,255,.15);
    transition:
        transform .18s ease,
        filter .25s ease,
        box-shadow .35s ease;
    margin-top:12px;
    animation:buttonGlow 4s ease-in-out infinite;
}

@keyframes buttonGlow{
    0%,100%{
        box-shadow:
            0 8px 18px rgba(0,0,0,.5),
            inset 0 1px rgba(255,255,255,.15);
    }
    50%{
        box-shadow:
            0 8px 20px rgba(0,0,0,.55),
            0 0 17px rgba(220,197,140,.17),
            inset 0 1px rgba(255,255,255,.18);
    }
}

#openButton:active,
#backButton:active{
    transform:scale(.94);
    filter:brightness(1.3);
    box-shadow:
        0 3px 10px rgba(0,0,0,.5),
        0 0 25px rgba(255,220,150,.35);
}

/* =================================
   ОТКРЫТИЕ
================================= */

#opening{
    position:fixed;
    inset:0;
    z-index:10;
    display:none;
    align-items:center;
    justify-content:center;
    background:
        radial-gradient(
            circle,
            rgba(92,76,42,.18),
            #080906 70%
        );
    opacity:0;
}

#opening.show{
    display:flex;
    animation:
        openingSceneIn .35s ease forwards;
}

@keyframes openingSceneIn{
    from{opacity:0;}
    to{opacity:1;}
}

.opening-book{
    width:min(62vw,430px);
    aspect-ratio:.7;
    border-radius:8px;
    overflow:hidden;
    box-shadow:
        0 0 70px rgba(255,230,170,.2),
        0 25px 60px rgba(0,0,0,.8);
    transform-origin:left center;
}

.opening-book img{
    width:100%;
    height:100%;
    display:block;
    object-fit:cover;
}

.opening-book.animate{
    animation:
        bookOpen .95s
        cubic-bezier(.18,.82,.2,1)
        forwards;
}

@keyframes bookOpen{
    0%{
        transform:
            perspective(1000px)
            rotateY(0deg)
            scale(.72);
        opacity:0;
    }
    22%{opacity:1;}
    55%{
        transform:
            perspective(1000px)
            rotateY(-22deg)
            scale(1.02);
        opacity:1;
    }
    100%{
        transform:
            perspective(1000px)
            rotateY(-82deg)
            scale(1.08);
        opacity:0;
    }
}

/* =================================
   ЧИТАТЕЛЬ
================================= */

#reader{
    position:fixed;
    inset:0;
    z-index:9;
    display:none;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    padding:12px;
    background:
        radial-gradient(
            circle,
            rgba(0,0,0,.12),
            rgba(0,0,0,.78)
        );
    opacity:0;
}

#reader.show{
    display:flex;
    animation:
        readerIn .65s ease forwards;
}

@keyframes readerIn{
    from{
        opacity:0;
        transform:scale(.97);
    }
    to{
        opacity:1;
        transform:scale(1);
    }
}

#reader.closing{
    display:flex;
    animation:
        readerOut .65s ease forwards;
}

@keyframes readerOut{
    from{
        opacity:1;
        transform:scale(1);
    }
    to{
        opacity:0;
        transform:scale(.96) translateY(15px);
    }
}

.book-spread{
    position:relative;
    width:min(96vw,1050px);
    height:min(76vh,680px);
    display:flex;
    overflow:hidden;
    border-radius:7px;
    box-shadow:
        0 25px 60px rgba(0,0,0,.75),
        0 0 30px rgba(255,220,150,.12);
    perspective:1400px;
}

.book-spread.page-turn{
    animation:
        spreadTurn .45s ease;
}

@keyframes spreadTurn{
    0%{
        transform:
            rotateY(0deg)
            scale(1);
    }
    45%{
        transform:
            rotateY(2deg)
            scale(.985);
    }
    100%{
        transform:
            rotateY(0deg)
            scale(1);
    }
}

.page{
    position:relative;
    width:50%;
    height:100%;
    overflow:hidden;
    background-image:url(
        "https://img.magnific.com/premium-vector/old-parchment-paper-sheet-vintage-aged-texture-vector-illustration_969400-501.jpg?semt=ais_hybrid&w=740&q=80"
    );
    background-size:cover;
    background-position:center;
    color:#3e2d1c;
    padding:clamp(20px,4vw,48px);
    box-shadow:
        inset 0 0 30px rgba(60,35,10,.18);
    transition:
        filter .4s ease;
}

.page:first-child{
    border-right:
        1px solid rgba(60,35,10,.35);
}

.page h2{
    margin:0 0 18px;
    font-family:
        "IM Fell English SC",
        "Cormorant Garamond",
        Georgia,
        serif;
    font-size:
        clamp(23px,4vw,36px);
    text-align:center;
    color:#49331d;
    font-weight:600;
    letter-spacing:.4px;
}

.page p{
    margin:0;
    font-size:
        clamp(16px,2.3vw,22px);
    line-height:1.58;
    text-align:left;
    font-weight:500;
}

.page::after{
    content:"";
    position:absolute;
    inset:0;
    pointer-events:none;
    background:
        linear-gradient(
            90deg,
            rgba(70,40,10,.08),
            transparent 15%,
            transparent 85%,
            rgba(70,40,10,.08)
        );
}

.reader-controls{
    display:flex;
    gap:10px;
    align-items:center;
    justify-content:center;
    margin-top:12px;
}

.reader-controls button{
    min-width:52px;
    height:45px;
    border:
        1px solid rgba(255,232,175,.35);
    border-radius:12px;
    background:rgba(40,28,16,.9);
    color:#eadbb8;
    font-size:25px;
    cursor:pointer;
    transition:
        transform .18s ease,
        filter .25s ease;
}

.reader-controls button:active{
    transform:scale(.9);
    filter:brightness(1.35);
}

#pageInfo{
    min-width:110px;
    text-align:center;
    color:#eadbb8;
    font-size:15px;
}

#backButton{
    min-width:150px;
    margin-top:8px;
}

/* =================================
   ВЫХОД
================================= */

#reader.exiting .book-spread{
    animation:
        bookClose .65s
        cubic-bezier(.2,.8,.2,1)
        forwards;
}

@keyframes bookClose{
    0%{
        transform:
            perspective(1400px)
            rotateY(0deg)
            scale(1);
        opacity:1;
    }
    45%{
        transform:
            perspective(1400px)
            rotateY(-8deg)
            scale(.98);
        opacity:.9;
    }
    100%{
        transform:
            perspective(1400px)
            rotateY(72deg)
            scale(.78)
            translateX(20px);
        opacity:0;
    }
}

/* =================================
   МОБИЛЬНАЯ ВЕРСИЯ
================================= */

@media(max-width:650px){

    .carousel{
        height:60vh;
    }

    .books{
        gap:18px;
    }

    .book{
        flex-basis:245px;
        height:390px;
    }

    .book.left{
        transform:
            scale(.76)
            rotateY(30deg)
            translateY(18px);
    }

    .book.right{
        transform:
            scale(.76)
            rotateY(-30deg)
            translateY(18px);
    }

    .book.herbal .book-cover{
        transform:scale(1.54);
    }

    .book.herbal.selected .book-cover{
        transform:scale(1.67);
    }

    .book.mushroom .book-cover{
        transform:scale(1.04);
    }

    .book.mushroom.selected .book-cover{
        transform:scale(1.08);
    }

    .book-name{
        bottom:-39px;
        font-size:16px;
    }

    .book-spread{
        width:98vw;
        height:68vh;
    }

    .page{
        padding:16px;
    }

    .page p{
        font-size:15px;
        line-height:1.45;
    }
}
</style>
</head>

<body>

<img
    id="forest"
    src="https://images.pexels.com/photos/17491580/pexels-photo-17491580.jpeg"
    alt=""
>

<div class="scene-dark"></div>

<!-- =================================
     МЕНЮ
================================= -->

<section id="menu">

    <h1 class="title">
        ТРАВНИК
    </h1>

    <p class="subtitle">
        Выберите книгу
    </p>

    <div
        class="carousel"
        id="carousel"
    >

        <div
            class="books"
            id="books"
        >

            <!-- ЦВЕТНИК -->

            <div
                class="book selected"
                data-index="0"
            >

                <div class="book-cover-wrap">

                    <img
                        class="book-cover"
                        src="https://basket-15.wbbasket.ru/vol2261/part226117/226117052/images/big/1.webp"
                        alt="Цветник"
                    >

                </div>

                <div class="book-name">
                    Цветник
                </div>

            </div>


            <!-- ЛЕЧЕБНИК -->

            <div
                class="book"
                data-index="1"
            >

                <div class="book-cover-wrap">

                    <img
                        class="book-cover"
                        src="https://cdn.eksmo.ru/v2/ITD000000000608288/COVER/cover1__w820.jpg"
                        alt="Лечебник"
                    >

                </div>

                <div class="book-name">
                    Лечебник
                </div>

            </div>


            <!-- ТРАВНИК -->

            <div
                class="book herbal"
                data-index="2"
            >

                <div class="book-cover-wrap">

                    <img
                        class="book-cover"
                        src="https://ogorod.ua/_sh/47/4703b_4.jpg"
                        alt="Травник"
                    >

                </div>

                <div class="book-name">
                    Травник
                </div>

            </div>


            <!-- ГРИБНИК -->

            <div
                class="book mushroom"
                data-index="3"
            >

                <div class="book-cover-wrap">

                    <img
                        class="book-cover"
                        src="https://content.rozetka.com.ua/goods/images/big/330812091.png"
                        alt="Грибник"
                    >

                </div>

                <div class="book-name">
                    Грибник
                </div>

            </div>

        </div>

    </div>

    <div class="swipe-hint">
        СВАЙП ВЛЕВО ИЛИ ВПРАВО
    </div>

    <div class="dots">
        <span class="dot active"></span>
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="dot"></span>
    </div>

    <button id="openButton">
        Открыть книгу
    </button>

</section>


<!-- =================================
     АНИМАЦИЯ ОТКРЫТИЯ
================================= -->

<section id="opening">

    <div class="opening-book">

        <img
            id="openingCover"
            src=""
            alt=""
        >

    </div>

</section>


<!-- =================================
     ЧИТАТЕЛЬ
================================= -->

<section id="reader">

    <div class="book-spread">

        <article
            class="page"
            id="leftPage"
        >
            <h2></h2>
            <p></p>
        </article>

        <article
            class="page"
            id="rightPage"
        >
            <h2></h2>
            <p></p>
        </article>

    </div>

    <div class="reader-controls">

        <button id="prevPage">
            ‹
        </button>

        <div id="pageInfo">
            1 / 7
        </div>

        <button id="nextPage">
            ›
        </button>

    </div>

    <button id="backButton">
        Вернуться
    </button>

</section>


<script>

/* =================================
   ЭЛЕМЕНТЫ
================================= */

const carousel =
    document.getElementById("carousel");

const booksContainer =
    document.getElementById("books");

const books =
    [...document.querySelectorAll(".book")];

const dots =
    [...document.querySelectorAll(".dot")];

const openButton =
    document.getElementById("openButton");

const menu =
    document.getElementById("menu");

const opening =
    document.getElementById("opening");

const openingBook =
    document.querySelector(".opening-book");

const openingCover =
    document.getElementById("openingCover");

const reader =
    document.getElementById("reader");

const bookSpread =
    document.querySelector(".book-spread");

const leftPage =
    document.getElementById("leftPage");

const rightPage =
    document.getElementById("rightPage");

const prevPage =
    document.getElementById("prevPage");

const nextPage =
    document.getElementById("nextPage");

const backButton =
    document.getElementById("backButton");

const pageInfo =
    document.getElementById("pageInfo");


/* =================================
   ЗВУКИ
================================= */

const sounds = {

    page: new Audio(
        "https://sfxmint.com/dl/paper-fabric-page-turn-01.mp3"
    ),

    forest: new Audio(
        "https://sfxmint.com/dl/ambience-forest-20.mp3"
    )

};

sounds.page.preload = "auto";

sounds.forest.preload = "auto";
sounds.forest.loop = true;
sounds.forest.volume = 0.22;

let soundStarted = false;


function startForestSound(){

    if(soundStarted) return;

    soundStarted = true;

    sounds.forest.currentTime = 0;

    sounds.forest.play().catch(() => {
        soundStarted = false;
    });

}


window.addEventListener(
    "pointerdown",
    startForestSound,
    {
        once:false,
        passive:true
    }
);


function playPageSound(){

    sounds.page.pause();

    sounds.page.currentTime = 0;

    sounds.page.volume = 0.75;

    sounds.page.play().catch(() => {});

}


/* =================================
   ДАННЫЕ КНИГ
================================= */

const library = [

    {
        name:"Цветник",

        cover:
            "https://basket-15.wbbasket.ru/vol2261/part226117/226117052/images/big/1.webp",

        pages:[

            ["Ромашка",
             "Нежный полевой цветок. В травниках ромашку часто описывают как растение с мягким ароматом и светлыми лепестками."],

            ["Василёк",
             "Синий цветок полей. Хорошо узнаваем по ярким лепесткам и тонкому стеблю."],

            ["Одуванчик",
             "Жёлтый солнечный цветок. После цветения его пушистые семена разлетаются от ветра."],

            ["Календула",
             "Яркий оранжевый цветок. Календула давно известна как декоративное и садовое растение."],

            ["Мак",
             "Красный полевой цветок с тонкими лепестками. Особенно красиво выглядит среди зелёной травы."],

            ["Лютик",
             "Небольшой жёлтый цветок. Его лепестки часто блестят на солнце."],

            ["Колокольчик",
             "Цветок с характерной формой колокольчика. Встречается на лугах и лесных полянах."],

            ["Незабудка",
             "Маленькие голубые цветки с жёлтой серединкой. Обычно растёт группами."],

            ["Ландыш",
             "Лесное растение с белыми колокольчатыми цветками. Все части растения требуют осторожности."],

            ["Иван-чай",
             "Высокое растение с розово-фиолетовыми цветками. Часто встречается на солнечных лесных местах."],

            ["Клевер",
             "Небольшое растение с округлыми соцветиями. Его часто можно увидеть на лугах."],

            ["Тысячелистник",
             "Растение с множеством маленьких цветков, собранных в плоские соцветия."],

            ["Мальва",
             "Высокое декоративное растение с крупными цветами. Часто украшает сады и деревенские участки."],

            ["Конец цветника",
             "Цветник закончен. Перелистите обратно или выберите другую книгу."]
        ]
    },


    {
        name:"Лечебник",

        cover:
            "https://cdn.eksmo.ru/v2/ITD000000000608288/COVER/cover1__w820.jpg",

        pages:[

            ["Мята",
             "Душистое растение с прохладным ароматом. Используется в напитках и кулинарии."],

            ["Крапива",
             "Известное растение с жгучими листьями. В природе его легко узнать по характерным зубчатым листьям."],

            ["Мелисса",
             "Ароматная трава с запахом лимона. Её часто выращивают возле дома."],

            ["Шалфей",
             "Душистое растение с серебристо-зелёными листьями. Любит солнечные места."],

            ["Чабрец",
             "Невысокое растение с маленькими листочками и приятным ароматом."],

            ["Ромашка аптечная",
             "Небольшой цветок с белыми лепестками и жёлтой серединкой."],

            ["Липа",
             "Большое дерево с душистыми цветками. Во время цветения вокруг липы слышно много насекомых."],

            ["Шиповник",
             "Колючий кустарник с красивыми цветами и красными плодами."],

            ["Малина",
             "Кустарник с ароматными ягодами. Встречается в садах и лесах."],

            ["Чистотел",
             "Дикорастущее растение с жёлтыми цветками. Сок растения может раздражать кожу."],

            ["Подорожник",
             "Растение с широкими листьями, часто растущее возле дорог и троп."],

            ["Зверобой",
             "Растение с жёлтыми цветками. Встречается на солнечных полянах и лугах."],

            ["Безопасность",
             "Не собирайте неизвестные растения. Некоторые растения могут быть ядовитыми. Для лечения всегда обращайтесь к взрослым и медицинским специалистам."],

            ["Конец лечебника",
             "Лечебник закончен. Можно вернуться в меню."]
        ]
    },


    {
        name:"Травник",

        cover:
            "https://ogorod.ua/_sh/47/4703b_4.jpg",

        pages:[

            ["Крапива",
             "Крапива легко узнаётся по зубчатым листьям и жгучим волоскам. В природе лучше не трогать её голыми руками."],

            ["Подорожник",
             "Подорожник часто растёт возле тропинок. Его широкие листья хорошо заметны среди травы."],

            ["Зверобой",
             "Зверобой любит солнечные места. У него небольшие жёлтые цветки."],

            ["Мята",
             "Мята обладает характерным прохладным ароматом. Её часто выращивают в садах."],

            ["Мелисса",
             "Мелиссу можно узнать по приятному лимонному запаху. Она хорошо растёт на садовых участках."],

            ["Чабрец",
             "Чабрец — невысокая ароматная трава с маленькими листьями и цветками."],

            ["Шиповник",
             "Шиповник — колючий кустарник. Осенью на нём появляются красные плоды."],

            ["Клевер",
             "Клевер часто покрывает луга. Его округлые соцветия привлекают насекомых."],

            ["Тысячелистник",
             "У тысячелистника много маленьких цветков, собранных в крупные соцветия."],

            ["Иван-чай",
             "Иван-чай может образовывать целые заросли на солнечных лесных участках."],

            ["Липовый цвет",
             "Во время цветения липы воздух наполняется сладким ароматом. Цветки привлекают множество насекомых."],

            ["Одуванчик",
             "Одуванчик легко узнать по ярко-жёлтым цветкам и белым пушистым семенам после цветения."],

            ["Правила травника",
             "Никогда не пробуйте неизвестные растения. Не собирайте растения рядом с дорогами и загрязнёнными местами. Всегда спрашивайте взрослого, если не уверены в растении."],

            ["Конец травника",
             "Травник закончен. Можно закрыть книгу и вернуться к лесной библиотеке."]
        ]
    },


    {
        name:"Грибник",

        cover:
            "https://content.rozetka.com.ua/goods/images/big/330812091.png",

        pages:[

            ["Белый гриб",
             "Белый гриб — один из самых известных лесных грибов. Его шляпка обычно коричневая, а ножка светлая и крепкая."],

            ["Лисичка",
             "Лисички имеют яркий жёлто-оранжевый цвет. Их часто находят группами во мху и среди лесной подстилки."],

            ["Подберёзовик",
             "Подберёзовик часто растёт рядом с берёзами. У него коричневая шляпка и светлая ножка с тёмными чешуйками."],

            ["Подосиновик",
             "Подосиновик отличается яркой шляпкой и крепкой ножкой. Название связано с тем, что гриб часто встречается возле осин."],

            ["Маслёнок",
             "Маслёнок обычно имеет гладкую, блестящую шляпку. Многие виды можно встретить возле хвойных деревьев."],

            ["Опёнок",
             "Опята часто растут группами на древесине и возле пней. Важно точно определять вид гриба перед сбором."],

            ["Мухомор",
             "Мухомор легко узнаётся по яркой шляпке. Некоторые виды ядовиты, поэтому незнакомые грибы нельзя пробовать."],

            ["Сыроежка",
             "Сыроежки бывают разных цветов. Только по цвету шляпки определять съедобность гриба нельзя."],

            ["Шампиньон",
             "Шампиньоны встречаются не только в природе, но и выращиваются человеком. Дикие похожие грибы нужно определять очень внимательно."],

            ["Сморчок",
             "Сморчок имеет характерную складчатую шляпку. При сборе лесных грибов важно знать особенности обработки и приготовления."],

            ["Где искать грибы",
             "Грибы встречаются в лесах, на полянах и возле деревьев. Место произрастания может помочь определить гриб, но не заменяет точного определения вида."],

            ["Корзина грибника",
             "Для сбора грибов используют открытую корзину или другую подходящую ёмкость. Не собирайте неизвестные или повреждённые грибы."],

            ["Правила грибника",
             "Никогда не пробуйте неизвестный гриб. Если есть сомнения в определении — не собирайте его. Лучше показать находку взрослому или опытному специалисту."],

            ["Конец грибника",
             "Грибник закончен. Можно вернуться в лесную библиотеку и выбрать другую книгу."]
        ]
    }

];


let selectedBook = 0;
let currentBook = 0;
let currentSpread = 0;
let isOpening = false;
let isClosing = false;


/* =================================
   ЦЕНТРИРОВАНИЕ
================================= */

function centerSelectedBook(){

    const selected =
        books[selectedBook];

    if(!selected) return;

    const rowCenter =
        booksContainer.offsetWidth / 2;

    const bookCenter =
        selected.offsetLeft +
        selected.offsetWidth / 2;

    const shift =
        rowCenter - bookCenter;

    booksContainer.style.transform =
        `translate3d(calc(-50% + ${shift}px),0,0)`;

}


/* =================================
   ВЫБОР КНИГИ
================================= */

function selectBook(index){

    if(index < 0)
        index = books.length - 1;

    if(index >= books.length)
        index = 0;

    selectedBook = index;

    books.forEach((book,i)=>{

        book.classList.remove(
            "selected",
            "left",
            "right"
        );

        if(i === selectedBook){

            book.classList.add("selected");

        }else if(i < selectedBook){

            book.classList.add("left");

        }else{

            book.classList.add("right");

        }

    });

    dots.forEach((dot,i)=>{

        dot.classList.toggle(
            "active",
            i === selectedBook
        );

    });

    centerSelectedBook();

}


function moveBook(direction){

    selectBook(
        selectedBook + direction
    );

}


/* =================================
   КЛИК ПО КНИГАМ
================================= */

books.forEach((book,index)=>{

    book.addEventListener(
        "click",
        ()=>{

            startForestSound();

            if(index === selectedBook){

                openBook();

            }else{

                selectBook(index);

            }

        }
    );

});


/* =================================
   СВАЙП КАРУСЕЛИ
================================= */

let carouselStartX = 0;
let carouselStartY = 0;

carousel.addEventListener(
    "pointerdown",
    e=>{

        carouselStartX = e.clientX;
        carouselStartY = e.clientY;

    }
);

carousel.addEventListener(
    "pointerup",
    e=>{

        const dx =
            e.clientX - carouselStartX;

        const dy =
            e.clientY - carouselStartY;

        if(
            Math.abs(dx) > 45 &&
            Math.abs(dx) > Math.abs(dy)
        ){

            if(dx < 0){

                moveBook(1);

            }else{

                moveBook(-1);

            }

        }

    }
);


/* =================================
   ОТКРЫТИЕ КНИГИ
================================= */

openButton.addEventListener(
    "click",
    openBook
);


function openBook(){

    if(isOpening || isClosing)
        return;

    startForestSound();

    isOpening = true;

    currentBook = selectedBook;
    currentSpread = 0;

    const data =
        library[currentBook];

    /*
       ПОЛНОСТЬЮ СБРАСЫВАЕМ
       СОСТОЯНИЕ ЧИТАТЕЛЯ
    */

    reader.classList.remove(
        "show",
        "closing",
        "exiting"
    );

    reader.style.display = "none";
    reader.style.opacity = "0";

    bookSpread.classList.remove(
        "page-turn"
    );

    /* Обложка открытия */

    openingCover.src =
        data.cover;

    opening.classList.remove("show");
    openingBook.classList.remove("animate");

    void opening.offsetWidth;

    opening.classList.add("show");
    openingBook.classList.add("animate");


    /*
       ПОКАЗЫВАЕМ ЧИТАТЕЛЬ
    */

    setTimeout(()=>{

        menu.style.display = "none";

        reader.style.display = "flex";

        reader.classList.remove(
            "closing",
            "exiting"
        );

        void reader.offsetWidth;

        reader.classList.add("show");

        /*
           ВАЖНО:
           каждый раз заново
           рисуем страницы
        */

        renderPages();

    },700);


    setTimeout(()=>{

        opening.classList.remove("show");
        openingBook.classList.remove("animate");

        isOpening = false;

    },1050);

}


/* =================================
   ОТОБРАЖЕНИЕ СТРАНИЦ
================================= */

function renderPages(){

    const data =
        library[currentBook];

    if(!data || !data.pages)
        return;

    const leftIndex =
        currentSpread * 2;

    const rightIndex =
        leftIndex + 1;

    const left =
        data.pages[leftIndex];

    const right =
        data.pages[rightIndex];


    leftPage.querySelector("h2").textContent =
        left ? left[0] : "";

    leftPage.querySelector("p").textContent =
        left ? left[1] : "";


    rightPage.querySelector("h2").textContent =
        right ? right[0] : "";

    rightPage.querySelector("p").textContent =
        right ? right[1] : "";


    const totalSpreads =
        Math.ceil(
            data.pages.length / 2
        );


    pageInfo.textContent =
        `${currentSpread + 1} / ${totalSpreads}`;


    prevPage.disabled =
        currentSpread <= 0;

    nextPage.disabled =
        currentSpread >= totalSpreads - 1;


    prevPage.style.opacity =
        prevPage.disabled ? ".35" : "1";

    nextPage.style.opacity =
        nextPage.disabled ? ".35" : "1";

}


/* =================================
   АНИМАЦИЯ СТРАНИЦ
================================= */

function animatePageTurn(){

    bookSpread.classList.remove(
        "page-turn"
    );

    void bookSpread.offsetWidth;

    bookSpread.classList.add(
        "page-turn"
    );

}


/* =================================
   СЛЕДУЮЩАЯ СТРАНИЦА
================================= */

function next(){

    const totalSpreads =
        Math.ceil(
            library[currentBook].pages.length / 2
        );

    if(currentSpread >= totalSpreads - 1)
        return;

    currentSpread++;

    playPageSound();

    animatePageTurn();

    renderPages();

}


/* =================================
   ПРЕДЫДУЩАЯ СТРАНИЦА
================================= */

function previous(){

    if(currentSpread <= 0)
        return;

    currentSpread--;

    playPageSound();

    animatePageTurn();

    renderPages();

}


nextPage.addEventListener(
    "click",
    next
);

prevPage.addEventListener(
    "click",
    previous
);


/* =================================
   СВАЙП СТРАНИЦ
================================= */

let pageStartX = 0;
let pageStartY = 0;

bookSpread.addEventListener(
    "pointerdown",
    e=>{

        pageStartX = e.clientX;
        pageStartY = e.clientY;

    }
);

bookSpread.addEventListener(
    "pointerup",
    e=>{

        const dx =
            e.clientX - pageStartX;

        const dy =
            e.clientY - pageStartY;

        if(
            Math.abs(dx) > 45 &&
            Math.abs(dx) > Math.abs(dy)
        ){

            if(dx < 0){

                next();

            }else{

                previous();

            }

        }

    }
);


/* =================================
   ВЫХОД ИЗ КНИГИ
================================= */

backButton.addEventListener(
    "click",
    closeReader
);


function closeReader(){

    if(isClosing || isOpening)
        return;

    isClosing = true;

    reader.classList.remove(
        "closing"
    );

    reader.classList.add(
        "exiting"
    );


    setTimeout(()=>{

        reader.classList.remove(
            "show",
            "closing",
            "exiting"
        );

        reader.style.display = "none";
        reader.style.opacity = "0";

        currentSpread = 0;

        leftPage.querySelector("h2").textContent = "";
        leftPage.querySelector("p").textContent = "";

        rightPage.querySelector("h2").textContent = "";
        rightPage.querySelector("p").textContent = "";

        bookSpread.classList.remove(
            "page-turn"
        );

        menu.style.display = "flex";

        menu.style.animation = "none";

        void menu.offsetWidth;

        menu.style.animation =
            "menuAppear .7s ease both";

        isClosing = false;

    },650);

}


/* =================================
   КЛАВИАТУРА
================================= */

window.addEventListener(
    "keydown",
    e=>{

        if(
            reader.classList.contains("show")
        ){

            if(e.key === "ArrowRight")
                next();

            if(e.key === "ArrowLeft")
                previous();

            if(e.key === "Escape")
                closeReader();

        }else{

            if(e.key === "ArrowRight")
                moveBook(1);

            if(e.key === "ArrowLeft")
                moveBook(-1);

            if(
                e.key === "Enter" ||
                e.key === " "
            ){

                openBook();

            }

        }

    }
);


/* =================================
   ЗАПУСК
================================= */

selectBook(0);

window.addEventListener(
    "resize",
    ()=>{
        centerSelectedBook();
    }
);

setTimeout(
    centerSelectedBook,
    50
);

</script>

</body>
</html>

Game Source: Травник

Creator: SparkDragon18

Libraries: none

Complexity: complex (2010 lines, 43.6 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-goldenbear87" to link back to the original. Then publish at arcadelab.ai/publish.