changes
This commit is contained in:
parent
5faecf4f25
commit
1eafc280bd
27 changed files with 683 additions and 439 deletions
|
|
@ -60,12 +60,32 @@ onMount(()=>{
|
|||
let center = [0.5, 0.5];
|
||||
let bulgefilter = new BulgePinchFilter();
|
||||
bulgefilter.radius = xFrac(0.45);
|
||||
bulgefilter.strength = 0.5;
|
||||
bulgefilter.strength = 0;
|
||||
bulgefilter.center = center;
|
||||
bulgefilter.resolution = 2;
|
||||
app.stage.filters = [bulgefilter];
|
||||
|
||||
gsap.to(bulgefilter, {
|
||||
strength: 0.5,
|
||||
duration: 2,
|
||||
delay: 1,
|
||||
ease: 'elastic.out',
|
||||
});
|
||||
|
||||
window.addEventListener('mousedown', ()=>{
|
||||
gsap.to(bulgefilter, {
|
||||
strength: 0,
|
||||
duration: 1,
|
||||
ease: 'elastic.out',
|
||||
});
|
||||
})
|
||||
window.addEventListener('mouseup', ()=>{
|
||||
gsap.to(bulgefilter, {
|
||||
strength: 0.5,
|
||||
duration: 2,
|
||||
ease: 'elastic.out',
|
||||
});
|
||||
})
|
||||
|
||||
/*----------------------------------
|
||||
* Convert text to canvas using
|
||||
|
|
@ -134,13 +154,27 @@ onMount(()=>{
|
|||
/*----------------------------------
|
||||
* Mousemove events
|
||||
*----------------------------------*/
|
||||
let tween = {
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
const pointerX = e.clientX / window.innerWidth;
|
||||
const pointerY = e.clientY / window.innerHeight;
|
||||
const pointerXfrac = pointerX - 0.5;
|
||||
const pointerYfrac = pointerY - 0.5;
|
||||
|
||||
center = [(0.5 + pointerXfrac/10),(0.5 + pointerYfrac/10)];
|
||||
// center = [(0.5 + pointerXfrac/10),(0.5 + pointerYfrac/10)];
|
||||
|
||||
gsap.to(tween, {
|
||||
duration: .5,
|
||||
ease: 'power3.out',
|
||||
overwrite: true,
|
||||
x: 0.5 + pointerXfrac/10,
|
||||
y: 0.5 + pointerYfrac/10,
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -151,7 +185,7 @@ onMount(()=>{
|
|||
|
||||
app.ticker.add((delta) => {
|
||||
elapsed += delta;
|
||||
bulgefilter.center = [(center[0] + Math.sin(elapsed/200)/20 ),(center[1] + Math.cos(elapsed/200)/20 )];
|
||||
bulgefilter.center = [(tween.x + Math.sin(elapsed/200)/20 ),(tween.y + Math.cos(elapsed/200)/20 )];
|
||||
updateImgs();
|
||||
updateText();
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<svg id="floter-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 287.4 83.5">
|
||||
<svg id="floter-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 287.4 83.5" width="290" height="85">
|
||||
<path id="r" d="M259.9 82.4h-24.2l11.6-54.9h22l-2.7 12.9c4.5-8.9 11-14 18.1-14 1 0 2.1 0 2.7.2l-4.7 22.1c-1.4-.4-3.3-.9-6.4-.9-5.4 0-9.7 2.3-10.8 7.7l-5.6 26.9z"/>
|
||||
<path id="e" d="M237.9 59.3h-35.7c.3 5.2 2.2 7.5 6.5 7.5 2.9 0 5.1-1.5 5.9-4.2h22.3c-4.3 13.2-14.5 20.9-30.4 20.9-16.3 0-26.5-9.7-26.5-25.2 0-17.8 13.4-32 32.3-32 16.3 0 26.5 9.7 26.5 25.2 0 2.7-.4 5.3-.9 7.8zM203.3 50h13.5c-.2-4-1.8-6.5-6-6.5-3.4 0-6.1 2.1-7.5 6.5z"/>
|
||||
<path id="t" d="M171.5 65.9c1.9 0 4.3-.3 6.3-1L174 82.2c-2.5.8-7.1 1.3-11.1 1.3-14 0-23.2-5.1-19.6-22.3l3.5-16.7h-5.5l3.6-17h5.5l3.3-15.4H178l-3.3 15.4h11l-3.6 17h-11l-3.3 15.8c-.9 3.9.3 5.6 3.7 5.6z"/>
|
||||
|
|
@ -13,5 +13,6 @@
|
|||
fill: currentColor;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
128
src/lib/components/MainNav.svelte
Normal file
128
src/lib/components/MainNav.svelte
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<script lang='ts'>
|
||||
import { onMount } from "svelte";
|
||||
import { gsap } from "gsap";
|
||||
|
||||
onMount(() => {
|
||||
|
||||
const navBlockElems: Array<HTMLElement> = Array.from(document.querySelectorAll('#nav'));
|
||||
navBlockElems.forEach((el) => {
|
||||
el.style.display = 'block';
|
||||
});
|
||||
gsap.set('#nav', { autoAlpha: 0, y: '100%'});
|
||||
gsap.set('nav a', { autoAlpha: 0, y: 200 });
|
||||
|
||||
//Nav links close menu when clicked
|
||||
let navlinks = document.querySelectorAll('nav a');
|
||||
navlinks.forEach((link, index) => {
|
||||
link.addEventListener('click', (e)=> {
|
||||
const checkbox = document.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||
checkbox ? checkbox.checked = false : null;
|
||||
gsap.to('.content > *', {duration: 0.5, y: '100%', autoAlpha: 1, ease: 'power4.out'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: '100%', ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 200, stagger: 0.05, ease: 'power4.out' , delay: 0.2});
|
||||
})
|
||||
});
|
||||
|
||||
//Toggle menu
|
||||
let checkbox = document.getElementById('menustate') as HTMLInputElement;
|
||||
checkbox.addEventListener('change', (e)=> {
|
||||
console.log(checkbox.checked);
|
||||
if (checkbox.checked) {
|
||||
gsap.to('.content > *', {duration: 0.75, y: -200, autoAlpha: 0.5, ease: 'power2.inOut'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: 0, ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.5, autoAlpha: 1, y: 0, stagger: 0.1, ease: 'power4.out' , delay: 0.2});
|
||||
} else {
|
||||
gsap.to('.content > *', {duration: 0.5, y: 0, autoAlpha: 1, ease: 'power4.out'});
|
||||
gsap.to('#nav', {duration: 0.5, autoAlpha: 1, y: '100%', ease: 'power4.out', delay: 0.1});
|
||||
gsap.to('nav a', {duration: 0.75, autoAlpha: 1, y: 200, stagger: 0.05, ease: 'power4.out' , delay: 0.2});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<input aria-hidden="true" type="checkbox" id="menustate" />
|
||||
<label for="menustate" aria-hidden="true">
|
||||
<span class="open">≡</span>
|
||||
<span class="close">×</span>
|
||||
</label>
|
||||
<nav id="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/work">About</a>
|
||||
<a href="/work">Hire</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
header {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
padding: var(--spacing-nav);
|
||||
display: flex;
|
||||
gap: .75em;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
label {
|
||||
height: 36px;
|
||||
}
|
||||
.open, .close {
|
||||
font-size: 4em;
|
||||
line-height: 0.3;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
cursor: url('/pointer.svg'), auto;
|
||||
}
|
||||
.close {
|
||||
top: 0.033em;
|
||||
color: #FFF;
|
||||
}
|
||||
#menustate, #nav, .close {
|
||||
/* Hide the checkbox, menu and close button by default */
|
||||
display: none;
|
||||
}
|
||||
#menustate:checked ~ #nav ,
|
||||
#menustate:checked ~ label .close {
|
||||
/*
|
||||
Show the menu and close button when the menu is open
|
||||
(when the #menustate input field is checked)
|
||||
*/
|
||||
display: block;
|
||||
}
|
||||
#menustate:checked ~ label .open {
|
||||
/* Hide the open button when the menu is open */
|
||||
display: none;
|
||||
}
|
||||
#nav {
|
||||
background-color: var(--color-bg);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: var(--spacing-outer);
|
||||
}
|
||||
#nav a {
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
font-size: 3em;
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
text-transform: lowercase;
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 5.5em;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -61,16 +61,16 @@ onMount(()=>{
|
|||
|
||||
let center = [0.5, 0.5];
|
||||
let bulgefilter = new BulgePinchFilter();
|
||||
bulgefilter.radius = xFrac(0.6);
|
||||
bulgefilter.radius = xFrac(0.5);
|
||||
bulgefilter.strength = bulgeFactor;
|
||||
bulgefilter.center = center;
|
||||
bulgefilter.resolution = 2;
|
||||
// app.stage.filters = [bulgefilter];
|
||||
let rgbFilter = new RGBSplitFilter();
|
||||
rgbFilter.red = [0, 0];
|
||||
rgbFilter.green = [0, 0];
|
||||
rgbFilter.blue = [0, 0];
|
||||
rgbFilter.resolution = 2;
|
||||
// let rgbFilter = new RGBSplitFilter();
|
||||
// rgbFilter.red = [0, 0];
|
||||
// rgbFilter.green = [0, 0];
|
||||
// rgbFilter.blue = [0, 0];
|
||||
// rgbFilter.resolution = 2;
|
||||
app.stage.filters = [bulgefilter];
|
||||
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ onMount(()=>{
|
|||
image.position.set(imagePosition.x, imagePosition.y);
|
||||
image.width = imagePosition.width;
|
||||
image.height = imagePosition.height;
|
||||
// image.alpha = imgElems[index].style.opacity as unknown as number;
|
||||
image.zIndex = imgElems[index].style.zIndex as unknown as number;
|
||||
image.alpha = window.getComputedStyle(imgElems[index]).opacity as unknown as number;
|
||||
})
|
||||
}
|
||||
|
|
@ -146,17 +146,17 @@ onMount(()=>{
|
|||
/*----------------------------------
|
||||
* Mousemove events
|
||||
*----------------------------------*/
|
||||
let tween = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
let tween = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
const pointerX = e.clientX / window.innerWidth;
|
||||
const pointerY = e.clientY / window.innerHeight;
|
||||
const pointerXfrac = pointerX - 0.5;
|
||||
const pointerYfrac = pointerY - 0.5;
|
||||
rgbFilter.red = [pointerXfrac * 10, pointerYfrac * 10];
|
||||
rgbFilter.green = [pointerXfrac * -10, pointerYfrac * -10];
|
||||
// rgbFilter.red = [pointerXfrac * 10, pointerYfrac * 10];
|
||||
// rgbFilter.green = [pointerXfrac * -10, pointerYfrac * -10];
|
||||
|
||||
gsap.to(tween, {
|
||||
duration: .5,
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ type Post = {
|
|||
|
||||
export const fetchMarkdownPosts = async () => {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const allPostFiles = import.meta.glob('/src/routes/work/\*.md')
|
||||
const allPostFiles = import.meta.glob('/src/routes/work/md/\*.md')
|
||||
|
||||
const iterablePostFiles = Object.entries(allPostFiles)
|
||||
|
||||
const allPosts = await Promise.all(
|
||||
iterablePostFiles.map(async ([path, resolver]) => {
|
||||
const postPath = path.slice(11, -3)
|
||||
const postPath = path.slice(11, -3).replace('work/md/','work/')
|
||||
const data: unknown = await resolver()
|
||||
const postData = data as Post
|
||||
const content = postData.default.render() as unknown as { html: string }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,27 @@
|
|||
:root {
|
||||
--spacing-outer: 5vw;
|
||||
--spacing-nav: 5vw;
|
||||
// --color-bg: #FFF;
|
||||
// --color-text: #000;
|
||||
--color-bg: #00117f;
|
||||
--color-text: #FF9494;
|
||||
--aspect-ratio-heroes: 1.5;
|
||||
--font-size-p: clamp(20px, 1.6vw, 1.6vw);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
--spacing-outer: 5vw;
|
||||
--spacing-nav: 2.5vw;
|
||||
}
|
||||
}
|
||||
body {
|
||||
cursor: url('/cursor.svg'), auto;
|
||||
}
|
||||
a:hover, input:hover, button:hover {
|
||||
cursor: url('/pointer.svg'), auto;
|
||||
}
|
||||
body {
|
||||
font-family: stratos, sans-serif;
|
||||
font-size: 20px;
|
||||
font-size: var(--font-size-p);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: var(--color-bg);
|
||||
|
|
@ -16,12 +32,21 @@ body * {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
h1, h2, h3, h4, h5 {
|
||||
font-size: 1em;
|
||||
font-size: 2em;
|
||||
line-height: 1.2;
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
p {
|
||||
ul, ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
ul {
|
||||
list-style: '▪︎ ';
|
||||
}
|
||||
p, li {
|
||||
font-weight: 400;
|
||||
font-size: clamp(20px, 1.6vw, 1.6vw);
|
||||
font-size: var(--font-size-p);
|
||||
line-height: 1.3;
|
||||
}
|
||||
a {
|
||||
|
|
@ -89,3 +114,21 @@ a {
|
|||
}
|
||||
}
|
||||
}
|
||||
.infobox {
|
||||
// border-top: 2px solid var(--color-text);
|
||||
// border-bottom: 2px solid var(--color-text);
|
||||
padding: 1.5em 0;
|
||||
font-size: var(--font-size-p);
|
||||
|
||||
& li {
|
||||
border-bottom: 1px solid;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
& > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
3
src/lib/utils/stores.ts
Normal file
3
src/lib/utils/stores.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
export const workbulge = writable(0.25);
|
||||
|
|
@ -1,139 +1,33 @@
|
|||
<script lang='ts'>
|
||||
import '$lib/styles/global.scss';
|
||||
import Logo from '$lib/components/Logo.svelte';
|
||||
import gsap from 'gsap';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
let navlinks = document.querySelectorAll('nav a');
|
||||
|
||||
navlinks.forEach((link, index) => {
|
||||
link.addEventListener('click', (e)=> {
|
||||
const checkbox = document.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||
checkbox ? checkbox.checked = false : null;
|
||||
gsap.to('.content > *', {duration: 0.5, y: '0%', autoAlpha: 1, ease: 'power4.out'});
|
||||
})
|
||||
});
|
||||
|
||||
let checkbox = document.getElementById('menustate') as HTMLInputElement;
|
||||
|
||||
checkbox.addEventListener('change', (e)=> {
|
||||
if (checkbox.checked) {
|
||||
gsap.to('.content > *', {duration: 0.75, y: -200, autoAlpha: 0.5, ease: 'power2.inOut'});
|
||||
gsap.from('#nav', {duration: 0.5, autoAlpha: 0, y: '100%', ease: 'power4.out', delay: 0.1});
|
||||
gsap.from('nav a', {duration: 0.5, autoAlpha: 0, y: 20, stagger: 0.1, ease: 'power4.out' , delay: 0.2});
|
||||
} else {
|
||||
gsap.to('.content > *', {duration: 0.5, y: '0%', autoAlpha: 1, ease: 'power4.out'});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
import MainNav from '$lib/components/MainNav.svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
export let data;
|
||||
</script>
|
||||
<a href='/' class="logo">
|
||||
<Logo />
|
||||
</a>
|
||||
<header>
|
||||
<input aria-hidden="true" type="checkbox" id="menustate" />
|
||||
<label for="menustate" aria-hidden="true">
|
||||
<span class="open">≡</span>
|
||||
<span class="close">×</span>
|
||||
</label>
|
||||
<!-- <a href="#nav" class="logo"><Logo /></a> -->
|
||||
<nav id="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/work">About</a>
|
||||
<a href="/work">Hire</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
<a href='/' class="logo"><Logo /></a>
|
||||
<MainNav />
|
||||
|
||||
{#key data.pathname}
|
||||
<div
|
||||
class="content"
|
||||
in:fade={{ duration: 100, delay: 0 }}
|
||||
out:fade={{ duration: 100, delay: 100 }}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<style lang="scss">
|
||||
header {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
padding: var(--spacing-outer);
|
||||
display: flex;
|
||||
gap: .75em;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
// width: 100%;
|
||||
|
||||
// &:before {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// background: linear-gradient(0deg, #00117fFF 0%, #00117fFF 30%, #00117f00 100%);
|
||||
// }
|
||||
}
|
||||
label {
|
||||
height: 36px;
|
||||
}
|
||||
.open, .close {
|
||||
font-size: 4em;
|
||||
line-height: 0.3;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
.close {
|
||||
top: 0.033em;
|
||||
color: #FFF;
|
||||
}
|
||||
#menustate, #nav, .close {
|
||||
/* Hide the checkbox, menu and close button by default */
|
||||
display: none;
|
||||
}
|
||||
#menustate:checked ~ nav ,
|
||||
#menustate:checked ~ label .close {
|
||||
/*
|
||||
Show the menu and close button when the menu is open
|
||||
(when the #menustate input field is checked)
|
||||
*/
|
||||
display: block;
|
||||
}
|
||||
#menustate:checked ~ label .open {
|
||||
/* Hide the open button when the menu is open */
|
||||
display: none;
|
||||
}
|
||||
.logo {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
margin: var(--spacing-outer);
|
||||
}
|
||||
nav {
|
||||
background-color: var(--color-bg);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: var(--spacing-outer);
|
||||
}
|
||||
nav a {
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
font-size: 5.5em;
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
text-transform: lowercase;
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
|
||||
&:first-child {
|
||||
margin-top: 1em;
|
||||
}
|
||||
margin: var(--spacing-nav);
|
||||
cursor: url('/pointer.svg'), auto;
|
||||
}
|
||||
</style>
|
||||
7
src/routes/+layout.ts
Normal file
7
src/routes/+layout.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export const load = ({ url }) => {
|
||||
const { pathname } = url
|
||||
|
||||
return {
|
||||
pathname
|
||||
}
|
||||
}
|
||||
|
|
@ -3,63 +3,65 @@
|
|||
import { gsap } from 'gsap';
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
|
||||
let canvasElems: Array<HTMLElement>;
|
||||
|
||||
onMount(() => {
|
||||
|
||||
gsap.registerPlugin( ScrollTrigger, SplitText );
|
||||
gsap.registerPlugin( ScrollTrigger, SplitText );
|
||||
|
||||
const sections = document.querySelectorAll('section');
|
||||
const sections = document.querySelectorAll('section');
|
||||
|
||||
sections.forEach( (section) => {
|
||||
if ( section.classList.contains('splash')){
|
||||
let split = new SplitText(section.querySelectorAll('h1'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(split.lines, {
|
||||
opacity: 0,
|
||||
y: window.innerHeight * 0.25,
|
||||
})
|
||||
gsap.to(split.lines, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
stagger: 0.075,
|
||||
ease: 'power4.out',
|
||||
sections.forEach( (section) => {
|
||||
if ( section.classList.contains('splash')){
|
||||
let split = new SplitText(section.querySelectorAll('h1'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(split.lines, {
|
||||
opacity: 1,
|
||||
y: window.innerHeight,
|
||||
})
|
||||
gsap.to(split.lines, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
stagger: 0.075,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
}
|
||||
else {
|
||||
let split = new SplitText(section.querySelectorAll('h2'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(split.lines, { transformOrigin: '0% 100%' });
|
||||
gsap.set(split.lines, { yPercent: 100, opacity: 0 });
|
||||
gsap.to(split.lines, { duration: 1, yPercent: 0, opacity: 1, stagger: 0.1, ease: 'power4.out',
|
||||
scrollTrigger: { scroller: '.scroller', trigger: split.lines, start: 'top 90%', end: 'bottom 70%', scrub: true, id: 'sth2' }
|
||||
})
|
||||
if (section.querySelector('p')) {
|
||||
let splitp = new SplitText(section.querySelectorAll('p'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(splitp.lines, { transformOrigin: '0% 100%' });
|
||||
gsap.set(splitp.lines, { yPercent: 100, autoAlpha: 0 });
|
||||
gsap.to(splitp.lines, { duration: 1, yPercent: 0, autoAlpha: 1, stagger: 0.05, ease: 'power4.out',
|
||||
scrollTrigger: { scroller: '.scroller', trigger: splitp.lines, start: 'top 80%', end: 'bottom 40%', scrub: true }
|
||||
})
|
||||
}
|
||||
else {
|
||||
let split = new SplitText(section.querySelectorAll('h2'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(split.lines, { transformOrigin: '0% 100%' });
|
||||
gsap.set(split.lines, { yPercent: 100, opacity: 0 });
|
||||
gsap.to(split.lines, { duration: 1, yPercent: 0, opacity: 1, stagger: 0.1, ease: 'power4.out',
|
||||
scrollTrigger: { trigger: split.lines, start: 'top 90%', end: 'bottom 70%', scrub: true }
|
||||
})
|
||||
if (section.querySelector('p')) {
|
||||
let splitp = new SplitText(section.querySelectorAll('p'), { type: 'lines', linesClass: 'lineChildren' });
|
||||
gsap.set(splitp.lines, { transformOrigin: '0% 100%' });
|
||||
gsap.set(splitp.lines, { yPercent: 100, autoAlpha: 0 });
|
||||
gsap.to(splitp.lines, { duration: 1, yPercent: 0, autoAlpha: 1, stagger: 0.05, ease: 'power4.out',
|
||||
scrollTrigger: { trigger: splitp.lines, start: 'top 80%', end: 'bottom 40%', scrub: true }
|
||||
})
|
||||
}
|
||||
}
|
||||
if (section.querySelector('.cols-2')){
|
||||
gsap.set(section.querySelector('.cols-2'), { autoAlpha: 0 });
|
||||
gsap.to(section.querySelector('.cols-2'), { duration: 1, autoAlpha: 1, ease: 'power4.out',
|
||||
scrollTrigger: { trigger: section.querySelector('.cols-2'), start: 'top 80%', end: 'bottom 40%', scrub: true }
|
||||
})
|
||||
}
|
||||
})
|
||||
canvasElems = Array.from(document.querySelectorAll('.lineChildren'));
|
||||
}
|
||||
if (section.querySelector('.cols-2')){
|
||||
gsap.set(section.querySelector('.cols-2'), { autoAlpha: 0 });
|
||||
gsap.to(section.querySelector('.cols-2'), { duration: 1, autoAlpha: 1, ease: 'power4.out',
|
||||
scrollTrigger: { scroller: '.scroller', trigger: section.querySelector('.cols-2'), start: 'top 80%', end: 'bottom 40%', scrub: true }
|
||||
})
|
||||
}
|
||||
})
|
||||
canvasElems = Array.from(document.querySelectorAll('.lineChildren'));
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<article class="scroller">
|
||||
<section class="splash">
|
||||
<h1>Simon Flöter creates products that stand out.</h1>
|
||||
</section>
|
||||
<section class="intro">
|
||||
<h2>As a Creative Frontend Developer ...</h2>
|
||||
<h2>As a Creative Web Developer ...</h2>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<p>I specialise in delivering beautifully crafted bespoke websites.</p>
|
||||
|
|
@ -100,8 +102,14 @@
|
|||
<HomeCanvas textsToCanvas={canvasElems}/>
|
||||
|
||||
<style lang="scss">
|
||||
article {
|
||||
.scroller {
|
||||
font-size: clamp(32px, 4.5vw, 4.5vw);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
section {
|
||||
scroll-snap-align: start;
|
||||
|
|
@ -129,6 +137,7 @@
|
|||
line-height: 1.1;
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,222 +1,58 @@
|
|||
<script lang="ts">
|
||||
import WorkCanvas from '$lib/components/WorkCanvas.svelte';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import gsap from 'gsap';
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { workClickHandler, initWorkPage } from './workUtils.js';
|
||||
import { workbulge } from '$lib/utils/stores.js';
|
||||
export let data;
|
||||
|
||||
let canvasTextElems: Array<HTMLElement>;
|
||||
let canvasImgElems: Array<HTMLElement>;
|
||||
let bulge = { factor: 0.15 };
|
||||
let bulge = {factor: 0};
|
||||
|
||||
onMount(() => {
|
||||
|
||||
gsap.registerPlugin( ScrollTrigger, SplitText );
|
||||
|
||||
let h1 = document.querySelector('h1') as HTMLElement;
|
||||
let h1Text = h1?.innerHTML || '';
|
||||
h1.innerHTML = h1Text + h1Text + h1Text + h1Text || '';
|
||||
h1.style.overflow = 'hidden';
|
||||
h1.style.whiteSpace = 'nowrap';
|
||||
|
||||
h1.querySelectorAll('span').forEach((span) => {
|
||||
gsap.to(span, {xPercent: -100, duration: 4, ease: 'none', repeat: -1 })
|
||||
});
|
||||
|
||||
const split = new SplitText('h1', { type: 'words', wordsClass: 'words' });
|
||||
gsap.set(split.words, { opacity: 0 });
|
||||
|
||||
canvasTextElems = Array.from(document.querySelectorAll('.words'));
|
||||
gsap.set(canvasTextElems, {
|
||||
opacity: 0, yPercent: -10
|
||||
})
|
||||
gsap.to(canvasTextElems, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
yPercent: 0,
|
||||
stagger: 0.1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
|
||||
canvasImgElems = Array.from(document.querySelectorAll('.workhero'));
|
||||
gsap.set(canvasImgElems, {
|
||||
opacity: 0, yPercent: -10
|
||||
})
|
||||
gsap.to(canvasImgElems, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
yPercent: 0,
|
||||
stagger: 0.1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
workbulge.subscribe((val) => {
|
||||
bulge.factor = val;
|
||||
});
|
||||
|
||||
function workClickHandler(e:Event){
|
||||
e.preventDefault();
|
||||
const target = e.target as HTMLElement;
|
||||
const originalLink = target.getAttribute('href') as string;
|
||||
const targetImg = target.querySelector('.workhero') as HTMLElement;
|
||||
const targetImgRect = targetImg.getBoundingClientRect();
|
||||
const targetImgAspectRatio = targetImgRect.width / targetImgRect.height;
|
||||
target.classList.add('active');
|
||||
targetImg.style.width = `${targetImgRect.width}px`;
|
||||
targetImg.style.height = `${targetImgRect.height}px`;
|
||||
targetImg.style.top = `${targetImgRect.top}px`;
|
||||
targetImg.style.left = `${targetImgRect.left}px`;
|
||||
targetImg.style.position = 'fixed';
|
||||
|
||||
gsap.to('.work:not(.active) .workhero', {
|
||||
duration: .3,
|
||||
opacity: 0,
|
||||
yPercent: 10,
|
||||
stagger: 0.1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
gsap.set(targetImg, {
|
||||
zIndex: 100,
|
||||
})
|
||||
gsap.to(targetImg, {
|
||||
duration: .6,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerWidth / targetImgAspectRatio,
|
||||
ease: 'circ.inOut',
|
||||
delay: .3,
|
||||
})
|
||||
gsap.to(bulge, {
|
||||
duration: .3,
|
||||
factor: 0,
|
||||
ease: 'circ.inOut',
|
||||
onUpdate: () => {
|
||||
bulge.factor = bulge.factor;
|
||||
}
|
||||
})
|
||||
gsap.to('h1', {
|
||||
duration: .3,
|
||||
yPercent: -200,
|
||||
ease: 'circ.inOut',
|
||||
})
|
||||
|
||||
// after a timeout of 1000ms (1s), navigate to the original link
|
||||
setTimeout(() => {
|
||||
window.location.href = originalLink;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const headline: HTMLElement = document.querySelector('.headline') as HTMLElement;
|
||||
const images: Array<HTMLElement> = Array.from(document.querySelectorAll('.workhero'));
|
||||
let canvasElems = initWorkPage( headline, images );
|
||||
canvasTextElems = canvasElems.text as Array<HTMLElement>;
|
||||
canvasImgElems = canvasElems.images;
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1><span>Casestudies</span></h1>
|
||||
<h1 class="headline"><span>Casestudies</span></h1>
|
||||
<div class="works">
|
||||
|
||||
{#each data.posts as work}
|
||||
<a href="{work.path}" class="work" on:click={ workClickHandler }>
|
||||
<a href="{work.path}" class="work" on:click={ (e) => workClickHandler(e) }>
|
||||
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
|
||||
<h2>{work.meta.title}</h2>
|
||||
</a>
|
||||
|
||||
{/each}
|
||||
{#each data.posts as work}
|
||||
<a href="{work.path}" class="work" on:click={ workClickHandler }>
|
||||
<a href="{work.path}" class="work" on:click={ (e) => workClickHandler(e) }>
|
||||
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
|
||||
<h2>{work.meta.title}</h2>
|
||||
</a>
|
||||
|
||||
{/each}
|
||||
{#each data.posts as work}
|
||||
<a href="{work.path}" class="work" on:click={ workClickHandler }>
|
||||
<a href="{work.path}" class="work" on:click={ (e) => workClickHandler(e) }>
|
||||
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
|
||||
<h2>{work.meta.title}</h2>
|
||||
</a>
|
||||
|
||||
{/each}
|
||||
{#each data.posts as work}
|
||||
<a href="{work.path}" class="work" on:click={ workClickHandler }>
|
||||
<a href="{work.path}" class="work" on:click={ (e) => workClickHandler(e) }>
|
||||
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
|
||||
<h2>{work.meta.title}</h2>
|
||||
</a>
|
||||
|
||||
{/each}
|
||||
</div>
|
||||
<WorkCanvas textsToCanvas={canvasTextElems} imgsToCanvas={canvasImgElems} bulgeFactor={bulge.factor} />
|
||||
<WorkCanvas
|
||||
textsToCanvas={canvasTextElems}
|
||||
imgsToCanvas={canvasImgElems}
|
||||
bulgeFactor={bulge.factor}
|
||||
/>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
h1 {
|
||||
font-size: 12vw;
|
||||
font-style: italic;
|
||||
margin: 0.5em 0 0.5em 0;
|
||||
letter-spacing: -0.04em;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 7vw;
|
||||
}
|
||||
|
||||
& span {
|
||||
display: inline-block;
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-size: 2vw;
|
||||
// visibility: hidden;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0%;
|
||||
top: 0%;
|
||||
transform: scale(0.8);
|
||||
margin: 0;
|
||||
padding: 0.1em 0.4em 0.2em 0.4em;
|
||||
border-radius: .25em;
|
||||
color: var(--color-bg);
|
||||
text-align: center;
|
||||
transform-origin: center center;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
background-color: var(--color-text);
|
||||
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
.works .work:hover & {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.works {
|
||||
padding: 24vw 0.5em 0.5em 0.5em;
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 14vw 0.5em 0.5em 0.5em;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25em;
|
||||
width: 100%;
|
||||
padding-bottom: 20svh;
|
||||
}
|
||||
}
|
||||
.work {
|
||||
flex: 0 0 calc(33% - .125em);
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
// visibility: hidden;
|
||||
}
|
||||
.workhero {
|
||||
width: 100%;
|
||||
// aspect-ratio: 4/3;
|
||||
object-fit: cover;
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<style src="./work.scss" lang="scss"></style>
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
export async function load({ params }){
|
||||
try {
|
||||
const post = await import(`../${params.slug}.md`)
|
||||
const { title, date, header_bg_image } = post.metadata
|
||||
const post = await import(`../md/${params.slug}.md`)
|
||||
const { title, date, header_bg_image, svg, video } = post.metadata
|
||||
const Content = post.default.render()
|
||||
|
||||
return {
|
||||
title,
|
||||
date,
|
||||
header_bg_image,
|
||||
Content
|
||||
svg,
|
||||
video,
|
||||
Content,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
|
|
|||
|
|
@ -1,45 +1,160 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { gsap } from 'gsap';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
export let data;
|
||||
|
||||
let visible = false;
|
||||
|
||||
function animForDesktop() {
|
||||
gsap.to('.hero', { duration: .6, x: "-10%", ease: "cubic.inOut" })
|
||||
gsap.fromTo('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0% 100%)",
|
||||
},{
|
||||
clipPath: "polygon(0 0, 50% 0, 25% 100%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "cubic.inOut"
|
||||
})
|
||||
}
|
||||
function animForMobile() {
|
||||
gsap.to('.hero', { duration: .6, x: 0, ease: "cubic.inOut" })
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 90%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "cubic.inOut"
|
||||
})
|
||||
}
|
||||
function animForSize(){
|
||||
if ( window.matchMedia("(min-width: 768px) and (orientation: landscape)").matches ) {
|
||||
animForDesktop();
|
||||
} else {
|
||||
animForMobile();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let workheros = document.querySelector('.hero');
|
||||
console.log(workheros);
|
||||
visible = true;
|
||||
|
||||
animForSize();
|
||||
window.addEventListener('resize', () => {
|
||||
animForSize();
|
||||
})
|
||||
|
||||
})
|
||||
</script>
|
||||
<article>
|
||||
<div class="heromask">
|
||||
<img class="hero" src="{data.header_bg_image}" alt="{data.title}" />
|
||||
<!-- <video class="herovid" autoplay muted loop playsinline>
|
||||
<source src="{data.video}" type="video/mp4">
|
||||
</video> -->
|
||||
</div>
|
||||
<div class="subnav">
|
||||
<a href="/work" class="subnav-item">← Back</a>
|
||||
</div>
|
||||
<article>
|
||||
{#if visible}
|
||||
<h1 in:fly>{data.title}</h1>
|
||||
<p in:fly>{@html data.Content.html}</p>
|
||||
<div class="work-content" in:fly={{ x: '10vw', duration: 400, delay: 400, opacity:0 }}>
|
||||
<h1>{@html data.svg}</h1>
|
||||
<div class="work-content-text">
|
||||
{@html data.Content.html}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
|
||||
<style lang="scss">
|
||||
article {
|
||||
article:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
right: 0; bottom: 0; left: 0;
|
||||
height: calc(36px + 2 * var(--spacing-outer));
|
||||
z-index: 1;
|
||||
border-top: 1px solid var(--color-text);
|
||||
background: #00117f;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
.subnav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
padding: var(--spacing-outer);
|
||||
}
|
||||
.hero {
|
||||
.heromask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left:0;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
z-index: 2;
|
||||
height: auto;
|
||||
// object-fit: cover;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
h1, p {
|
||||
.hero {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
z-index: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
perspective: 400px;
|
||||
}
|
||||
.herovid{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
perspective: 300px;
|
||||
animation: fadein 2s .5s both;
|
||||
}
|
||||
@keyframes fadein {
|
||||
from { opacity: 0; transform: rotateX(0deg); }
|
||||
to { opacity: 1; transform: rotateX(45deg);}
|
||||
}
|
||||
.work-content {
|
||||
padding: var(--spacing-outer);
|
||||
padding-top: calc(100vw / var(--aspect-ratio-heroes) + 1.5em);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: 0;
|
||||
color: var(--color-text);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
margin-left: 40vw;
|
||||
max-width: 60vw;
|
||||
padding-top: calc( 3 * var(--spacing-outer) );
|
||||
padding-left: calc(var(--spacing-outer) * 1.5);
|
||||
padding-right: calc(var(--spacing-outer) * 2.5);
|
||||
|
||||
}
|
||||
}
|
||||
.work-content-text {
|
||||
border-top: 1px solid var(--color-text);
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (max-width: 767px){
|
||||
h1 {
|
||||
margin-top: calc(100vw / 1.333);
|
||||
h1 {
|
||||
max-width: 200px;
|
||||
margin: 0;
|
||||
padding: 0 0 1em 0;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
title: Adidas
|
||||
header_bg_image: /work/adidas/adidas_hero.jpg
|
||||
order: 12
|
||||
description: Adidas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="enable-background:new 0 0 1440 900" viewBox="0 0 1440 900">
|
||||
<path d="M1440 900v-.5l-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.5-14.3-14.2-8.2.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.3-14.2-8.2.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.3-14.1-8.2.4-14.3-14.1-8.3.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.2-8.3.4-11.9H707.9l-.1.6 14.1 8.3-.4 14.3 14.1 8.2-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.2-.5 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.2-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.2-.5 14.3 14.2 8.3-.5 14.2 14.2 8.3-.5 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.2-.5 14.3 14.1 8.3-.4 14.3 14.1 8.2-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.5 14.2 14.2 8.3-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.3 14.1 8.2V900z" style="fill:#ffb7ab"/>
|
||||
</svg>'
|
||||
---
|
||||
<div class="infobox">
|
||||
<p>Client: <a href="https://www.adidas-group.com/en/">Adidas E-commerce dept</a></p>
|
||||
Tasks:
|
||||
<ul>
|
||||
<li>Visual Design</li>
|
||||
<li>Campaign asset production</li>
|
||||
<li>Illustration</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p><span class="drop_cap">A</span>didas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.</p>
|
||||
</div>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
title: Formo.bio
|
||||
header_bg_image: /work/formo/formo_hero.png
|
||||
order: 12
|
||||
description: Formo, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="enable-background:new 0 0 1440 900" viewBox="0 0 1440 900">
|
||||
<path d="M1440 900v-.5l-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.5-14.3-14.2-8.2.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.2.5-14.3-14.2-8.3.5-14.3-14.2-8.2.5-14.3-14.1-8.3.4-14.2-14.1-8.3.4-14.3-14.1-8.3.4-14.3-14.1-8.2.4-14.3-14.1-8.3.4-14.3-14.1-8.3.5-14.2-14.2-8.3.5-14.3-14.2-8.3.4-11.9H707.9l-.1.6 14.1 8.3-.4 14.3 14.1 8.2-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.2-.5 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.2-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.2-.5 14.3 14.2 8.3-.5 14.2 14.2 8.3-.5 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.2-.5 14.3 14.1 8.3-.4 14.3 14.1 8.2-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.5 14.2 14.2 8.3-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.5 14.3 14.2 8.3-.5 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.2 14.1 8.3-.4 14.3 14.1 8.3-.4 14.3 14.1 8.2V900z" style="fill:#ffb7ab"/>
|
||||
</svg>'
|
||||
---
|
||||
<div class="infobox">
|
||||
<p>Client: <a href="https://www.adidas-group.com/en/">Adidas E-commerce dept</a></p>
|
||||
Tasks:
|
||||
<ul>
|
||||
<li>Visual Design</li>
|
||||
<li>Campaign asset production</li>
|
||||
<li>Illustration</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p><span class="drop_cap">A</span>didas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.</p>
|
||||
</div>
|
||||
9
src/routes/work/md/adidas.md
Normal file
9
src/routes/work/md/adidas.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Adidas
|
||||
header_bg_image: /work/adidas/adidas_hero.jpg
|
||||
order: 12
|
||||
description: Adidas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
svg: '<svg id="adidas-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 1000 674.2" style="enable-background:new 0 0 1000 674.2; fill: currentColor" xml:space="preserve"><path d="M654.5 442.2 448.9 84.8 596.6 0l255.7 442.2H654.5"/><path d="m106.8 392.1 147.7-85.4 78.2 135.5H135.5l-28.7-50.1"/><path d="M396.7 670.2h42V500.1h-42v170.1z"/><path d="M923.6 674.2c-47 0-75.3-24.3-76.8-58.5h44.3c0 10.7 6.7 26.4 35.4 26.9 19.1 0 28.1-11.3 28.1-19.7-1.1-13.4-18-14.5-35.9-17.4-18-2.9-33.3-6.1-44.3-11.8-14.1-7.3-23.7-22.9-23.7-40.9 0-30.4 26.4-54.5 70.3-54.5 42.6 0 69.6 22.4 72.4 55.6h-42.8c-.4-9-2.1-23.1-27.3-23.1-17 0-28.3 3.4-29.2 15.3 0 17.4 35.4 16.2 62.9 23.5 26.4 6.7 43.2 23.1 43.2 46.1-.2 42.2-34.4 58.5-76.6 58.5"/><path d="m280 240.4 147.7-85.2 165.7 287H438.8v42h-42V442L280 240.4"/><path class="st0" d="M283.8 674.2c-48.9 0-88.7-39.9-88.7-88.3 0-48.9 39.7-87.5 88.7-87.5 18.5 0 35.4 5 50.1 15.1v-71.3h42v228h-42v-11.3c-14.8 9.6-31.6 15.3-50.1 15.3zm-48.4-88.3c0 26.4 22.5 48.3 49.5 48.3 26.4 0 48.9-22 48.9-48.3 0-26.4-22.5-48.9-48.9-48.9-26.9 0-49.5 22.5-49.5 48.9"/><path class="st0" d="M594.5 442.2H636v228h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3-48.3 0-88.1-39.9-88.1-88.3 0-48.9 39.7-87.5 88.1-87.5 19.1 0 35.9 5 50.6 15.1v-71.3zm-97.8 143.7c0 26.4 22.5 48.3 48.3 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-25.8 0-48.3 22.5-48.3 48.9"/><path class="st0" d="M738.2 674.2c-48.2 0-88.1-39.9-88.1-88.3 0-48.9 39.9-87.5 88.1-87.5 18.5 0 35.9 5 50.1 15.1v-13.6h42v170.3h-42v-11.3c-14.2 9.6-31 15.3-50.1 15.3zM691 585.9c0 26.4 22.5 48.3 48.9 48.3s48.3-22 48.3-48.3c0-26.4-22-48.9-48.3-48.9-26.4 0-48.9 22.5-48.9 48.9"/><path class="st0" d="M40.5 585.9c0 26.4 22.5 48.3 48.9 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-26.3 0-48.9 22.5-48.9 48.9zm47.8 88.3c-48.4 0-88.3-40-88.3-88.3 0-48.9 39.9-87.5 88.3-87.5 18.5 0 35.9 5 50.6 15.1v-13.6h41.5v170.3h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3"/></svg>'
|
||||
---
|
||||
Adidas, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
|
||||
15
src/routes/work/md/formo.md
Normal file
15
src/routes/work/md/formo.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
title: Formo.bio
|
||||
header_bg_image: /work/formo/formo_hero.png
|
||||
video: /work/formo/formo-rec.webm
|
||||
order: 12
|
||||
description: Formo, as you might have heard, is an international sports and lifestyle clothing brand. I worked on several digital campaigns as a graphic designer and illustrator.
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 70 20" style="enable-background:new 0 0 70 20" xml:space="preserve"><path d="m3.4 5.6 7.9-3.3c.1 0 .1-.1.1-.2l-.9-2c-.1-.1-.2-.1-.2-.1L2.4 3.3C.9 3.9 0 5.3 0 6.9v12.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.7c.2.1.3.1.5.2l7.2 1.8c.1 0 .1 0 .1-.1l.6-2.1c0-.1 0-.1-.1-.1L3.6 8.3C2.7 8 2.5 7.2 2.5 7s0-1 .9-1.4zm59.7.7c-3.8 0-6.9 3-6.9 6.8s3.1 6.8 6.9 6.8 6.9-3 6.9-6.8-3.1-6.8-6.9-6.8zm0 11.1c-2.4 0-4.4-1.9-4.4-4.3s2-4.3 4.4-4.3 4.4 1.9 4.4 4.3-2 4.3-4.4 4.3zM18.8 6.3c-3.8 0-6.9 3-6.9 6.8s3.1 6.8 6.9 6.8 6.9-3 6.9-6.8-3.1-6.8-6.9-6.8zm0 11.1c-2.4 0-4.4-1.9-4.4-4.3s2-4.3 4.4-4.3 4.4 1.9 4.4 4.3-2 4.3-4.4 4.3zM53.1 7.1c-.9-.7-2-.9-3-.6l-3.2.9c-.3.1-.6.2-.9.4-.2-.3-.4-.5-.7-.7-.9-.7-2-.9-3-.6l-3.3.9c-1.6.4-2.7 1.9-2.7 3.5v8.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.3c0-.5.4-1 .9-1.1l3.2-.9c.4-.1.7.1.8.2.3.2.4.5.4.8v9.4c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1V11c0-.5.4-1 .9-1.1l3.2-.9c.4-.1.7.1.8.2.3.2.4.5.4.8v9.4c0 .1.1.1.1.1H54c.1 0 .1 0 .1-.1V9.9c.4-1.1-.1-2.1-1-2.8zm-23 .3c-1.6.4-2.7 1.9-2.7 3.5v8.3c0 .1.1.1.1.1h2.2c.1 0 .1-.1.1-.1v-8.3c0-.5.4-1 .9-1.1l4.6-1.3c.1 0 .1-.1.1-.1l-.6-2.1c0-.1-.1-.1-.1-.1l-4.6 1.2z" style="fill:currentColor"/></svg>'
|
||||
---
|
||||
## A website for the future of dairy.
|
||||
|
||||
Formo is using precision fermentation instead of cows to make dairy products and save the world.
|
||||
|
||||
Formo's previous website was lacking an easy way for the team to create new, visually engaging content without technical knowledge. After consulting with the team, we decided to use the latest version of WordPress which offers full support for the powerful Gutenberg editor.
|
||||
|
||||
We built custom content blocks for the team to use and combine when creating new pages and articles.
|
||||
80
src/routes/work/work.scss
Normal file
80
src/routes/work/work.scss
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
h1 {
|
||||
font-size: 12vw;
|
||||
font-style: italic;
|
||||
margin: 0.5em 0 0.5em 0;
|
||||
letter-spacing: -0.04em;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 7vw;
|
||||
}
|
||||
|
||||
& span {
|
||||
display: inline-block;
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.025em;
|
||||
// font-weight: 400;
|
||||
font-size: 2.5vw;
|
||||
// visibility: hidden;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: .5em;
|
||||
bottom: .5em;
|
||||
transform: scale(0.8) translateY(100%);
|
||||
margin: 0;
|
||||
padding: 0.1em 0.4em 0.2em 0.4em;
|
||||
// border-radius: .25em;
|
||||
text-align: center;
|
||||
transform-origin: center center;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
.works .work:not(.active):hover & {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
.works {
|
||||
padding: 15vw 0.5em 0.5em 0.5em;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
flex-wrap: wrap;
|
||||
@media screen and (min-width: 768px) {
|
||||
gap: 0.25em;
|
||||
padding: 8.5vw 0.5em 0.5em 0.5em;
|
||||
width: 100%;
|
||||
padding-bottom: 20svh;
|
||||
}
|
||||
}
|
||||
.work {
|
||||
flex: 0 0 100%;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
flex: 0 0 calc(33% - .125em);
|
||||
|
||||
}
|
||||
}
|
||||
.workhero {
|
||||
width: 100%;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
object-fit: fill;
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
}
|
||||
106
src/routes/work/workUtils.ts
Normal file
106
src/routes/work/workUtils.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import gsap from 'gsap';
|
||||
import SplitText from 'gsap/dist/SplitText';
|
||||
import { workbulge } from '$lib/utils/stores';
|
||||
|
||||
const bulge = {factor: 0};
|
||||
workbulge.subscribe(value => {
|
||||
bulge.factor = value;
|
||||
})
|
||||
|
||||
export function workClickHandler(e:Event){
|
||||
e.preventDefault();
|
||||
const target = e.target as HTMLElement;
|
||||
const originalLink = target.getAttribute('href') as string;
|
||||
const targetImg = target.querySelector('.workhero') as HTMLElement;
|
||||
const targetImgRect = targetImg.getBoundingClientRect();
|
||||
const targetImgAspectRatio = targetImgRect.width / targetImgRect.height;
|
||||
target.classList.add('active');
|
||||
targetImg.style.width = `${targetImgRect.width}px`;
|
||||
targetImg.style.height = `${targetImgRect.height}px`;
|
||||
targetImg.style.top = `${targetImgRect.top}px`;
|
||||
targetImg.style.left = `${targetImgRect.left}px`;
|
||||
targetImg.style.position = 'fixed';
|
||||
|
||||
gsap.to('.work:not(.active) .workhero', {
|
||||
duration: .3,
|
||||
opacity: 0,
|
||||
yPercent: 10,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
gsap.set(targetImg, {
|
||||
zIndex: 100,
|
||||
})
|
||||
gsap.to(targetImg, {
|
||||
duration: .3,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerWidth / targetImgAspectRatio,
|
||||
ease: 'circ.inOut',
|
||||
delay: 0.3,
|
||||
})
|
||||
gsap.to(bulge, {
|
||||
duration: .3,
|
||||
factor: 0,
|
||||
ease: 'circ.inOut',
|
||||
onUpdate: () => {
|
||||
workbulge.set(bulge.factor);
|
||||
}
|
||||
})
|
||||
gsap.to('h1', {
|
||||
duration: .3,
|
||||
yPercent: -200,
|
||||
ease: 'circ.inOut',
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
goto(originalLink);
|
||||
}, 600);
|
||||
}
|
||||
|
||||
export function initWorkPage( h1: HTMLElement, canvasImgElems: Array<HTMLElement>) {
|
||||
|
||||
workbulge.set(0.25);
|
||||
|
||||
gsap.registerPlugin( SplitText );
|
||||
|
||||
console.log('initWorkPage:', h1);
|
||||
const h1Text = h1?.innerHTML || '';
|
||||
h1.innerHTML = h1Text + h1Text + h1Text + h1Text || '';
|
||||
h1.style.overflow = 'hidden';
|
||||
h1.style.whiteSpace = 'nowrap';
|
||||
|
||||
h1.querySelectorAll('span').forEach((span) => {
|
||||
gsap.to(span, {xPercent: -100, duration: 4, ease: 'none', repeat: -1 })
|
||||
});
|
||||
|
||||
const split = new SplitText(h1, { type: 'words', wordsClass: 'words' });
|
||||
|
||||
gsap.set(split.words, {
|
||||
opacity: 0, yPercent: -10
|
||||
})
|
||||
gsap.to(split.words, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
yPercent: 0,
|
||||
stagger: 0.1,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
|
||||
gsap.set(canvasImgElems, {
|
||||
opacity: 1, yPercent: 100
|
||||
})
|
||||
gsap.to(canvasImgElems, {
|
||||
duration: 1,
|
||||
opacity: 1,
|
||||
yPercent: 0,
|
||||
stagger: 0.05,
|
||||
ease: 'power4.out',
|
||||
})
|
||||
|
||||
return {
|
||||
text: Array.from(split.words),
|
||||
images: canvasImgElems
|
||||
}
|
||||
}
|
||||
BIN
static/cursor.png
Normal file
BIN
static/cursor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 753 B |
1
static/cursor.svg
Normal file
1
static/cursor.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 50" style="enable-background:new 0 0 40 50" xml:space="preserve" width="40" height="50"><path style="fill:#ff9494;stroke:#00117f;stroke-width:3;stroke-miterlimit:10" d="M5.4 5 5 44.6l13.1-10.9L35 31.2z"/></svg>
|
||||
|
After Width: | Height: | Size: 266 B |
BIN
static/pointer.png
Normal file
BIN
static/pointer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
1
static/pointer.svg
Normal file
1
static/pointer.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 50" style="enable-background:new 0 0 40 50" xml:space="preserve" width="40" height="50"><path d="m17.2 12.2-5.7-8.1c-1.3-1.9-3.9-2.3-5.7-1l-.2.2C3.7 4.6 3.3 7.2 4.6 9l6.3 8.9c-3 1.7-2.4 4.8-2.4 4.8-2.2.8-3.2 2.4-3 4.8 0 0-3.9 2.1-3.1 6.1l6.4 9.1c3.5 5 10.4 6.2 15.3 2.7l4.6-3.3c3-2.1 4.7-5.5 4.7-9.1-.1-6.1 1-8.2-2-13.5l-4-9.6c-.6-1.1-1.8-1.6-3-1.3l-.7.2c-1.2.3-2 1.4-2 2.6l.4 7.6-4.9-6.8z" style="fill:#ff9494;stroke:#00117f;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:10"/></svg>
|
||||
|
After Width: | Height: | Size: 548 B |
BIN
static/work/formo/formo-rec.mp4
Normal file
BIN
static/work/formo/formo-rec.mp4
Normal file
Binary file not shown.
BIN
static/work/formo/formo-rec.webm
Normal file
BIN
static/work/formo/formo-rec.webm
Normal file
Binary file not shown.
|
|
@ -1,5 +1,6 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
import preprocess from 'svelte-preprocess';
|
||||
import { mdsvex } from 'mdsvex'
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
|
|
@ -7,6 +8,7 @@ const config = {
|
|||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: [
|
||||
preprocess(),
|
||||
vitePreprocess(),
|
||||
mdsvex({
|
||||
extensions: ['.md']
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue