implements brevo form, webfonts sh
This commit is contained in:
parent
bad6faf555
commit
8e2a5ed8bf
14 changed files with 220 additions and 71 deletions
|
|
@ -3,7 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<link rel="stylesheet" href="https://use.typekit.net/ltu2cxf.css">
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/Stratos-Regular.woff2" crossorigin>
|
||||||
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/Stratos-BoldItalic.woff2" crossorigin>
|
||||||
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/PPFormula-Medium.woff2" crossorigin>
|
||||||
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/PPFormula-Extrabold.woff2" crossorigin>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
:root {
|
:root {
|
||||||
--spacing-outer: 5vw;
|
--spacing-outer: 5vw;
|
||||||
--spacing-nav: 5vw;
|
--spacing-nav: 5vw;
|
||||||
// --color-bg: hsl(202, 58%, 39%);
|
--color-bg: rgb(207, 63, 63);
|
||||||
--color-bg: hsl(0, 60%, 53%);
|
--color-text: rgb(255, 234, 217);
|
||||||
--color-text: #ffead9;
|
--color-highlight: rgb(29, 12, 18);
|
||||||
--color-highlight: #1d0c12;
|
|
||||||
// --color-bg: #000000;
|
|
||||||
// --color-text: #FFFFFF;
|
|
||||||
// --color-highlight: #FF4D00;
|
|
||||||
--aspect-ratio-heroes: 1.5;
|
--aspect-ratio-heroes: 1.5;
|
||||||
--font-size-p: clamp(20px, 1.6vw, 1.6vw);
|
--font-size-p: clamp(20px, 1.6vw, 1.6vw);
|
||||||
|
|
||||||
|
|
@ -17,6 +13,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Stratos';
|
||||||
|
src: url('/fonts/Stratos-Regular.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Stratos';
|
||||||
|
src: url('/fonts/Stratos-BoldItalic.woff2') format('woff2');
|
||||||
|
font-weight: bold;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
a:hover, input:hover, button:hover {
|
a:hover, input:hover, button:hover {
|
||||||
cursor: url('/pointer.svg'), auto;
|
cursor: url('/pointer.svg'), auto;
|
||||||
}
|
}
|
||||||
|
|
@ -32,18 +42,6 @@ body {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
// body:after {
|
|
||||||
// content: '';
|
|
||||||
// display: block;
|
|
||||||
// position: fixed;
|
|
||||||
// top: 0;
|
|
||||||
// left: 0;
|
|
||||||
// width: calc(100vw - 00px);
|
|
||||||
// height: calc(100svh - 30px);
|
|
||||||
// border: 15px solid var(--color-text);
|
|
||||||
// z-index: 1;
|
|
||||||
// pointer-events: none;
|
|
||||||
// }
|
|
||||||
body * {
|
body * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import * as PIXI from 'pixi.js';
|
import * as PIXI from 'pixi.js';
|
||||||
|
|
||||||
export default function createCanvasText( element: HTMLImageElement, stage: PIXI.Container ){
|
export default function createCanvasText(
|
||||||
|
element: HTMLImageElement,
|
||||||
|
stage: PIXI.Container,
|
||||||
|
spriteObject: PIXI.Sprite | null = null
|
||||||
|
){
|
||||||
|
|
||||||
const elem = element;
|
const elem = element;
|
||||||
const elemSrc = elem.currentSrc || elem.src;
|
const elemSrc = elem.currentSrc || elem.src;
|
||||||
|
|
@ -11,10 +15,23 @@ export default function createCanvasText( element: HTMLImageElement, stage: PIX
|
||||||
if (elemSrc.includes('.svg')) {
|
if (elemSrc.includes('.svg')) {
|
||||||
scalefactor = Number(elem.attributes.getNamedItem('data-svgscale')?.value);
|
scalefactor = Number(elem.attributes.getNamedItem('data-svgscale')?.value);
|
||||||
const canvasImgTexture = PIXI.Texture.from(elemSrc, { resourceOptions: { scale: scalefactor } });
|
const canvasImgTexture = PIXI.Texture.from(elemSrc, { resourceOptions: { scale: scalefactor } });
|
||||||
|
|
||||||
|
// Use provided sprite object or create a new one
|
||||||
|
if (spriteObject) {
|
||||||
|
spriteObject.texture = canvasImgTexture;
|
||||||
|
canvasImg = spriteObject;
|
||||||
|
} else {
|
||||||
canvasImg = new PIXI.Sprite(canvasImgTexture);
|
canvasImg = new PIXI.Sprite(canvasImgTexture);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use provided sprite object or create a new one
|
||||||
|
if (spriteObject) {
|
||||||
|
spriteObject.texture = PIXI.Texture.from(elemSrc);
|
||||||
|
canvasImg = spriteObject;
|
||||||
} else {
|
} else {
|
||||||
canvasImg = PIXI.Sprite.from(elemSrc);
|
canvasImg = PIXI.Sprite.from(elemSrc);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
canvasImg.position.set(elemPosition.x, elemPosition.y);
|
canvasImg.position.set(elemPosition.x, elemPosition.y);
|
||||||
canvasImg.width = elemPosition.width/scalefactor;
|
canvasImg.width = elemPosition.width/scalefactor;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
import type * as PIXI from 'pixi.js';
|
import type * as PIXI from 'pixi.js';
|
||||||
import { Text } from 'pixi.js';
|
import { Text } from 'pixi.js';
|
||||||
|
|
||||||
export default function createCanvasText( element: HTMLElement, stage: PIXI.Container ){
|
export default function createCanvasText(
|
||||||
|
element: HTMLElement,
|
||||||
|
stage: PIXI.Container,
|
||||||
|
textObject: PIXI.Text | null = null
|
||||||
|
){
|
||||||
|
|
||||||
const elem = element;
|
const elem = element;
|
||||||
const elemStyles = window.getComputedStyle(elem);
|
const elemStyles = window.getComputedStyle(elem);
|
||||||
|
|
@ -19,7 +23,8 @@ export default function createCanvasText( element: HTMLElement, stage: PIXI.Con
|
||||||
elem.textContent = elem.textContent?.toUpperCase() as string;
|
elem.textContent = elem.textContent?.toUpperCase() as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvasText = new Text(elem.textContent as string, {
|
// Use provided text object or create a new one
|
||||||
|
const canvasText = textObject || new Text(elem.textContent as string, {
|
||||||
fontFamily: elemFontFamily,
|
fontFamily: elemFontFamily,
|
||||||
fontSize: elemFontSize,
|
fontSize: elemFontSize,
|
||||||
fontWeight: elemFontWeight as PIXI.TextStyleFontWeight,
|
fontWeight: elemFontWeight as PIXI.TextStyleFontWeight,
|
||||||
|
|
@ -30,6 +35,18 @@ export default function createCanvasText( element: HTMLElement, stage: PIXI.Con
|
||||||
padding: 20,
|
padding: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update existing text object properties
|
||||||
|
if (textObject) {
|
||||||
|
canvasText.text = elem.textContent as string;
|
||||||
|
canvasText.style.fontFamily = elemFontFamily;
|
||||||
|
canvasText.style.fontSize = parseInt(elemFontSize);
|
||||||
|
canvasText.style.fontWeight = elemFontWeight as PIXI.TextStyleFontWeight;
|
||||||
|
canvasText.style.fontStyle = elemFontStyle as PIXI.TextStyleFontStyle;
|
||||||
|
canvasText.style.letterSpacing = elemLetterSpacing;
|
||||||
|
canvasText.style.fill = elemColor;
|
||||||
|
canvasText.style.align = elemAlignment as PIXI.TextStyleAlign;
|
||||||
|
}
|
||||||
|
|
||||||
canvasText.on('added', () => {
|
canvasText.on('added', () => {
|
||||||
elem.classList.add('canvas-text-added');
|
elem.classList.add('canvas-text-added');
|
||||||
elem.style.visibility = 'hidden';
|
elem.style.visibility = 'hidden';
|
||||||
|
|
|
||||||
79
src/routes/contact/+page.server.ts
Normal file
79
src/routes/contact/+page.server.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import type { Actions } from './$types';
|
||||||
|
import { BREVO_API_KEY } from '$env/static/private';
|
||||||
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
const BREVO_ENDPOINT = 'https://api.brevo.com/v3/smtp/email';
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
default: async ({ request, fetch }) => {
|
||||||
|
const formData = await request.formData();
|
||||||
|
const name = (formData.get('name') ?? '').toString().trim();
|
||||||
|
const email = (formData.get('email') ?? '').toString().trim();
|
||||||
|
const message = (formData.get('contact') ?? '').toString().trim();
|
||||||
|
const fields = { name, email, contact: message };
|
||||||
|
|
||||||
|
if (!email || !message) {
|
||||||
|
return fail(400, { error: 'Please provide your email and message.', fields });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
return fail(400, { error: 'Please provide a valid email address.', fields });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BREVO_API_KEY) {
|
||||||
|
console.error('Missing BREVO_API_KEY in environment');
|
||||||
|
return fail(500, { error: 'Email service is not configured. Please try again later.', fields });
|
||||||
|
}
|
||||||
|
|
||||||
|
const subject = `New contact form submission from ${name || 'Website visitor'}`;
|
||||||
|
const textContent = `Name: ${name || 'N/A'}\nEmail: ${email}\n\nMessage:\n${message}`;
|
||||||
|
const htmlContent = `<p><strong>Name:</strong> ${name || 'N/A'}</p><p><strong>Email:</strong> ${email}</p><p><strong>Message:</strong></p><p>${message.replace(/\n/g, '<br>')}</p>`;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
sender: {
|
||||||
|
name: 'Website Contact',
|
||||||
|
email: 'simon@floter.design'
|
||||||
|
},
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
email: 'simon@floter.design',
|
||||||
|
name: 'Simon Flöter'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
replyTo: {
|
||||||
|
email,
|
||||||
|
name: name || 'Website visitor'
|
||||||
|
},
|
||||||
|
subject,
|
||||||
|
textContent,
|
||||||
|
htmlContent,
|
||||||
|
tags: ['contact-form']
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(BREVO_ENDPOINT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'api-key': BREVO_API_KEY
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error(`Brevo API error ${response.status}: ${errorText}`);
|
||||||
|
return fail(502, { error: 'Sending failed. Please try again later.', fields });
|
||||||
|
}
|
||||||
|
|
||||||
|
throw redirect(303, '/success');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Brevo API request failed', err);
|
||||||
|
return fail(500, { error: 'Unexpected error. Please try again later.', fields });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
import { SplitText } from 'gsap/SplitText';
|
import { SplitText } from 'gsap/SplitText';
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||||
|
|
||||||
|
export let form: { error?: string; fields?: { name?: string; email?: string; contact?: string } } | undefined;
|
||||||
|
|
||||||
let canvasTexts: Array<HTMLElement> = [];
|
let canvasTexts: Array<HTMLElement> = [];
|
||||||
let contactFormVisible = false;
|
let contactFormVisible = false;
|
||||||
let contactFormClickHandler = (e: Event) => {
|
let contactFormClickHandler = (e: Event) => {
|
||||||
|
|
@ -88,25 +90,27 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="formwrapper">
|
<div class="formwrapper">
|
||||||
<span class="button button-back" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">← Back</span>
|
<span class="button button-back" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">← Back</span>
|
||||||
<form name="contact" action="/success" method="POST" data-netlify="true">
|
<form name="contact" method="POST">
|
||||||
<input type="hidden" name="form-name" value="contact">
|
|
||||||
<div class="inputs-flex-row">
|
<div class="inputs-flex-row">
|
||||||
<label for="name">
|
<label for="name">
|
||||||
<!-- <p>How would you like to be addressed?</p> -->
|
<!-- <p>How would you like to be addressed?</p> -->
|
||||||
<input type="text" name="name" id="name" placeholder="Your name">
|
<input type="text" name="name" id="name" placeholder="Your name" value={form?.fields?.name ?? ''}>
|
||||||
</label>
|
</label>
|
||||||
<label for="email">
|
<label for="email">
|
||||||
<!-- <p>For receiving a reply, add your Email address:</p> -->
|
<!-- <p>For receiving a reply, add your Email address:</p> -->
|
||||||
<input type="email" name="email" id="email" placeholder="Your Email?*" required>
|
<input type="email" name="email" id="email" placeholder="Your Email?*" required value={form?.fields?.email ?? ''}>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<label for="contact">
|
<label for="contact">
|
||||||
<!-- <p>Please describe your plight in a few words</p> -->
|
<!-- <p>Please describe your plight in a few words</p> -->
|
||||||
<textarea rows="8" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required></textarea>
|
<textarea rows="8" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required>{form?.fields?.contact ?? ''}</textarea>
|
||||||
</label>
|
</label>
|
||||||
<div class="disclaimer">
|
<div class="disclaimer">
|
||||||
<p>Disclaimer: I will only use the data you submit here (name, email, message) to respond. I will not pass it on to any third party. If I don't hear from you I will delete the data and keep no records of it.</p>
|
<p>Disclaimer: I will only use the data you submit here (name, email, message) to respond. I will not pass it on to any third party. If I don't hear from you I will delete the data and keep no records of it.</p>
|
||||||
</div>
|
</div>
|
||||||
|
{#if form?.error}
|
||||||
|
<p class="form-error" role="alert" aria-live="polite">{form.error}</p>
|
||||||
|
{/if}
|
||||||
<div class="send">
|
<div class="send">
|
||||||
<button class="button button--xl button--primary" type="submit">Send it!</button>
|
<button class="button button--xl button--primary" type="submit">Send it!</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -266,6 +270,15 @@
|
||||||
margin: 1em auto;
|
margin: 1em auto;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
.form-error {
|
||||||
|
max-width: var(--form-maxwidth);
|
||||||
|
margin: 0 auto 1em auto;
|
||||||
|
padding: .75em;
|
||||||
|
border: 1px solid var(--color-text);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: .95em;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
.send {
|
.send {
|
||||||
max-width: var(--form-maxwidth);
|
max-width: var(--form-maxwidth);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export const prerender = true
|
export const prerender = false
|
||||||
export function load() {
|
export function load() {
|
||||||
return {
|
return {
|
||||||
title: 'Contact me!',
|
title: 'Contact me!',
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@
|
||||||
let canvas: HTMLCanvasElement;
|
let canvas: HTMLCanvasElement;
|
||||||
let introDone = false;
|
let introDone = false;
|
||||||
|
|
||||||
onMount( () => {
|
// Object pool for PIXI.Text objects
|
||||||
|
const textPool: PIXI.Text[] = [];
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
let highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
|
let highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
|
||||||
|
|
||||||
let is_fine = window.matchMedia('(pointer:fine)').matches
|
let is_fine = window.matchMedia('(pointer:fine)').matches
|
||||||
|
|
@ -31,7 +33,7 @@
|
||||||
let textgroup = new PIXI.Container();
|
let textgroup = new PIXI.Container();
|
||||||
textgroup.pivot.set(0, 0);
|
textgroup.pivot.set(0, 0);
|
||||||
textgroup.x = 0;
|
textgroup.x = 0;
|
||||||
textgroup.y = - window.innerHeight;
|
textgroup.y = -window.innerHeight;
|
||||||
textgroup.height = window.innerHeight;
|
textgroup.height = window.innerHeight;
|
||||||
textgroup.width = window.innerWidth;
|
textgroup.width = window.innerWidth;
|
||||||
app.stage.addChild(textgroup);
|
app.stage.addChild(textgroup);
|
||||||
|
|
@ -40,9 +42,11 @@
|
||||||
|
|
||||||
let fontSize = window.innerHeight / 3;
|
let fontSize = window.innerHeight / 3;
|
||||||
|
|
||||||
function createText(string: string){
|
function createText(string: string): PIXI.Text {
|
||||||
let text = new PIXI.Text(string,
|
let text = getTextFromPool();
|
||||||
{ fontFamily: 'Stratos',
|
text.text = string;
|
||||||
|
text.style = {
|
||||||
|
fontFamily: 'Stratos',
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
fontStyle: 'italic',
|
fontStyle: 'italic',
|
||||||
|
|
@ -50,13 +54,24 @@
|
||||||
letterSpacing: -10,
|
letterSpacing: -10,
|
||||||
fill: highLightColor,
|
fill: highLightColor,
|
||||||
padding: 0
|
padding: 0
|
||||||
}
|
};
|
||||||
);
|
|
||||||
text.anchor.set(0);
|
text.anchor.set(0);
|
||||||
textgroup.addChild(text);
|
textgroup.addChild(text);
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Text object pool management
|
||||||
|
function getTextFromPool(): PIXI.Text {
|
||||||
|
if (textPool.length > 0) {
|
||||||
|
return textPool.pop() as PIXI.Text;
|
||||||
|
}
|
||||||
|
return new PIXI.Text();
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnTextToPool(text: PIXI.Text) {
|
||||||
|
textPool.push(text);
|
||||||
|
}
|
||||||
|
|
||||||
let allTexts = [
|
let allTexts = [
|
||||||
createText('SERVICES SERVICES '), createText('SERVICES SERVICES '),
|
createText('SERVICES SERVICES '), createText('SERVICES SERVICES '),
|
||||||
createText('CONSULTATION DESIGN WEB DEVELOPMENT '), createText('CONSULTATION DESIGN WEB DEVELOPMENT '),
|
createText('CONSULTATION DESIGN WEB DEVELOPMENT '), createText('CONSULTATION DESIGN WEB DEVELOPMENT '),
|
||||||
|
|
@ -70,7 +85,7 @@
|
||||||
[allTexts[4], allTexts[5]],
|
[allTexts[4], allTexts[5]],
|
||||||
[allTexts[6], allTexts[7]]
|
[allTexts[6], allTexts[7]]
|
||||||
]
|
]
|
||||||
textRows.forEach( (row, index) => {
|
textRows.forEach((row, index) => {
|
||||||
if (index % 2 === 0) {
|
if (index % 2 === 0) {
|
||||||
row[0].x = 0;
|
row[0].x = 0;
|
||||||
row[1].x = row[0].width;
|
row[1].x = row[0].width;
|
||||||
|
|
@ -136,11 +151,17 @@
|
||||||
mouse.y = e.clientY / window.innerHeight;
|
mouse.y = e.clientY / window.innerHeight;
|
||||||
}, { passive: true })
|
}, { passive: true })
|
||||||
|
|
||||||
|
// Optimize ticker by using requestAnimationFrame
|
||||||
|
let animationFrameId: number;
|
||||||
let elapsed = 0;
|
let elapsed = 0;
|
||||||
app.ticker.add((delta) => {
|
|
||||||
elapsed += delta;
|
function animate() {
|
||||||
|
elapsed += app.ticker.elapsedMS / 16.6667; // Convert to delta time
|
||||||
bulgefilter.center = new PIXI.Point(mouse.x, 0.5);
|
bulgefilter.center = new PIXI.Point(mouse.x, 0.5);
|
||||||
})
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
let scrollTriggerTweens: Array<GSAPTween> = [];
|
let scrollTriggerTweens: Array<GSAPTween> = [];
|
||||||
|
|
||||||
|
|
@ -173,23 +194,23 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
// IMPORTANT: Kill ScrollTrigger and GSAP animations FIRST,
|
// IMPORTANT: Kill ScrollTrigger and GSAP animations FIRST,
|
||||||
// before destroying PixiJS objects they reference
|
// before destroying PixiJS objects they reference
|
||||||
scrollTriggerTweens.forEach( tween => {
|
scrollTriggerTweens.forEach(tween => {
|
||||||
if (tween.scrollTrigger) {
|
if (tween.scrollTrigger) {
|
||||||
tween.scrollTrigger.kill();
|
tween.scrollTrigger.kill();
|
||||||
}
|
}
|
||||||
tween.kill();
|
tween.kill();
|
||||||
});
|
});
|
||||||
// Kill all other tweens
|
// Kill all other tweens
|
||||||
tweens.forEach( tween => tween.kill() );
|
tweens.forEach(tween => tween.kill());
|
||||||
|
// Return objects to pools
|
||||||
|
allTexts.forEach(text => returnTextToPool(text));
|
||||||
// Now safe to destroy PixiJS app
|
// Now safe to destroy PixiJS app
|
||||||
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<canvas bind:this={canvas} id="service-canvas"></canvas>
|
<canvas bind:this={canvas} id="service-canvas"></canvas>
|
||||||
|
|
@ -205,3 +226,4 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,18 +129,18 @@
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Hover states or .work
|
// Hover states of .work
|
||||||
works.forEach((work, index) => {
|
works.forEach((work, index) => {
|
||||||
work.addEventListener('mouseenter', (e) => {
|
work.addEventListener('mouseenter', (e) => {
|
||||||
if (isZoomed) return;
|
if (isZoomed) return;
|
||||||
gsap.to(work, {
|
gsap.to(work, {
|
||||||
backgroundColor: 'var(--color-bg)',
|
backgroundColor: 'var(--color-bg)',
|
||||||
duration: .5,
|
duration: .125,
|
||||||
ease: 'power1.inOut',
|
ease: 'power1.inOut',
|
||||||
})
|
})
|
||||||
gsap.to(work.querySelector('.work-logo'), {
|
gsap.to(work.querySelector('.work-logo'), {
|
||||||
color: 'var(--color-highlight)',
|
color: 'var(--color-highlight)',
|
||||||
duration: .5,
|
duration: .125,
|
||||||
ease: 'power1.inOut',
|
ease: 'power1.inOut',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -148,12 +148,12 @@
|
||||||
if (isZoomed) return;
|
if (isZoomed) return;
|
||||||
gsap.to(work, {
|
gsap.to(work, {
|
||||||
backgroundColor: 'var(--color-highlight)',
|
backgroundColor: 'var(--color-highlight)',
|
||||||
duration: .5,
|
duration: .125,
|
||||||
ease: 'power1.inOut',
|
ease: 'power1.inOut',
|
||||||
})
|
})
|
||||||
gsap.to(work.querySelector('.work-logo'), {
|
gsap.to(work.querySelector('.work-logo'), {
|
||||||
color: 'var(--color-bg)',
|
color: 'var(--color-bg)',
|
||||||
duration: .5,
|
duration: .125,
|
||||||
ease: 'power1.inOut',
|
ease: 'power1.inOut',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
BIN
static/fonts/PPFormula-Extrabold.woff2
Normal file
BIN
static/fonts/PPFormula-Extrabold.woff2
Normal file
Binary file not shown.
BIN
static/fonts/PPFormula-Medium.woff2
Normal file
BIN
static/fonts/PPFormula-Medium.woff2
Normal file
Binary file not shown.
BIN
static/fonts/PPFormula-MediumItalic.woff2
Normal file
BIN
static/fonts/PPFormula-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
static/fonts/Stratos-BoldItalic.woff2
Normal file
BIN
static/fonts/Stratos-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
static/fonts/Stratos-Regular.woff2
Normal file
BIN
static/fonts/Stratos-Regular.woff2
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue