add first draft for work examples

This commit is contained in:
saiminh 2023-08-15 21:22:08 +02:00
parent 46d559784a
commit 5faecf4f25
18 changed files with 609 additions and 106 deletions

View file

@ -7,37 +7,28 @@ import SplitText from 'gsap/dist/SplitText';
import { PixiPlugin } from "gsap/dist/PixiPlugin";
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';
import createCanvasText from '$lib/utils/creareCanvasText';
import createCanvasText from '$lib/utils/createCanvasText';
import createCanvasImg from '$lib/utils/createCanvasImg';
import { tick } from 'svelte';
export let textsToCanvas: NodeListOf<Element>;
export let textsToCanvas: Array<HTMLElement> = [];
export let imgsToCanvas: Array<HTMLElement> = [];
let app: PIXI.Application;
let canvas: HTMLCanvasElement;
onMount(()=>{
gsap.registerPlugin(PixiPlugin, ScrollTrigger, SplitText);
if (document.querySelector('.homeCanvas')) {
app = new PIXI.Application({
resizeTo: window,
antialias: true,
autoDensity: true,
resolution: 2,
backgroundAlpha: 0,
view: document.querySelector('.homeCanvas') as HTMLCanvasElement,
view: canvas,
});
}
else {
app = new PIXI.Application({
resizeTo: window,
antialias: true,
autoDensity: true,
resolution: 2,
backgroundAlpha: 0,
});
let canvaselem = document.body.appendChild(app.view as HTMLCanvasElement);
canvaselem.classList.add('homeCanvas');
}
//for debugging but Typescript has an issue with this:
// globalThis.__PIXI_APP__ = app as any;
@ -86,12 +77,11 @@ onMount(()=>{
async function convertText(){
await tick();
textsToCanvas.forEach((element) => {
elems.push(element as HTMLElement);
let canvasText = createCanvasText(element as HTMLElement, app.stage);
canvasTexts.push(canvasText as PIXI.Text);
elems.push(element);
let canvasText = createCanvasText(element, app.stage);
canvasTexts.push(canvasText);
})
}
convertText();
/*----------------------------------
@ -106,11 +96,45 @@ onMount(()=>{
})
}
/*----------------------------------
* Convert images to canvas
* createCanvacImgs function
----------------------------------*/
let canvasImgs: Array<PIXI.Sprite> = [];
let imgElems: Array<HTMLElement> = [];
function convertImgs(){
imgsToCanvas.forEach((element) => {
imgElems.push(element);
let canvasImg = createCanvasImg(element, app.stage);
canvasImgs.push(canvasImg);
})
}
setTimeout(() => {
convertImgs();
convertText();
}, 100);
// convertImgs();
// convertText();
/*----------------------------------
* Function to update text on canvas
* runs in the Ticker
----------------------------------*/
function updateImgs(){
canvasImgs.forEach((image, index) => {
let imagePosition = imgElems[index].getBoundingClientRect();
image.position.set(imagePosition.x, imagePosition.y);
image.width = imagePosition.width;
image.height = imagePosition.height;
})
}
/*----------------------------------
* Mousemove events
*----------------------------------*/
window.addEventListener('pointermove', (e) => {
window.addEventListener('mousemove', (e) => {
const pointerX = e.clientX / window.innerWidth;
const pointerY = e.clientY / window.innerHeight;
const pointerXfrac = pointerX - 0.5;
@ -128,9 +152,9 @@ onMount(()=>{
app.ticker.add((delta) => {
elapsed += delta;
bulgefilter.center = [(center[0] + Math.sin(elapsed/200)/20 ),(center[1] + Math.cos(elapsed/200)/20 )];
updateImgs();
updateText();
})
console.log('HomeCanvas mounted', textsToCanvas);
}) // <- end onMount
onDestroy(() => {
@ -140,3 +164,14 @@ onDestroy(() => {
}) // <- end onDestroy
</script>
<canvas bind:this={canvas}></canvas>
<style>
canvas {
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
</style>

View file

@ -0,0 +1,205 @@
<script lang="ts">
import * as PIXI from 'pixi.js';
import { RGBSplitFilter, BulgePinchFilter } from 'pixi-filters';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/dist/ScrollTrigger';
import SplitText from 'gsap/dist/SplitText';
import { PixiPlugin } from "gsap/dist/PixiPlugin";
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';
import createCanvasText from '$lib/utils/createCanvasText';
import createCanvasImg from '$lib/utils/createCanvasImg';
import { tick } from 'svelte';
export let textsToCanvas: Array<HTMLElement> = [];
export let imgsToCanvas: Array<HTMLElement> = [];
export let bulgeFactor: number = 0.15;
let app: PIXI.Application;
let canvas: HTMLCanvasElement;
onMount(()=>{
gsap.registerPlugin(PixiPlugin, ScrollTrigger, SplitText);
app = new PIXI.Application({
resizeTo: window,
antialias: true,
autoDensity: true,
resolution: 2,
backgroundAlpha: 0,
view: canvas,
});
//for debugging but Typescript has an issue with this:
// globalThis.__PIXI_APP__ = app as any;
PixiPlugin.registerPIXI(PIXI);
function xFrac(x: number){
return window.innerWidth * x;
}
function yFrac(y: number){
return window.innerHeight * y;
}
let group = new PIXI.Container();
group.pivot.set(window.innerWidth / 2, window.innerHeight / 2);
group.x = window.innerWidth / 2;
group.y = window.innerHeight / 2;
app.stage.addChild(group);
let recty = new PIXI.Graphics();
recty.beginFill('rgb(0, 0, 0)');
recty.drawRect(0, 0, xFrac(1), yFrac(1));
recty.endFill();
recty.alpha = 0;
recty.pivot.set(xFrac(.5), yFrac(.5));
recty.x = xFrac(0.5);
recty.y = yFrac(0.5);
group.addChild(recty);
let center = [0.5, 0.5];
let bulgefilter = new BulgePinchFilter();
bulgefilter.radius = xFrac(0.6);
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;
app.stage.filters = [bulgefilter];
/*----------------------------------
* Convert text to canvas using
* createCanvasText function
----------------------------------*/
let canvasTexts: Array<PIXI.Text> = [];
let elems: Array<HTMLElement> = [];
async function convertText(){
await tick();
textsToCanvas.forEach((element) => {
elems.push(element);
let canvasText = createCanvasText(element, app.stage);
canvasTexts.push(canvasText);
})
}
/*----------------------------------
* Function to update text on canvas
* runs in the Ticker
----------------------------------*/
function updateText(){
canvasTexts.forEach((text, index) => {
let headlinePosition = elems[index].getBoundingClientRect();
text.position.set(headlinePosition.x, headlinePosition.y);
text.alpha = elems[index].style.opacity as unknown as number;
})
}
/*----------------------------------
* Convert images to canvas
* createCanvacImgs function
----------------------------------*/
let canvasImgs: Array<PIXI.Sprite> = [];
let imgElems: Array<HTMLElement> = [];
function convertImgs(){
imgsToCanvas.forEach((element) => {
imgElems.push(element);
let canvasImg = createCanvasImg(element, app.stage);
// canvasImg.tint = 0xff9494;
canvasImgs.push(canvasImg);
})
}
/*----------------------------------
* Function to update text on canvas
* runs in the Ticker
----------------------------------*/
function updateImgs(){
canvasImgs.forEach((image, index) => {
let imagePosition = imgElems[index].getBoundingClientRect();
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.alpha = window.getComputedStyle(imgElems[index]).opacity as unknown as number;
})
}
setTimeout(() => {
convertImgs();
convertText();
}, 100);
// convertImgs();
// convertText();
/*----------------------------------
* Mousemove events
*----------------------------------*/
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];
gsap.to(tween, {
duration: .5,
ease: 'power3.out',
overwrite: true,
x: pointerX,
y: pointerY,
})
})
/*----------------------------------
* The Ticker
* ----------------------------------*/
let elapsed = 0.0;
app.ticker.add((delta) => {
elapsed += delta;
// bulgefilter.center = center;
// bulgefilter.center = [(center[0] + Math.sin(elapsed/200)/20 ),(center[1] + Math.cos(elapsed/200)/20 )];
bulgefilter.center = [tween.x, tween.y];
bulgefilter.strength = bulgeFactor;
updateImgs();
updateText();
})
}) // <- end onMount
onDestroy(() => {
if (browser){
app.destroy(true, true);
}
}) // <- end onDestroy
</script>
<canvas bind:this={canvas}></canvas>
<style>
canvas {
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
</style>

View file

@ -4,6 +4,7 @@ type Post = {
date: string
description: string
tags: string[]
header_bg_image: string
}
default: {
render: () => string

View file

@ -15,12 +15,6 @@ body {
body * {
box-sizing: border-box;
}
canvas {
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
h1, h2, h3, h4, h5 {
font-size: 1em;
font-weight: 900;

View file

@ -0,0 +1,19 @@
import * as PIXI from 'pixi.js';
export default function createCanvasText( element: HTMLElement, stage: PIXI.Container ){
const elem = element;
// console.log(elem);
const elemSrc = elem.getAttribute('src') || '';
const elemPosition = elem.getBoundingClientRect();
const canvasImg = PIXI.Sprite.from(elemSrc);
canvasImg.position.set(elemPosition.x, elemPosition.y);
canvasImg.width = elemPosition.width;
canvasImg.height = elemPosition.height;
stage.addChild(canvasImg);
// elem.style.opacity = '0';
elem.style.visibility = 'hidden';
return canvasImg;
}

View file

@ -6,7 +6,9 @@ export default function createCanvasText( element: HTMLElement, stage: PIXI.Con
const elem = element;
const elemStyles = window.getComputedStyle(elem);
const elemFontSize = elemStyles.getPropertyValue('font-size');
const elemFontWeight = elemStyles.getPropertyValue('font-weight');
const elemFontFamily = elemStyles.getPropertyValue('font-family');
const elemFontStyle = elemStyles.getPropertyValue('font-style');
const elemLetterSpacing = parseInt(elemStyles.getPropertyValue('letter-spacing'));
const elemColor = elemStyles.getPropertyValue('color');
const elemAlignment = elemStyles.getPropertyValue('text-align');
@ -15,11 +17,14 @@ export default function createCanvasText( element: HTMLElement, stage: PIXI.Con
const canvasText = new Text(elem?.textContent as string, {
fontFamily: elemFontFamily,
fontSize: elemFontSize,
fontWeight: elemFontWeight as PIXI.TextStyleFontWeight,
fontStyle: elemFontStyle as PIXI.TextStyleFontStyle,
letterSpacing: elemLetterSpacing,
fill: elemColor,
align: elemAlignment as PIXI.TextStyleAlign,
});
canvasText.position.set(elemPosition.x, elemPosition.y);
// canvasText.zIndex = 100;
stage.addChild(canvasText);
elem.style.opacity = '0';

View file

@ -6,30 +6,33 @@
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'});
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.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'});
gsap.to('.content > *', {duration: 0.5, y: '0%', autoAlpha: 1, ease: 'power4.out'});
}
})
});
</script>
<a href='/' class="logo">
<a href='/' class="logo">
<Logo />
</a>
<header>
</a>
<header>
<input aria-hidden="true" type="checkbox" id="menustate" />
<label for="menustate" aria-hidden="true">
<span class="open"></span>
@ -41,7 +44,7 @@
<a href="/work">About</a>
<a href="/work">Hire</a>
</nav>
</header>
</header>
<div class="content">
<slot />
@ -52,13 +55,24 @@
position: fixed;
bottom: 0;
right: 0;
z-index: 1;
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;
@ -95,7 +109,7 @@
width: auto;
position: fixed;
bottom: 0;
z-index: 2;
z-index: 4;
display: flex;
margin: var(--spacing-outer);
}

View file

@ -5,9 +5,8 @@
import SplitText from 'gsap/dist/SplitText';
import { onMount } from 'svelte';
let canvasElems: NodeListOf<Element>;
let canvasElems: Array<HTMLElement>;
onMount(() => {
console.log('Home mounted');
gsap.registerPlugin( ScrollTrigger, SplitText );
@ -51,7 +50,7 @@
})
}
})
canvasElems = document.querySelectorAll('.lineChildren') as NodeListOf<Element>;
canvasElems = Array.from(document.querySelectorAll('.lineChildren'));
});
</script>
@ -98,13 +97,7 @@
</div>
</section>
</article>
{#await onMount}
waiting
{:then}
<HomeCanvas textsToCanvas={canvasElems}/>
{:catch error}
<p>error</p>
{/await}
<style lang="scss">
article {
@ -142,11 +135,11 @@
h1 {
letter-spacing: -0.05em;
line-height: .9;
font-size: 2.5em;
font-size: 17vw;
margin-top: -.5em;
text-align: center;
@media screen and (min-width: 768px) {
font-size: 2.7em;
font-size: 12vw;
}
}
h2 {

View file

@ -1,59 +1,222 @@
<script lang="ts">
import HomeCanvas from '$lib/components/HomeCanvas.svelte';
import { onMount } from 'svelte';
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';
export let data;
let canvasElems: NodeListOf<Element>;
onMount(() => {
canvasElems = document.querySelectorAll('h1') as NodeListOf<Element>;
// console.log('Work mounted', canvasElems);
let canvasTextElems: Array<HTMLElement>;
let canvasImgElems: Array<HTMLElement>;
let bulge = { factor: 0.15 };
gsap.set(canvasElems, { autoAlpha: 0 });
gsap.to(canvasElems, {
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',
})
});
});
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);
}
</script>
<div class="work">
<h1>My work examples:</h1>
<h1><span>Casestudies</span></h1>
<div class="works">
{#each data.posts as work}
<a href="{work.path}" class="work" on:click={ workClickHandler }>
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
<h2>{work.meta.title}</h2>
<a href="{work.path}">Link to work</a>
</a>
{/each}
{#each data.posts as work}
<a href="{work.path}" class="work" on:click={ workClickHandler }>
<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 }>
<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 }>
<img class="workhero" src="{work.meta.header_bg_image}" alt="{work.meta.title}"/>
<h2>{work.meta.title}</h2>
</a>
{/each}
</div>
{#await onMount}
waiting
{:then}
<HomeCanvas textsToCanvas={canvasElems}/>
{:catch error}
<p>error</p>
{/await}
<WorkCanvas textsToCanvas={canvasTextElems} imgsToCanvas={canvasImgElems} bulgeFactor={bulge.factor} />
<style lang="scss">
h1, h2 {
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;
visibility: hidden;
position: relative;
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);
}
h1 {
letter-spacing: -0.05em;
line-height: .9;
font-size: 2.5em;
}
.works {
padding: 24vw 0.5em 0.5em 0.5em;
@media screen and (min-width: 768px) {
font-size: 5.7em;
padding: 14vw 0.5em 0.5em 0.5em;
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
padding-bottom: 20svh;
}
}
.work {
padding: var(--spacing-outer);
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>

View file

@ -1,12 +1,13 @@
export async function load({ params }){
try {
const post = await import(`../${params.slug}.md`)
const { title, date } = post.metadata
const { title, date, header_bg_image } = post.metadata
const Content = post.default.render()
return {
title,
date,
header_bg_image,
Content
}
} catch (error) {

View file

@ -1,13 +1,45 @@
<script>
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { fly } from 'svelte/transition';
import { cubicOut } from 'svelte/easing';
export let data;
let visible = false;
onMount(() => {
let workheros = document.querySelector('.hero');
console.log(workheros);
visible = true;
})
</script>
<article>
<h1>{data.title}</h1>
<p>{@html data.Content.html}</p>
<img class="hero" src="{data.header_bg_image}" alt="{data.title}" />
{#if visible}
<h1 in:fly>{data.title}</h1>
<p in:fly>{@html data.Content.html}</p>
{/if}
</article>
<style lang="scss">
article {
padding: var(--spacing-outer);
}
.hero {
position: fixed;
top: 0;
left:0;
width: 100%;
z-index: 0;
height: auto;
// object-fit: cover;
}
h1, p {
position: relative;
z-index: 1;
}
@media (max-width: 767px){
h1 {
margin-top: calc(100vw / 1.333);
}
}
</style>

View file

@ -1,10 +1,8 @@
---
title: Adidas
category: Visual Design
permalink: /work/adidas
header_bg_image: ./img/work_adidas/adidas_hero_bw.jpg
extra_classes: portfolio theme-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>'

21
src/routes/work/formo.md Normal file
View file

@ -0,0 +1,21 @@
---
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>

21
src/routes/work/jpl.md Normal file
View file

@ -0,0 +1,21 @@
---
title: JustPeace Labs
header_bg_image: /work/jpl/jpl_hero.jpg
description: JustPeace Labs is a non-profit organization that works with local communities to build peace and prevent violence.
order: 11
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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View file

@ -1,6 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});