feat(frontend): adds animations

This commit is contained in:
itsscb 2024-08-30 15:53:43 +02:00
parent f7d1adc198
commit 684b7917f3
3 changed files with 329 additions and 249 deletions

View File

@ -1,14 +1,24 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8"> <head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8">
<title>wordl</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link data-trunk href="./public/styles.css" rel="css"> <title>wordl</title>
<!-- <link data-trunk href="./assets/favicon.ico" rel="icon" type="image/x-icon"> --> <link data-trunk href="./public/styles.css" rel="css">
<link data-trunk rel="copy-dir" href="public" > <!-- <link data-trunk href="./assets/favicon.ico" rel="icon" type="image/x-icon"> -->
<link rel="manifest" href="public/manifest.json" /> <link data-trunk rel="copy-dir" href="public">
</head> <link rel="manifest" href="public/manifest.json" />
<body class="bg-black text-white"> </head>
</body>
</html> <body class="bg-black text-white">
<div class="h-screen flex justify-center items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 rotate-ease" viewBox="0 -960 960 960" fill="white">
<path
d="M320-160h320v-120q0-66-47-113t-113-47q-66 0-113 47t-47 113v120Zm160-360q66 0 113-47t47-113v-120H320v120q0 66 47 113t113 47ZM160-80v-80h80v-120q0-61 28.5-114.5T348-480q-51-32-79.5-85.5T240-680v-120h-80v-80h640v80h-80v120q0 61-28.5 114.5T612-480q51 32 79.5 85.5T720-280v120h80v80H160Zm320-80Zm0-640Z" />
</svg>
<p>Loading...</p>
</div>
</body>
</html>

View File

@ -2,13 +2,53 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
.rotate-box {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
.rotate-ease {
animation: rotate-ease 2s ease-in-out infinite;
/* Total duration of 4 seconds */
}
@keyframes rotate-ease {
0% {
transform: rotate(0deg);
}
45% {
transform: rotate(180deg);
/* Rotate to 180 degrees */
}
55% {
transform: rotate(180deg);
/* Hold at 180 degrees */
}
100% {
transform: rotate(360deg);
/* Rotate to 360 degrees */
}
}
.logo { .logo {
@apply absolute; @apply absolute;
} }
.logo-fadein-right { .logo-fadein-right {
position:relative; position: relative;
animation: logo-fadein-right 1s ; animation: logo-fadein-right 1s;
} }
.validation-error { .validation-error {
@ -20,17 +60,21 @@
transform: translateX(-200%); transform: translateX(-200%);
opacity: 0; opacity: 0;
} }
70%{
70% {
transform: translateX(0); transform: translateX(0);
opacity: 0.7; opacity: 0.7;
} }
80%{
80% {
opacity: 1; opacity: 1;
} }
90%{
90% {
opacity: 0.8; opacity: 0.8;
} }
100%{
100% {
opacity: 1; opacity: 1;
} }
} }
@ -46,75 +90,96 @@
transform: translateX(500%); transform: translateX(500%);
opacity: 0; opacity: 0;
} }
70%{
70% {
opacity: 0.7; opacity: 0.7;
} }
80%{
80% {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
} }
90%{
90% {
opacity: 0.8; opacity: 0.8;
} }
100%{
100% {
opacity: 1; opacity: 1;
} }
} }
@keyframes logo-fadein-left { @keyframes logo-fadein-left {
from{ from {
left: -200%; left: -200%;
opacity: 0.5; opacity: 0.5;
} }
to{
to {
left: 0; left: 0;
opacity: 1; opacity: 1;
} }
} }
.fade-in{ .fade-in {
position:relative; position: relative;
animation:fadein 0.4s} animation: fadein 0.4s
@keyframes fadein{
from{
left:200%;
opacity:0;
} to{
left:0;
opacity:1}
} }
.fade-out{ @keyframes fadein {
position:relative; from {
animation:fadeout 0.4s} left: 200%;
@keyframes fadeout{ opacity: 0;
from{ }
left:0;
opacity:0 to {
} to{ left: 0;
left:-200%; opacity: 1
opacity:1} }
}
.fade-out {
position: relative;
animation: fadeout 0.4s
}
@keyframes fadeout {
from {
left: 0;
opacity: 0
}
to {
left: -200%;
opacity: 1
}
} }
.mail-animation { .mail-animation {
position:relative; position: relative;
animation:mail-animation 2s infinite; animation: mail-animation 2s infinite;
} }
@keyframes mail-animation { @keyframes mail-animation {
0% { 0% {
left: -20%; left: -20%;
opacity: 0.5; opacity: 0.5;
} }
20% { 20% {
opacity: 1; opacity: 1;
} }
70%{
70% {
left: 20%; left: 20%;
} }
80%{
80% {
opacity: 1; opacity: 1;
} }
100%{
100% {
left: 100%; left: 100%;
opacity: 0; opacity: 0;
} }
@ -125,24 +190,24 @@ opacity:1}
} }
.cx-date-picker[type="date"]::-webkit-calendar-picker-indicator { .cx-date-picker[type="date"]::-webkit-calendar-picker-indicator {
background: transparent; background: transparent;
bottom: 0; bottom: 0;
color: transparent; color: transparent;
cursor: pointer; cursor: pointer;
height: auto; height: auto;
left: 0; left: 0;
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
width: auto; width: auto;
} }
.text-danger { .text-danger {
color: rgba(255, 82, 82,1.0); color: rgba(255, 82, 82, 1.0);
} }
.bg-danger { .bg-danger {
background-color: rgba(255, 82, 82,1.0); background-color: rgba(255, 82, 82, 1.0);
} }
.min-height { .min-height {
@ -150,53 +215,53 @@ opacity:1}
} }
.text-tertiary { .text-tertiary {
color: rgba(132, 129, 122,1.0); color: rgba(132, 129, 122, 1.0);
} }
.bg-tertriary { .bg-tertriary {
background-color: rgba(132, 129, 122,1.0); background-color: rgba(132, 129, 122, 1.0);
} }
.border-primary { .border-primary {
border-color: rgba(51,217,178,1.0); border-color: rgba(51, 217, 178, 1.0);
} }
.border-error { .border-error {
border-color: rgba(255, 82, 82,1.0) !important; border-color: rgba(255, 82, 82, 1.0) !important;
} }
.accent-primary { .accent-primary {
accent-color: rgba(51, 217, 178,1.0); accent-color: rgba(51, 217, 178, 1.0);
} }
.text-primary { .text-primary {
color: rgba(51, 217, 178,1.0); color: rgba(51, 217, 178, 1.0);
} }
.text-primary-dark { .text-primary-dark {
color: rgba(33, 140, 116,1.0); color: rgba(33, 140, 116, 1.0);
} }
.bg-primary-dark { .bg-primary-dark {
background-color: rgba(33, 140, 116,1.0); background-color: rgba(33, 140, 116, 1.0);
} }
.bg-primary { .bg-primary {
background-color: rgba(51, 217, 178,1.0); background-color: rgba(51, 217, 178, 1.0);
} }
.text-secondary { .text-secondary {
color: rgba(52, 172, 224,1.0); color: rgba(52, 172, 224, 1.0);
} }
.text-secondary-dark { .text-secondary-dark {
color: rgba(34, 112, 147,1.0); color: rgba(34, 112, 147, 1.0);
} }
.bg-secondary-dark { .bg-secondary-dark {
background-color: rgba(34, 112, 147,1.0); background-color: rgba(34, 112, 147, 1.0);
} }
.bg-secondary { .bg-secondary {
background-color: rgba(52, 172, 224,1.0); background-color: rgba(52, 172, 224, 1.0);
} }

View File

@ -363,202 +363,207 @@ pub fn Home() -> Html {
) )
} }
> >
<div if *loading {
class={ <svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 rotate-ease" viewBox="0 -960 960 960" fill="white">
classes!( <path d="M320-160h320v-120q0-66-47-113t-113-47q-66 0-113 47t-47 113v120Zm160-360q66 0 113-47t47-113v-120H320v120q0 66 47 113t113 47ZM160-80v-80h80v-120q0-61 28.5-114.5T348-480q-51-32-79.5-85.5T240-680v-120h-80v-80h640v80h-80v120q0 61-28.5 114.5T612-480q51 32 79.5 85.5T720-280v120h80v80H160Zm320-80Zm0-640Z"/>
"h-5/6", </svg>
"flex", <p>{"Loading..."}</p>
"flex-col", } else {
"items-center",
"pt-12",
)
}
>
<div class={ <div
classes!( class={
"mb-12", classes!(
)}> "h-5/6",
{ for submitted_words.iter().map(|e| {string_to_html(e)})} "flex",
</div> "flex-col",
<form "items-center",
class="mb-4" "pt-12",
)
}
> >
<div
class={ <div class={
classes!( classes!(
"flex", "mb-12",
"flex-row", )}>
"font-bold", { for submitted_words.iter().map(|e| {string_to_html(e)})}
"text-lg", </div>
"gap-4", <form
) class="mb-4"
}
> >
{ <div
if *loading { class={
html!(<p>{"Loading..."}</p>) classes!(
"flex",
"flex-row",
"font-bold",
"text-lg",
"gap-4",
)
} }
else if *game_over { >
{
let (text, color) = match *result { if *game_over {
GameResult::Win => {
("FOUND", "bg-green-600") let (text, color) = match *result {
}, GameResult::Win => {
GameResult::Lose => { ("FOUND", "bg-green-600")
("WANTED", "bg-red-600") },
} GameResult::Lose => {
}; ("WANTED", "bg-red-600")
html! ( }
<div> };
<h1>{ html! (
text <div>
}</h1> <h1>{
<ul text
class={ }</h1>
classes!( <ul
"flex",
"flex-row",
"gap-4",
"notranslate",
)
}
>
{
word.chars().map(|e|{
let text = e;
html!{
<li
class={ class={
classes!( classes!(
"flex", "flex",
"items-center" "flex-row",
"gap-4",
"notranslate",
) )
} }
> >
<span {
class={ word.chars().map(|e|{
classes!(
"w-16", let text = e;
"h-16", html!{
"text-center", <li
"py-4", class={
"font-bold", classes!(
"text-lg", "flex",
{color}, "items-center"
) )
} }
> >
{text} <span
</span>
</li>
}}).collect::<Html>()
}
</ul>
</div>
)
}
else if !*game_over {
node_refs.iter().enumerate().map(|(index, node_ref)| {
let on_focus = {
let curr_index = curr_index.clone();
Callback::from(move |e: FocusEvent| {
let target = e.target_unchecked_into::<web_sys::HtmlElement>();
if let Some(index) = target.get_attribute("tabindex") {
if let Ok(i) = index.parse::<usize>() {
curr_index.set(i);
}
}
})
};
html! {
<input
onkeyup={on_enter.clone()}
oninput={on_input.clone()}
tabindex={index.to_string()}
ref={node_ref.clone()}
value={input_values[index].clone()}
onfocus={on_focus.clone()}
class={ class={
classes!( classes!(
"w-16", "w-16",
"h-16", "h-16",
"text-center", "text-center",
"bg-gray-600" "py-4",
"font-bold",
"text-lg",
{color},
) )
} }
/> >
} {text}
}).collect::<Html>() </span>
} else { </li>
html!(<div></div>) }}).collect::<Html>()
} }
} </ul>
</div> </div>
</form> )
{
if *loading {
html!{<></>}
} else {
html!{
<div
class={
classes!(
"w-full",
"flex",
"justify-end",
)
}
>
<button
tabindex={(*length + 1).to_string()}
class={
classes!(
"w-24",
"h-16",
"text-2xl",
"font-bold",
"rounded-xl",
"flex",
"items-center",
"justify-center",
{if input_values.iter().any(std::string::String::is_empty) && !*game_over {"bg-gray-700"} else {"bg-green-600"}},
)
}
onclick={if input_values.iter().any(std::string::String::is_empty) && !*game_over {on_disabled} else {on_submit}} type="submit">
{
if *game_over {
html!{
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 -960 960 960" fill="white">
<path d="M480-80q-75 0-140.5-28.5t-114-77q-48.5-48.5-77-114T120-440h80q0 117 81.5 198.5T480-160q117 0 198.5-81.5T760-440q0-117-81.5-198.5T480-720h-6l62 62-56 58-160-160 160-160 56 58-62 62h6q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-440q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-80Z"/>
</svg>
}
} }
else if input_values.iter().any(std::string::String::is_empty) { else if !*game_over {
html!{ node_refs.iter().enumerate().map(|(index, node_ref)| {
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 -960 960 960" width="24px" fill="white"> let on_focus = {
<path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q54 0 104-17.5t92-50.5L228-676q-33 42-50.5 92T160-480q0 134 93 227t227 93Zm252-124q33-42 50.5-92T800-480q0-134-93-227t-227-93q-54 0-104 17.5T284-732l448 448Z"/> let curr_index = curr_index.clone();
</svg>
} Callback::from(move |e: FocusEvent| {
let target = e.target_unchecked_into::<web_sys::HtmlElement>();
if let Some(index) = target.get_attribute("tabindex") {
if let Ok(i) = index.parse::<usize>() {
curr_index.set(i);
}
}
})
};
html! {
<input
onkeyup={on_enter.clone()}
oninput={on_input.clone()}
tabindex={index.to_string()}
ref={node_ref.clone()}
value={input_values[index].clone()}
onfocus={on_focus.clone()}
class={
classes!(
"w-16",
"h-16",
"text-center",
"bg-gray-600"
)
}
/>
}
}).collect::<Html>()
} else { } else {
html!{ html!(<div></div>)
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 -960 960 960" fill="white">
<path d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
</svg>
}
} }
} }
</button>
</div> </div>
</form>
{
if *loading {
html!{<></>}
} else {
html!{
<div
class={
classes!(
"w-full",
"flex",
"justify-end",
)
}
>
<button
tabindex={(*length + 1).to_string()}
class={
classes!(
"w-24",
"h-16",
"text-2xl",
"font-bold",
"rounded-xl",
"flex",
"items-center",
"justify-center",
{if input_values.iter().any(std::string::String::is_empty) && !*game_over {"bg-gray-700"} else {"bg-green-600"}},
)
}
onclick={if input_values.iter().any(std::string::String::is_empty) && !*game_over {on_disabled} else {on_submit}} type="submit">
{
if *game_over {
html!{
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12 rotate-box" viewBox="0 -960 960 960" fill="white">
<path d="M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z"/>
</svg>
}
}
else if input_values.iter().any(std::string::String::is_empty) {
html!{
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 -960 960 960" width="24px" fill="white">
<path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q54 0 104-17.5t92-50.5L228-676q-33 42-50.5 92T160-480q0 134 93 227t227 93Zm252-124q33-42 50.5-92T800-480q0-134-93-227t-227-93q-54 0-104 17.5T284-732l448 448Z"/>
</svg>
}
} else {
html!{
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 -960 960 960" fill="white">
<path d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
</svg>
}
}
}
</button>
</div>
}
} }
} }
}
</div>
</div> }
</div> </div>
} }
} }