HTML + CSS + JS kullanan Animasyonlu Takvim
Başlangıç seviyesindeki bir geliştiriciyim ve akıllı telefonlar için bir takvim oluşturuyorum. Ben kullanıyorum HTML, CSS ve JS. Henüz projeyi tamamen bitirmedim, ancak dağınık bir kod yaptığımı hissediyorum . Git + kodunun bağlantısını burada yayınlayacağım, böylece daha iyi okuyabilir / klonlayabilir ve tasarım kalıpları konusunda bana yardımcı olabilirsiniz.
Not: Proje, IPhoneX çözünürlüğü (375 x 812) kullanılarak geliştirildi . Henüz yanıt vermiyor, bu yüzden kodu yukarıdaki çözünürlüğü kullanarak çalıştırmanızı tavsiye ederim.
Git bağlantısı: https://github.com/LucasBiazi/Mobile_Calendar_HTML_CSS_JS
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Calendar</title>
<script src="../script/script.js"></script>
<link rel="stylesheet" href="../css/style.css" />
</head>
<!-- As soon as the page loads, fulfills the table. -->
<body onload="load_DB(new Date().getFullYear(), new Date().getMonth())">
<div class="main">
<div class="title">
<span class="year_title" id="year_title"></span>
<span class="month_title" id="month_title"></span>
</div>
<div class="calendar">
<div id="month_days" class="month_days">
<table id="days">
<tr>
<th style="color: lightsalmon">Sun</th>
<th class="even">Mon</th>
<th>Tue</th>
<th class="even">Wed</th>
<th>Thu</th>
<th class="even">Fri</th>
<th style="color: lightsalmon">Sat</th>
</tr>
</table>
</div>
<!-- Future implementations. -->
<div class="data">
<div class="data_content">
<p style="color: black; font-size: 20px">No content.</p>
</div>
</div>
</div>
<div class="buttons">
<!-- Reteats a month. -->
<button
style="
animation: display_button_back 1s ease;
position: relative;
"
onclick="retreat_month(parseInt(document.getElementById('year_title').textContent),
identify_month(document.getElementById('month_title').textContent))"
>
<img
src="../../media/left-arrow-line-symbol.svg"
style="width: 35px; height: 35px"
alt="back"
/>
</button>
<!-- Shows current month. -->
<button
style="animation: display_opacity_zoom 1s ease-out;"
onclick="advance_month(new Date().getFullYear(), new Date().getMonth() - 1)"
>
T
</button>
<!-- Advances a month. -->
<button
style="
animation: display_button_next 1s ease;
position: relative;
"
onclick="advance_month(parseInt(document.getElementById('year_title').textContent),
identify_month(document.getElementById('month_title').textContent))"
>
<img
src="../../media/right-arrow-angle.svg"
style="width: 35px; height: 35px"
alt="next"
/>
</button>
</div>
</div>
</body>
</html>
CSS:
* {
padding: 0px;
margin: 0px;
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-weight: lighter;
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-image: url(../../media/grass.jpg);
/* Blurring the background. Applies behind the element... */
backdrop-filter: blur(9px);
background-size: cover;
}
@keyframes display_data {
0% {
transform: scale3d(0.25, 0, 1);
}
25% {
transform: scale3d(0.5, 0, 1);
}
50% {
transform: scale3d(0.1, 0, 1);
}
75% {
transform: scale3d(0.1, 1.2, 1);
}
100% {
transform: scale3d(1, 1, 1);
}
}
@keyframes display_month_days {
from {
opacity: 0%;
}
to {
opacity: 100%;
}
}
@keyframes display_button_back {
0% {
right: 25px;
transform: scale3d(0.75, 0.75, 1);
}
100% {
right: 0px;
transform: scale3d(1, 1, 1);
}
}
@keyframes display_button_next {
0% {
left: 25px;
transform: scale3d(0.75, 0.75, 1);
}
100% {
left: 0px;
transform: scale3d(1, 1, 1);
}
}
@keyframes display_opacity_zoom {
from {
opacity: 0%;
transform: scale3d(0.5, 0.5, 1);
}
to {
opacity: 100%;
transform: scale3d(1, 1, 1);
}
}
.main {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
color: white;
background-color: rgba(0, 0, 0, 0.65);
}
.title {
margin-top: 7%;
height: 80px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
animation: display_opacity_zoom 1s ease-out;
}
.year_title {
margin-left: 5px;
font-size: 40px;
letter-spacing: 5px;
color: lightsalmon;
text-align: center;
}
.month_title {
margin-left: 15px;
font-size: 25px;
letter-spacing: 15px;
text-align: center;
}
.calendar {
height: 75%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.month_days {
margin-top: 10px;
width: 100%;
height: 50%;
animation: display_month_days 1s ease-in-out;
}
table {
margin-top: 20px;
width: 100%;
font-size: 22px;
}
tr,
th,
td {
background-color: none;
}
th {
width: 14%;
text-align: center;
color: white;
}
td {
width: 2.38em;
height: 2.38em;
color: white;
text-align: center;
border-radius: 50%;
}
td:hover {
background-color: black;
}
.data {
display: flex;
justify-content: center;
align-items: center;
width: 95%;
height: 30%;
background-color: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 5px;
animation: display_data 0.8s ease;
}
.data_content {
width: 95%;
height: 95%;
}
.other_month {
background: none;
color: rgba(175, 175, 175, 0.45);
}
.buttons {
width: 100vw;
height: 70px;
display: flex;
justify-content: space-around;
align-items: flex-start;
}
button {
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
background: none;
border: none;
font-size: 35px;
font-weight: 400;
color: white;
}
JS:
// Returns the day of the week in which the month starts.
starting_day = (year, month) => new Date(year, month, 1).getDay();
// Returns the amount of days in a month.
total_month_days = (year, month) => new Date(year, month + 1, 0).getDate();
// Set the background color of the cell that contains today's day.
function is_today(months, cell) {
if (
new Date().getFullYear() ==
parseInt(document.getElementById("year_title").textContent) &&
months[new Date().getMonth()].month ==
document.getElementById("month_title").textContent &&
cell.textContent == new Date().getDate()
) {
cell.style.background = "rgba(255, 160, 122, 0.8)";
}
}
// Changes color if it is a weekend.
color_weekend = (let_value, cell) => {
if (let_value == 6 || let_value == 0) cell.style.color = "lightsalmon";
};
// Populates the DB.
function load_DB(year, month) {
let counter = 1;
const table = document.getElementById("days");
const date_object = new Date(year, month);
// Array containing months names, starting_day()_+ total_month_days().
const months = [
{
// Month 0
month: "January",
first_day: starting_day(date_object.getFullYear(), 0),
days: Array(total_month_days(date_object.getFullYear(), 0)),
},
{
// Month 1
month: "February",
first_day: starting_day(date_object.getFullYear(), 1),
days: Array(total_month_days(date_object.getFullYear(), 1)),
},
{
// Month 2
month: "March",
first_day: starting_day(date_object.getFullYear(), 2),
days: Array(total_month_days(date_object.getFullYear(), 2)),
},
{
// Month 3
month: "April",
first_day: starting_day(date_object.getFullYear(), 3),
days: Array(total_month_days(date_object.getFullYear(), 3)),
},
{
// Month 4
month: "May",
first_day: starting_day(date_object.getFullYear(), 4),
days: Array(total_month_days(date_object.getFullYear(), 4)),
},
{
// Month 5
month: "June",
first_day: starting_day(date_object.getFullYear(), 5),
days: Array(total_month_days(date_object.getFullYear(), 5)),
},
{
// Month 6
month: "July",
first_day: starting_day(date_object.getFullYear(), 6),
days: Array(total_month_days(date_object.getFullYear(), 6)),
},
{
// Month 7
month: "August",
first_day: starting_day(date_object.getFullYear(), 7),
days: Array(total_month_days(date_object.getFullYear(), 7)),
},
{
// Month 8
month: "September",
first_day: starting_day(date_object.getFullYear(), 8),
days: Array(total_month_days(date_object.getFullYear(), 8)),
},
{
// Month 9
month: "October",
first_day: starting_day(date_object.getFullYear(), 9),
days: Array(total_month_days(date_object.getFullYear(), 9)),
},
{
// Month 10
month: "November",
first_day: starting_day(date_object.getFullYear(), 10),
days: Array(total_month_days(date_object.getFullYear(), 10)),
},
{
// Month 11
month: "December",
first_day: starting_day(date_object.getFullYear(), 11),
days: Array(total_month_days(date_object.getFullYear(), 11)),
},
];
// Prints the name of the current month and year.
document.getElementById("year_title").textContent = date_object.getFullYear();
document.getElementById("month_title").textContent =
months[date_object.getMonth()].month;
// Month days that will appear in the first row.
const number_of_cells_in_the_first_row =
7 - months[date_object.getMonth()].first_day;
// Month days that will appear after the first row.
let normal_days = number_of_cells_in_the_first_row + 1;
// Creates + populates the 5 last rows.
for (let r = 0; r < 5; r++) {
let row = table.insertRow(r + 1);
let cell;
// Creates + populates 7 cells in each row.
for (let x = 0; x < 7; x++) {
cell = row.insertCell(x);
cell.textContent = normal_days;
is_today(months, cell);
color_weekend(x, cell);
normal_days++;
// Filling the rest of the table with the days of the next month(gray days).
if (normal_days > months[date_object.getMonth()].days.length + 1) {
// Next month days are always lowlighted.
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
cell.textContent = counter++;
cell.className = "other_month";
}
}
}
// Creates + populates the 1 row.
for (let i = 0; i < 1; i++) {
let row = table.insertRow(i + 1);
let cell;
let number_of_blank_cells = 7 - number_of_cells_in_the_first_row;
// Populates it.
for (let y = 0; y < number_of_cells_in_the_first_row; y++) {
cell = row.insertCell(0);
cell.textContent = number_of_cells_in_the_first_row - y;
is_today(months, cell);
color_weekend(y, cell);
}
// Filling the rest of the table (next month days).
for (let w = 0; w < number_of_blank_cells; w++) {
cell = row.insertCell(0);
if (months[date_object.getMonth() - 1]) {
cell.textContent = months[date_object.getMonth() - 1].days.length--;
cell.className = "other_month";
} else {
cell.textContent = months[date_object.getMonth() + 11].days.length--;
cell.className = "other_month";
}
}
}
}
// Converts the month name to the month number (January = 0).
function identify_month(string) {
const all_months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
let i = 0;
while (string != all_months[i]) {
i++;
}
return i;
}
// Advancecs a month.
function advance_month(year, month) {
// Cleaning table.
const table = document.getElementById("days");
for (let i = 0; i < 6; i++) {
table.deleteRow(1);
}
// Add new data.
month++;
load_DB(year, month);
}
// Retreats a month.
function retreat_month(year, month) {
// Cleaning table.
const table = document.getElementById("days");
for (let i = 0; i < 6; i++) {
table.deleteRow(1);
}
month--; // Add new data.
load_DB(year, month);
}
Yanıtlar
Kod İncelemesine hoş geldiniz. CSS oldukça iyi görünüyor. Renkli lightsalmon😃 çok fazla stil sayfası görmedim . Aşağıda bazı öneriler bulunmaktadır.
DOM referanslarını önbelleğe al
"... DOM erişimi aslında oldukça maliyetlidir - sanki bir köprüm varmış gibi düşünüyorum - paralı köprülü iki arazi parçası gibi ve JavaScript motoru bir tarafta, DOM ise diğer tarafta ve DOM'a JavaScript motorundan her erişmek istediğimde, bu geçiş ücretini ödemek zorundayım ”
- John Hrvatin, Microsoft, MIX09, bu konuşmada 29: 38'de Yüksek Performanslı Web Uygulamaları ve Siteleri Oluşturma, ayrıca O'Reilly'de de bahsedilmiştir. JavaScript Nicholas C Zakas Pg 36 kadar kitap sıra belirtildiği gibi, bu yazı
10 yıldan daha uzun bir süre önce belirtildiği ve tarayıcıların muhtemelen o zamandan beri yol katettiği akılda tutulursa, bunun hala farkında olmak akıllıca olacaktır. DOM öğelerine referansları depolayın - örneğin document.getElementById("year_title")değişkenlerde - bu, DOMContentLoadedolaya yapılan geri aramada olabilir .
let titleEl;
window.addEventListener('DOMContentLoaded', (event) => {
titleEl = document.getElementById("year_title");
});
Joshua'nın bahsettiği gibi, HTML'deki olay işleyicileri bugünün standartlarına uygun görülmüyor ve bu geri çağırma, kodun şu anda onloadbody öğesinin özniteliğinde bulunması için uygun bir yer olacaktır .
ay isimleri
Joshua'nın daha önce bahsettiği gibi, aylar için kod oldukça tekrarlı. Ay adlarını depolamak yerine Date.prototype.toLocaleDateString(), ay adlarını dinamik olarak, muhtemelen kullanıcı tarafından belirlenen bir dilde ve / veya navigator.languagemevcutsa oluşturmak için kullanılabilir.
Daha okunaklı işlev adı
// Set the background color of the cell that contains today's day. function is_today(months, cell) {
İsme göre değerlendirildiğinde, fonksiyonun bugün bir değer olup olmadığını gösteren bir Boolean döndüreceğini tahmin ediyorum, ancak yorum ve uygulama verilse de durum böyle değil. Daha uygun bir isim olabilir style_todays_date_cell.
Kesin eşitlik
Katı karşılaştırma operatörleri kullanmak iyi bir alışkanlıktır - yani ===ve !==mümkün olduğunda - örneğin bu karşılaştırma için load_DB():
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
Ön düzeltme artışı
Bu satırlar:
month++; load_DB(year, month);
Ön ek artırma operatörümonth kullanılarak çağrıdan önce değeri artırıldığında birleştirilebilir :
load_DB(year, ++month);
yedekli satır içi stiller -> CSS
Düğmelerin içindeki görüntülerin satır içi stilleri vardır.
style="width: 35px; height: 35px"
Bu, stil sayfasına taşınabilir:
button img {
width: 35px;
height: 35px;
}
Tablo başlıkları için satır içi stiller
İlk ve son hücrelerin varsayılan thkuralı geçersiz kılmak için satır içi stilleri vardır :
<th style="color: lightsalmon">Sun</th>
Bu, daha özel seçiciler kullanılarak stil sayfasına da taşınabilir - örneğin sözde seçiciler :first-childve :last-child:
th:first-child,
th:last-child {
color: lightsalmon;
}
Alternatifler şunları içerir :first-of-typeve :last-of-typeBu teknik, satırı JavaScript'in dışına ve stil sayfasına taşımak için aşağıdakilerin tdyerine kullanılabilir tr:
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
Not: Cevap IPhone VII + kullanılarak oluşturuldu
Değişkenler
Değişkeninizi ilk tanımlarken her zaman bir değişken bildirimi kullanın. Yapmazsanız değişkeniniz küresel kapsamda yer alacaktır , ki bu neredeyse hiçbir zaman istediğiniz şey değildir. Ayrıca, zaten var olan bir global değişkene yeniden atamak istemediğinizi daha net hale getirir.
Yani için starting_day, örneğin, bir koyun var, letya da constonun önünde.
HTML Nitelikleri
Olay işleme onclickveya onloadyapmak gibi öznitelikler kullanmak yerine (bunlar satır içi işleyiciler olarak da bilinir), genellikle bu olayları JavaScript'inizde işlemek en iyi uygulama olarak kabul edilir. HTML / JavaScript'inizi ayırmanıza yardımcı olur.
Örneğin, niteliği değiştirmek için onloadbunun yerine aşağıdaki JS işlevini kullanabilirsiniz:
function ready(func) {
if (document.readyState !== 'loading') {
func();
} else {
document.addEventListener('DOMContentLoaded', func);
}
}
mainÖrneğin, DOM üzerinde çalışılmaya hazır olduğunda çalıştırmak istediğiniz herhangi bir kodu koyacağınız ve ardından çağıracağınız bir işlevi bildirebilirsiniz ready(main);.
onclickÖznitelikleri değiştirmek için , JavaScript kodunda seçici olarak kullanılmak üzere bunlara bazı CSS sınıfları / kimlikleri eklemelisiniz. Daha sonra bunları gibi bir şey kullanarak seçersiniz const backButton = document.querySelector('calendarBack');. Ardından, bu öğeye şu şekilde bir olay dinleyicisi ekleyebilirsiniz :
backButton.addEventListener("click", () => {
// The code you want to run when the button is clicked goes here
});
Kod Yeniden Düzenlemeleri
months
monthsDeğişken bildirim oldukça tekrarlayan ve her ayın bunun, 'endeksi' için değişen tek şey vardır. Bu, onu bir döngü / dizi haritasına girmek için mükemmel bir aday yapar.
Bunu şu mapişlevi kullanarak yapardım :
// This would ideally not be in load_DB but be a top level declaration, since you could then reference this again in the identify_month function instead of rewriting the months
const monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
// Saving this in a variable for brevity and since it doesn't change
const fullYear = date_object.getFullYear();
const months = monthNames.map((monthName, index) => {
return {
month: monthName,
first_day: starting_day(fullYear, index),
days: Array(total_month_days(fullYear, index)),
};
});
Emin olmadığım bir şey, daysözellik ve neden başlamak için bir dizi olduğu. Kodda referans verildiği tek yer uzunluğunu kontrol etmektir. Bu bahsettiğiniz bitmemiş kodun bir parçası mı? Değilse, satırı sadece günlerle değiştirirdim: Array(total_month_days(fullYear, index)),bunun yerine.
identify_month
"Ayın dizinini monthNamesdiziden al" demenin uzun bir yolu olsa da, bu işlevin orijinal uygulaması çalışmalıdır .
İşte onu nasıl yeniden yazacağım:
// Converts the month name to the month number (January = 0).
function identify_month(string) {
return monthNames.indexOf(string);
}
Geçerli ay kodu
monthsDizide bulunan ay nesnesinin tekrarlanan aramaları nedeniyle orijinal kodun okunması oldukça zordur . Okunabilirliği artırmak ve gereksiz ek dizi erişimlerini kaldırmak için bunları bir değişkene yerleştirin.
const currentMonth = date_object.getMonth();
const currentMonthObj = months[currentMonth];
// Prints the name of the current month and year.
document.getElementById("year_title").textContent = fullYear;
document.getElementById("month_title").textContent = currentMonthObj.month;
// Month days that will appear in the first row.
const number_of_cells_in_the_first_row = 7 - currentMonthObj.first_day;
Daha sonra geri dönüp düşüncelerimi daha eklemeye çalışacağım :)