Сегодня сделаем прикольную штуку, используя только эмодзи и одну картинку: веб-страницу с изображением морского дна, растениями, раковинами и разными животными. Сама по себе такая страница не несёт никакой пользы, но при её создании мы потренируемся работать с вёрсткой, стилями и скриптами. Вот что у нас получится:
Логика проекта
Наш веб-проект будет состоять из трёх частей:
- HTML-страница, в которой мы будем видеть результат, а по нажатию кнопки добавлять новых обитателей аквариума.
- Стили оформления, которые будут отвечать за внешний вид элементов морского дна, рыбок и кнопки.
- Скрипт, который оживит наших морских обитателей, чтобы они плавали туда-сюда :-)
Создавать всё это мы будем в такой же последовательности — от страницы к скрипту, чтобы показать, что происходит на каждом из этапов.
Создаём веб-страницу
Поскольку всё будут делать стили и скрипт, веб-страница нужна нам в качестве контейнера для них. Создадим новый HTML-документ, пропишем необходимый минимум элементов, разметим задний, средний и передний план нашего аквариума. Заодно подключим файлы стилей и скрипта — их пока нет, но страница уже будет работать.
<!DOCTYPE html>
<html lang="ru" >
<head>
<meta charset="UTF-8">
<title>Аквариум</title>
<!-- подключаем стили -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- добавляем элементы -->
<div>
<!-- кнопка для добавления рыбки --!>
<button>Добавить рыбку</button>
<!-- задний фон --!>
<div>
<!-- рыбки -->
<div></div>
<!-- кораллы и раковины -->
<div></div>
</div>
<!-- элементы посередине -->
<div>
<!-- рыбки -->
<div></div>
<!-- растения -->
<div></div>
</div>
<!-- передний план -->
<div>
<!-- рыбки -->
<div></div>
</div>
</div>
<!-- подключаем скрипт -->
<script src="script.js"></script>
</body>
</html>
Сохраним этот код в файле index.html и откроем в браузере. На нашей странице пока нет ничего, кроме белого фона и кнопки, но мы это сейчас исправим.
Настраиваем стили
Для начала сделаем нашу кнопку интереснее и красивее. Раскрасим её и рамку, зададим отступы внутри, укажем шрифт и его размер. Ещё добавим немного анимации, которая будет отображаться при наведении мыши: пусть стрелка становится рукой с вытянутым указательным пальцем, а цвет кнопки немного меняется.
/* задаём стиль кнопки */
button {
/* абсолютное позиционирование */
position: absolute;
/* определяем порядок слоёв элементов на странице */
z-index: 2;
/* отступы для кнопки */
margin: 1rem;
/* размер шрифта текста на кнопке */
font-size: 1.2rem;
/* внутренние отступы для текста на кнопке */
padding: .5rem 1rem;
/* шрифт для текста на кнопке */
font-family: sans-serif;
/* меняем курсор при наведении на кнопку */
cursor:pointer;
/* фоновый цвет кнопки с прозрачностью 60% */
background: #009dff60;
/* тень для кнопки */
box-shadow: 0 0 .25rem 0 ;
/* границу для кнопки */
border: 4px solid #009dff;
/* радиус скругления углов кнопки */
border-radius: 4px;
/* при наведении на кнопку фоновый цвет меняется на другой с прозрачностью 90% */
&:hover{
background: #009dff90;
}
/* при нажатии кнопки её размер увеличивается на 10% */
&:active{
transform: scale(1.1);
}
}
Теперь нужно добавить изображение морского дна, причём так, чтобы оно занимало всё окно и не имело окантовки. Сгенерируем картинку в Кандинском с пропорциями 3:2 по промту «морское дно с песком и голубой водой, через которую проникают солнечные лучи». Сохраним картинку, укажем её в стилях и растянем на всю страницу:
body {
/* отступы фоновой картинки */
margin: 0;
}
.back {
/* цвет заднего фона */
background: #009dff30;
/* размер элементов фона */
font-size: 2rem;
/* картинка заднего фона */
background-image: url("sea_bottom.png");
/* картинка полностью заполняет задний план без изменения пропорций */
background-size: cover;
}
Теперь пропишем стили для элементов на заднем фоне, в середине и на переднем плане: определим размеры, а фон сделаем более голубым. Дополним стиль body
:
.coral {
/* отступы снизу */
bottom: -4rem;
/* отступы слева */
left: -3rem;
/* абсолютное позиционирование */
position: absolute;
/* размер шрифта */
font-size: 12rem;
/* запрет на перенос на новую строку */
white-space: nowrap;
/* расстояние между символами */
letter-spacing: 2rem;
}
.back .coral {
/* отступы слева */
left: 2rem;
/* отступы снизу */
bottom: -5rem;
/* размер шрифта */
font-size: 17rem;
/* запрет на перенос на новую строку */
white-space: nowrap;
/* расстояние между символами */
letter-spacing: 5rem;
}
.middle {
/* цвет фона */
background: #009dff30;
/* размер шрифта */
font-size: 4rem;
}
.front {
/* цвет фона */
background: #009dff30;
/* размер шрифта */
font-size: 6rem;
}
.layer {
/* ширина 100% */
width: 100%;
/* высота 100% */
height: 100%;
/* абсолютное позиционирование */
position: absolute;
/* скрывать содержимое, если оно выходит за пределы */
overflow: hidden;
}
.fish {
/* анимация длительностью 17 секунд с линейным типом и бесконечным повтором */
animation: example 17s linear infinite;
/* абсолютное позиционирование */
position: absolute;
}
Зададим стили будущей анимации:
// задаём ключевые кадры
@keyframes example {
// что будет на первом кадре
0% {
// разворачиваем рыбку
left: 0;
transform: scaleX(-1);
}
// в середине анимации
50% {
// в середине она доплыла до края аквариума
left: 100%;
transform: scaleX(-1);
}
51% {
// после разворачиваем рыбу
transform: scaleX(1);
}
// и в конце
100% {
// и отправляем её в обратное плавание
left: 0;
}
}
Дополним веб-страницу
Добавим недостающие элементы на нашу страницу: кораллы, раковины и растения.
<div>
<!-- кнопка для добавления рыбки -->
<button v-on:click="addFish">Добавить рыбку</button>
<!-- задний фон -->
<div class="layer back">
<!-- рыбки -->
<div v-for="fish in back" class="fish" :style="fish.style">{{fish.fish}}</div>
<!-- кораллы и раковины -->
<div class="coral">🪸🎍🐚🪸🎍🐚🪸🎍🐚🪸🎍</div>
</div>
<!-- элементы посередине -->
<div class="layer middle">
<!-- рыбки -->
<div v-for="fish in middle" class="fish" :style="fish.style">{{fish.fish}}</div>
<!-- растения -->
<div class="coral">🌿🌱🌿🌱🌿🌱🌿🌱🌿</div>
</div>
<!-- передний план -->
<div class="layer front">
<!-- рыбки -->
<div v-for="fish in front" class="fish" :style="fish.style">{{fish.fish}}</div>
</div>
</div>
Вместо рыбок у нас пока плавает код, но мы исправим это на следующем шаге.
Пишем скрипт
За выбор рыбок и анимацию у нас будет отвечать такой скрипт:
// используем Vue для программирования поведения и появления рыбок
new Vue({
// создаём новое vue-приложение
el: "#app",
data: {
// массив с рыбками
fish: ["🐟", "🐠", "🐡", "🪼", "🐙", '🦑', '🐬', '🐳', '🐋', '🦑', '🦈', '🧜♀️', '🧜'],
// устанавливаем, какие рыбки будут двигаться на разных планах и с какой скоростью
front: [{ 'fish': '🐠', 'style': { top: '10%', animationDuration: '30s' } }],
middle: [{ 'fish': '🐡', 'style': { top: '47%', animationDuration: '25s' } }],
back: [{ 'fish': '🐟', 'style': { top: '14%', animationDuration: '35s' } }],
layers: ["front", "middle", "back"]
},
// задаём методы приложения
methods: {
// функция добавления новой рыбки
addFish: function (event) {
// выбираем случайным образом рыбку и план для неё
const icon = Math.floor(Math.random() * this.fish.length);
const layer = Math.floor(Math.random() * 3);
// создаём новую рыбку с выбранными параметрами
const object = {
fish: this.fish[icon],
style: {
// задаём размер и скорость движения рыбы
fontSize: Math.floor(Math.random() * 10 + 2) + "rem",
top: Math.floor(Math.random() * 100) + "%", animationDuration: Math.floor(Math.random() * 30 + 10) + "s" }
};
// добавляем рыбку на нужный слой
this.layers[layer] === "front" && this.front.push(object);
this.layers[layer] === "middle" && this.middle.push(object);
this.layers[layer] === "back" && this.back.push(object);
}
}
});
Получилось! Теперь в нашем аквариуме плавают рыбки:
<!DOCTYPE html>
<html lang="ru" >
<head>
<meta charset="UTF-8">
<title>Аквариум</title>
<!-- подключаем стили -->
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- добавляем элементы -->
<div id="app">
<!-- кнопка для добавления рыбки -->
<button v-on:click="addFish">Добавить рыбку</button>
<!-- задний фон -->
<div class="layer back">
<!-- рыбки -->
<div v-for="fish in back" class="fish" :style="fish.style">{{fish.fish}}</div>
<!-- кораллы и раковины -->
<div class="coral">🪸🎍🐚🪸🎍🐚🪸🎍🐚🪸🎍</div>
</div>
<!-- элементы посередине -->
<div class="layer middle">
<!-- рыбки -->
<div v-for="fish in middle" class="fish" :style="fish.style">{{fish.fish}}</div>
<!-- растения -->
<div class="coral">🌿🌱🌿🌱🌿🌱🌿🌱🌿</div>
</div>
<!-- передний план -->
<div class="layer front">
<!-- рыбки -->
<div v-for="fish in front" class="fish" :style="fish.style">{{fish.fish}}</div>
</div>
</div>
<!-- подключаем скрипты -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script><script src="./script.js"></script>
</body>
</html>
body {
/* отступы фоновой картинки */
margin: 0;
}
.coral {
/* отступы снизу */
bottom: -4rem;
/* отступы слева */
left: -3rem;
/* абсолютное позиционирование */
position: absolute;
/* размер шрифта */
font-size: 12rem;
/* запрет на перенос на новую строку */
white-space: nowrap;
/* расстояние между символами */
letter-spacing: 2rem;
}
.back {
/* цвет заднего фона */
background: #009dff30;
/* размер элементов фона */
font-size: 2rem;
/* картинка заднего фона */
background-image: url("sea_bottom.png");
/* картинка полностью заполняет задний план без изменения пропорций */
background-size: cover;
}
.back .coral {
/* отступы слева */
left: 2rem;
/* отступы снизу */
bottom: -5rem;
/* размер шрифта */
font-size: 17rem;
/* запрет на перенос на новую строку */
white-space: nowrap;
/* расстояние между символами */
letter-spacing: 5rem;
}
.middle {
/* цвет фона */
background: #009dff30;
/* размер шрифта */
font-size: 4rem;
}
.front {
/* цвет фона */
background: #009dff30;
/* размер шрифта */
font-size: 6rem;
}
.layer {
/* ширина 100% */
width: 100%;
/* высота 100% */
height: 100%;
/* абсолютное позиционирование */
position: absolute;
/* скрывать содержимое, если оно выходит за пределы */
overflow: hidden;
}
.fish {
/* анимация длительностью 17 секунд с линейным типом и бесконечным повтором */
animation: example 17s linear infinite;
/* абсолютное позиционирование */
position: absolute;
}
// задаём ключевые кадры
@keyframes example {
// что будет на первом кадре
0% {
// разворачиваем рыбку
left: 0;
transform: scaleX(-1);
}
// в середине анимации
50% {
// в середине она доплыла до края аквариума
left: 100%;
transform: scaleX(-1);
}
51% {
// после разворачиваем рыбу
transform: scaleX(1);
}
// и в конце
100% {
// и отправляем её в обратное плавание
left: 0;
}
}
// используем Vue для программирования поведения и появления рыбок
new Vue({
// создаём новое vue-приложение
el: "#app",
data: {
// массив с рыбками
fish: ["🐟", "🐠", "🐡", "🪼", "🐙", '🦑', '🐬', '🐳', '🐋', '🦑', '🦈', '🧜♀️', '🧜'],
// устанавливаем, какие рыбки будут двигаться на разных планах и с какой скоростью
front: [{ 'fish': '🐠', 'style': { top: '10%', animationDuration: '30s' } }],
middle: [{ 'fish': '🐡', 'style': { top: '47%', animationDuration: '25s' } }],
back: [{ 'fish': '🐟', 'style': { top: '14%', animationDuration: '35s' } }],
layers: ["front", "middle", "back"]
},
// задаём методы приложения
methods: {
// функция добавления новой рыбки
addFish: function (event) {
// выбираем случайным образом рыбку и план для неё
const icon = Math.floor(Math.random() * this.fish.length);
const layer = Math.floor(Math.random() * 3);
// создаём новую рыбку с выбранными параметрами
const object = {
fish: this.fish[icon],
style: {
// задаём размер и скорость движения рыбы
fontSize: Math.floor(Math.random() * 10 + 2) + "rem",
top: Math.floor(Math.random() * 100) + "%", animationDuration: Math.floor(Math.random() * 30 + 10) + "s" }
};
// добавляем рыбу на нужный слой
this.layers[layer] === "front" && this.front.push(object);
this.layers[layer] === "middle" && this.middle.push(object);
this.layers[layer] === "back" && this.back.push(object);
}
}
});