Skip to content

Commit

Permalink
feat: add submit animation
Browse files Browse the repository at this point in the history
  • Loading branch information
fbuireu committed Feb 29, 2024
1 parent 189ea82 commit c5ef739
Show file tree
Hide file tree
Showing 14 changed files with 567 additions and 220 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineConfig({
mdx(),
sitemap(),
react(),
million.vite({mode: 'react', server: true, auto: true}),
million.vite({ mode: 'react', server: true, auto: true }),
partytown({
config: {
forward: ['dataLayer.push'],
Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
"@fontsource-variable/nunito-sans": "^5.0.9",
"@fontsource/baskervville": "^5.0.18",
"@hookform/resolvers": "^3.3.4",
"@types/node": "^20.11.20",
"@types/react": "^18.2.58",
"@types/node": "^20.11.22",
"@types/react": "^18.2.60",
"@types/react-dom": "^18.2.19",
"astro": "^4.4.4",
"astro": "^4.4.6",
"gsap": "^3.12.5",
"markdown-it": "^14.0.0",
"million": "^3.0.3",
Expand All @@ -57,22 +57,22 @@
"react-globe.gl": "^2.27.2",
"react-google-recaptcha-v3": "^1.10.1",
"react-hook-form": "^7.50.1",
"react-router-dom": "^6.22.1",
"swiper": "^11.0.6",
"react-router-dom": "^6.22.2",
"swiper": "^11.0.7",
"three": "^0.161.0",
"typescript": "^5.3.3",
"zod": "^3.22.4"
},
"devDependencies": {
"@commitlint/cli": "^19.0.2",
"@commitlint/config-conventional": "^18.6.2",
"@commitlint/cli": "^19.0.3",
"@commitlint/config-conventional": "^19.0.3",
"@testing-library/react": "^14.2.1",
"@testing-library/react-hooks": "^8.0.1",
"@types/eslint": "^8.56.3",
"@types/eslint": "^8.56.5",
"@types/markdown-it": "^13.0.7",
"@types/three": "^0.161.2",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-astro": "^0.31.4",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Testimonials from '@components/organisms/testimonials/Testimonials.astro'
import MyWork from '@components/organisms/myWork/MyWork.astro';
import LatestArticles from '@components/organisms/latestArticles/LatestArticles.astro';
// current send button
// current: contact intro
// todo: responsive
// todo: add resume (PDF) in about?
// todo: add calendly (https://www.kelseyohalloran.com/chat) + form in contact
Expand Down
48 changes: 31 additions & 17 deletions src/ui/components/organisms/contactForm/ContactForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
Expand All @@ -7,6 +7,7 @@ import './contact-form.css';
import { autosize } from '@components/organisms/contactForm/utils/autosize';
import { CONTACT_FORM_REQUEST_PARAMETERS } from 'src/consts.ts';
import { encode } from '@components/organisms/contactForm/utils/encode';
import { flyPlane } from '@components/organisms/contactForm/utils/flyPlane';

const schema = z.object({
name: z.string().trim().min(1, 'Please insert your name'),
Expand Down Expand Up @@ -40,6 +41,7 @@ export const ContactForm = () => {
});
const [formStatus, setFormStatus] = useState<FormStatus>(FormStatus.INITIAL);
const { executeRecaptcha } = useGoogleReCaptcha();
const submitRef = useRef<HTMLButtonElement>(null);

const verifyRecaptcha = useCallback(
async (data: FormData, event: React.FormEvent<HTMLFormElement>) => {
Expand All @@ -60,21 +62,28 @@ export const ContactForm = () => {

const submitForm = useCallback(async (data: FormData, event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
setFormStatus(FormStatus.LOADING);
const requestParams: RequestInit = {
...CONTACT_FORM_REQUEST_PARAMETERS,
body: encode({ ...data }),
};

const requestParams: RequestInit = {
...CONTACT_FORM_REQUEST_PARAMETERS,
body: encode({ ...data }),
};
setFormStatus(FormStatus.LOADING);
fetch(`/`, requestParams)
.then(() => {
reset();
setFormStatus(FormStatus.SUCCESS);
})
.catch((error) => {
console.log(error);
setFormStatus(FormStatus.ERROR);
});
const response = await fetch(`/`, requestParams);

if (response.ok) {
flyPlane(submitRef.current!);
setTimeout(() => {
setFormStatus(FormStatus.SUCCESS);
reset();
}, 2000);
} else {
throw new Error('Failed to submit form');
}
} catch (error) {
console.error(error);
setFormStatus(FormStatus.ERROR);
}
}, []);

return (
Expand Down Expand Up @@ -142,10 +151,15 @@ export const ContactForm = () => {
<p className="contact-form__recaptcha__error-message">{errors.recaptcha.message}</p>
)}
</div>
<button type="submit">Enviar</button>
<button ref={submitRef} className="contact-form__submit plane clickable" type="submit">
<span>Send email</span>
<div className="plane__left-wing" />
<div className="plane__right-wing" />
<span></span>
</button>
</form>
) : (
<h4>Form sent correctly! Will be in touch soon</h4>
<h4 className="contact-form__success-message">Form sent correctly! Will be in touch soon</h4>
)}
</>
);
Expand Down
120 changes: 118 additions & 2 deletions src/ui/components/organisms/contactForm/contact-form.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@
}

.contact-form__textarea {
min-height: 4rem;
resize: vertical;
text-align: start;
width: 100%;
/*form-sizing: content;*/
z-index: 10;

&:is(:focus, :not(:placeholder-shown), :focus-within) + .contact-form__textarea-label {
font-size: 1rem;
Expand Down Expand Up @@ -129,4 +128,121 @@
.contact-form__recaptcha-wrapper {
width: 100%;
}

.contact-form__success-message {
text-align: center;
}

.contact-form__submit {
--text-opacity: 1;
--overflow: hidden;
--rotate: 0;
--plane-x: 0;
--plane-y: 0;
--plane-opacity: 1;
--left-wing-background: var(--neutral-main);
--left-wing-first-x: 0;
--left-wing-first-y: 0;
--left-wing-second-x: 50;
--left-wing-second-y: 0;
--left-wing-third-x: 0;
--left-wing-third-y: 100;
--left-body-background: var(--neutral-main);
--left-body-first-x: 50;
--left-body-first-y: 0;
--left-body-second-x: 50;
--left-body-second-y: 100;
--left-body-third-x: 0;
--left-body-third-y: 100;
--right-wing-background: var(--neutral-main);
--right-wing-first-x: 50;
--right-wing-first-y: 0;
--right-wing-second-x: 100;
--right-wing-second-y: 0;
--right-wing-third-x: 100;
--right-wing-third-y: 100;
--right-body-background: var(--neutral-main);
--right-body-first-x: 50;
--right-body-first-y: 0;
--right-body-second-x: 50;
--right-body-second-y: 100;
--right-body-third-x: 100;
--right-body-third-y: 100;
color: var(--white);
display: block;
min-width: 150px;
padding: 0.75rem 0;
position: relative;
-webkit-tap-highlight-color: transparent;
text-align: center;
transform: rotate(calc(var(--rotate) * 1deg)) translateZ(0);
}

.contact-form__submit .plane__left-wing,
.contact-form__submit .plane__right-wing {
inset: 0;
opacity: var(--plane-opacity);
position: absolute;
transform: translate(calc(var(--plane-x) * 1px), calc(var(--plane-y) * 1px)) translateZ(0);

&::after,
&::before {
background: var(--background, var(--left-wing-background));
clip-path: polygon(
calc(var(--first-x, var(--left-wing-first-x)) * 1%) calc(var(--first-y, var(--left-wing-first-y)) * 1%),
calc(var(--second-x, var(--left-wing-second-x)) * 1%)
calc(var(--second-y, var(--left-wing-second-y)) * 1%),
calc(var(--third-x, var(--left-wing-third-x)) * 1%) calc(var(--third-y, var(--left-wing-third-y)) * 1%)
);
content: '';
inset: 0;
position: absolute;
transform: translate(var(--inner-x, 0.4%), var(--inner-y, 0)) translateZ(0);
z-index: var(--z-index, 2);
}
}

.plane__left-wing:after {
--inner-x: 0;
--z-index: 1;
--background: var(--left-body-background);
--first-x: var(--left-body-first-x);
--first-y: var(--left-body-first-y);
--second-x: var(--left-body-second-x);
--second-y: var(--left-body-second-y);
--third-x: var(--left-body-third-x);
--third-y: var(--left-body-third-y);
}

.plane__right-wing:before {
--inner-x: -0.4%;
--z-index: 2;
--background: var(--right-wing-background);
--first-x: var(--right-wing-first-x);
--first-y: var(--right-wing-first-y);
--second-x: var(--right-wing-second-x);
--second-y: var(--right-wing-second-y);
--third-x: var(--right-wing-third-x);
--third-y: var(--right-wing-third-y);
}

.plane__right-wing:after {
--inner-x: 0;
--z-index: 1;
--background: var(--right-body-background);
--first-x: var(--right-body-first-x);
--first-y: var(--right-body-first-y);
--second-x: var(--right-body-second-x);
--second-y: var(--right-body-second-y);
--third-x: var(--right-body-third-x);
--third-y: var(--right-body-third-y);
}

.contact-form__submit span {
display: block;
opacity: var(--text-opacity);
position: relative;
text-shadow: 0 0 0 var(--white);
z-index: 1;
}
}
93 changes: 93 additions & 0 deletions src/ui/components/organisms/contactForm/utils/flyPlane/flyPlane.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { gsap } from 'gsap';

export const flyPlane = (button: HTMLButtonElement) => {
if (!button.classList.contains('--is-active')) {
button.classList.add('--is-active');

const getPropertyValue = (variable: string) => window.getComputedStyle(button).getPropertyValue(variable);

gsap.timeline().to(button, {
keyframes: [
{
'--left-wing-first-x': 50,
'--left-wing-first-y': 100,
'--right-wing-second-x': 50,
'--right-wing-second-y': 100,
duration: 0.5,
onComplete: () => {
gsap.set(button, {
'--left-wing-first-y': 0,
'--left-wing-second-x': 40,
'--left-wing-second-y': 100,
'--left-wing-third-x': 0,
'--left-wing-third-y': 100,
'--left-body-third-x': 40,
'--right-wing-first-x': 50,
'--right-wing-first-y': 0,
'--right-wing-second-x': 60,
'--right-wing-second-y': 100,
'--right-wing-third-x': 100,
'--right-wing-third-y': 100,
'--right-body-third-x': 60,
});
},
},
{
'--left-wing-third-x': 20,
'--left-wing-third-y': 90,
'--left-wing-second-y': 90,
'--left-body-third-y': 90,
'--right-wing-third-x': 80,
'--right-wing-third-y': 90,
'--right-body-third-y': 90,
'--right-wing-second-y': 90,
duration: 0.2,
},
{
'--rotate': 50,
'--left-wing-third-y': 95,
'--left-wing-third-x': 27,
'--right-body-third-x': 45,
'--right-wing-second-x': 45,
'--right-wing-third-x': 60,
'--right-wing-third-y': 83,
duration: 0.25,
},
{
'--rotate': 55,
'--plane-x': -8,
'--plane-y': 24,
duration: 0.2,
},
{
'--rotate': 40,
'--plane-x': 45,
'--plane-y': -180,
'--plane-opacity': 0,
duration: 0.3,
},
],
});

gsap.to(button, {
keyframes: [
{
'--text-opacity': 0,
'--left-wing-background': getPropertyValue('--primary-dark-2'),
'--right-wing-background': getPropertyValue('--primary-dark-2'),
duration: 0.2,
},
{
'--left-wing-background': getPropertyValue('--neutral-main'),
'--right-wing-background': getPropertyValue('--neutral-main'),
duration: 0.1,
},
{
'--left-body-background': getPropertyValue('--primary-dark-1'),
'--right-body-background': getPropertyValue('--primary-dark-2'),
duration: 0.4,
},
],
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './flyPlane.ts';
3 changes: 1 addition & 2 deletions src/ui/components/organisms/siteHead/SiteHead.astro
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props as
<script
type="text/partytown"
src=`https://www.googletagmanager.com/gtag/js?id=${import.meta.env.PUBLIC_GOOGLE_ANALYTICS_ID}`
>
</script>
></script>
<script type="text/partytown">
window.dataLayer = window.dataLayer || [];

Expand Down
Loading

0 comments on commit c5ef739

Please sign in to comment.