upgrade to pixi8
This commit is contained in:
parent
01b217a2a5
commit
2149372530
15 changed files with 211 additions and 1648 deletions
1226
package-lock.json
generated
1226
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -38,14 +38,10 @@
|
|||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@pixi/core": "^7.4.3",
|
||||
"@pixi/filter-alpha": "^7.4.3",
|
||||
"@pixi/filter-blur": "^7.4.3",
|
||||
"@pixi/unsafe-eval": "^7.4.3",
|
||||
"gsap": "^3.13.0",
|
||||
"mdsvex": "^0.11.0",
|
||||
"pixi-filters": "^5.2.1",
|
||||
"pixi.js": "^7.2.4",
|
||||
"pixi-filters": "^6.1.5",
|
||||
"pixi.js": "^8.0.0",
|
||||
"superjson": "^1.13.1",
|
||||
"svelte-cloudinary": "^1.1.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import '$lib/utils/pixiInit'; // Initialize PixiJS settings before importing filters
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { BulgePinchFilter, TwistFilter } from 'pixi-filters';
|
||||
import * as filters from 'pixi-filters';
|
||||
import gsap from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { SplitText } from 'gsap/SplitText';
|
||||
|
|
@ -23,6 +23,9 @@ let app: PIXI.Application;
|
|||
let canvas: HTMLCanvasElement;
|
||||
|
||||
onMount(()=>{
|
||||
let cleanup: (() => void) | undefined;
|
||||
|
||||
(async ()=>{
|
||||
|
||||
function xFrac(x: number){
|
||||
return window.innerWidth * x;
|
||||
|
|
@ -39,13 +42,14 @@ onMount(()=>{
|
|||
|
||||
gsap.registerPlugin(PixiPlugin, ScrollTrigger, SplitText);
|
||||
|
||||
app = new PIXI.Application({
|
||||
app = new PIXI.Application();
|
||||
await app.init({
|
||||
canvas: canvas,
|
||||
resizeTo: window,
|
||||
antialias: true,
|
||||
autoDensity: true,
|
||||
resolution: 2,
|
||||
backgroundAlpha: 0,
|
||||
view: canvas,
|
||||
});
|
||||
|
||||
//for debugging but Typescript has an issue with this:
|
||||
|
|
@ -60,9 +64,8 @@ onMount(()=>{
|
|||
app.stage.addChild(bulgegroup);
|
||||
|
||||
let bulgebg = new PIXI.Graphics();
|
||||
bulgebg.beginFill('rgb(0, 0, 0)');
|
||||
bulgebg.drawRect(0, 0, xFrac(1), yFrac(1));
|
||||
bulgebg.endFill();
|
||||
bulgebg.rect(0, 0, xFrac(1), yFrac(1));
|
||||
bulgebg.fill('rgb(0, 0, 0)');
|
||||
bulgebg.alpha = 0;
|
||||
bulgebg.pivot.set(xFrac(.5), yFrac(.5));
|
||||
bulgebg.x = xFrac(0.5);
|
||||
|
|
@ -71,13 +74,13 @@ onMount(()=>{
|
|||
|
||||
let center = [0.5, 0.5];
|
||||
|
||||
let bulgefilter = new BulgePinchFilter();
|
||||
let bulgefilter = new filters.BulgePinchFilter();
|
||||
bulgefilter.radius = is_landscape ? xFrac(0.5) : xFrac(0.55);
|
||||
bulgefilter.strength = 0.5;
|
||||
bulgefilter.center = is_landscape ? center : [0.5, 0];
|
||||
bulgefilter.resolution = 2;
|
||||
|
||||
let twistfilter = new TwistFilter();
|
||||
let twistfilter = new filters.TwistFilter();
|
||||
twistfilter.angle = 0;
|
||||
twistfilter.radius = is_landscape ? window.innerWidth/4 : window.innerWidth/2;
|
||||
twistfilter.offset = new PIXI.Point(window.innerWidth/2, window.innerHeight/3);
|
||||
|
|
@ -207,8 +210,8 @@ onMount(()=>{
|
|||
* ----------------------------------*/
|
||||
let elapsed = 0.0;
|
||||
|
||||
app.ticker.add((delta) => {
|
||||
elapsed += delta;
|
||||
app.ticker.add((ticker) => {
|
||||
elapsed += ticker.deltaTime;
|
||||
if (!is_landscape) {
|
||||
// bulgefilter.center = [(tween.x + Math.sin(elapsed/200)/20 ),(tween.y + Math.cos(elapsed/200)/20 )];
|
||||
let movingCenter = [(0.5 + Math.sin(elapsed/200)/20 ),(0.33 + Math.cos(elapsed/200)/20 )];
|
||||
|
|
@ -229,11 +232,17 @@ onMount(()=>{
|
|||
window.addEventListener('resize', (e) => {
|
||||
tween.y = is_fine ? 0.5 : 250/window.innerHeight;
|
||||
})
|
||||
return () => {
|
||||
|
||||
cleanup = () => {
|
||||
gsap.killTweensOf(imgElems);
|
||||
gsap.killTweensOf(elems);
|
||||
ScrollTrigger.getAll().forEach( instance => instance.kill() );
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
}
|
||||
}) // <- end onMount
|
||||
|
||||
onDestroy(() => {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ let app: PIXI.Application;
|
|||
let canvas: HTMLCanvasElement;
|
||||
|
||||
onMount(()=>{
|
||||
let cleanup: (() => void) | undefined;
|
||||
|
||||
(async ()=>{
|
||||
|
||||
function xFrac(x: number){
|
||||
return window.innerWidth * x;
|
||||
|
|
@ -38,13 +41,14 @@ onMount(()=>{
|
|||
|
||||
gsap.registerPlugin(PixiPlugin, ScrollTrigger, SplitText);
|
||||
|
||||
app = new PIXI.Application({
|
||||
app = new PIXI.Application();
|
||||
await app.init({
|
||||
canvas: canvas,
|
||||
resizeTo: document.querySelector('.canvasResizeToThis') as HTMLElement,
|
||||
antialias: true,
|
||||
autoDensity: true,
|
||||
resolution: 2,
|
||||
backgroundAlpha: 0,
|
||||
view: canvas
|
||||
});
|
||||
|
||||
//for debugging but Typescript has an issue with this:
|
||||
|
|
@ -59,9 +63,8 @@ onMount(()=>{
|
|||
app.stage.addChild(bulgegroup);
|
||||
|
||||
let bulgebg = new PIXI.Graphics();
|
||||
bulgebg.beginFill('rgb(0, 0, 0)');
|
||||
bulgebg.drawRect(0, 0, xFrac(1.2), yFrac(1.2));
|
||||
bulgebg.endFill();
|
||||
bulgebg.rect(0, 0, xFrac(1.2), yFrac(1.2));
|
||||
bulgebg.fill('rgb(0, 0, 0)');
|
||||
bulgebg.alpha = 0;
|
||||
bulgebg.pivot.set(xFrac(.5), yFrac(.5));
|
||||
bulgebg.x = xFrac(0.5);
|
||||
|
|
@ -202,8 +205,8 @@ onMount(()=>{
|
|||
* ----------------------------------*/
|
||||
let elapsed = 0.0;
|
||||
|
||||
app.ticker.add((delta) => {
|
||||
elapsed += delta;
|
||||
app.ticker.add((ticker) => {
|
||||
elapsed += ticker.deltaTime;
|
||||
if (is_portrait) {
|
||||
bulgefilter.center = [(0.5 + Math.sin(elapsed/200)/40 ),(0.45 + Math.cos(elapsed/200)/20 )];
|
||||
// bulgefilter.center = [0.5, 0.5];
|
||||
|
|
@ -218,11 +221,17 @@ onMount(()=>{
|
|||
window.addEventListener('resize', (e) => {
|
||||
tween.y = is_fine ? 0.5 : 250/window.innerHeight;
|
||||
})
|
||||
return () => {
|
||||
|
||||
cleanup = () => {
|
||||
gsap.killTweensOf(imgElems);
|
||||
gsap.killTweensOf(elems);
|
||||
ScrollTrigger.getAll().forEach( instance => instance.kill() );
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
}
|
||||
}) // <- end onMount
|
||||
|
||||
onDestroy(() => {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ let {
|
|||
let app: PIXI.Application;
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
||||
onMount( () => {
|
||||
onMount(() => {
|
||||
let cleanup: (() => void) | undefined;
|
||||
|
||||
(async () => {
|
||||
|
||||
let is_fine = window.matchMedia('(pointer:fine)').matches
|
||||
let is_landscape = window.matchMedia('(orientation:landscape)').matches
|
||||
|
|
@ -37,14 +40,15 @@ onMount( () => {
|
|||
const highlightColorFromRoot = () => { return getComputedStyle(root).getPropertyValue('--color-highlight') || 'rgb(0, 0, 0)' };
|
||||
const thisElemBgColor = (elem: HTMLElement) => { return getComputedStyle(elem).getPropertyValue('background-color') || 'rgb(255, 255, 255)' };
|
||||
|
||||
app = new PIXI.Application({
|
||||
app = new PIXI.Application();
|
||||
await app.init({
|
||||
canvas: canvas,
|
||||
resizeTo: window,
|
||||
antialias: true,
|
||||
autoDensity: true,
|
||||
resolution: 2,
|
||||
backgroundColor: bgColorFromRoot(),
|
||||
backgroundAlpha: 0,
|
||||
view: canvas,
|
||||
});
|
||||
|
||||
//for debugging but Typescript has an issue with this:
|
||||
|
|
@ -68,9 +72,8 @@ onMount( () => {
|
|||
let group_background = new PIXI.Graphics();
|
||||
function draw_group_background(group_background: PIXI.Graphics) {
|
||||
group_background.clear();
|
||||
group_background.beginFill(bgColorFromRoot());
|
||||
group_background.drawRect(0, 0, xFrac(1), yFrac(1));
|
||||
group_background.endFill();
|
||||
group_background.rect(0, 0, xFrac(1), yFrac(1));
|
||||
group_background.fill(bgColorFromRoot());
|
||||
group_background.alpha = 0;
|
||||
group_background.pivot.set(xFrac(.5), yFrac(.5));
|
||||
group_background.x = xFrac(0.5);
|
||||
|
|
@ -162,9 +165,8 @@ onMount( () => {
|
|||
workinfos.forEach( workinfo => {
|
||||
const workinfoRect = workinfo.getBoundingClientRect();
|
||||
let workinfoGraphic = new PIXI.Graphics();
|
||||
workinfoGraphic.beginFill(textColorFromRoot());
|
||||
workinfoGraphic.drawRect(workinfoRect.x, workinfoRect.y, workinfoRect.width, workinfoRect.height);
|
||||
workinfoGraphic.endFill();
|
||||
workinfoGraphic.rect(workinfoRect.x, workinfoRect.y, workinfoRect.width, workinfoRect.height);
|
||||
workinfoGraphic.fill(textColorFromRoot());
|
||||
workinfoGraphic.pivot.set(workinfoRect.x, workinfoRect.y);
|
||||
workinfoBgs.push(workinfoGraphic);
|
||||
app.stage.addChild(workinfoGraphic);
|
||||
|
|
@ -178,9 +180,8 @@ onMount( () => {
|
|||
workinfos.forEach((workinfo, index) => {
|
||||
const workinfoRect = workinfo.getBoundingClientRect();
|
||||
workinfoBgs[index].clear();
|
||||
workinfoBgs[index].beginFill(thisElemBgColor(workinfo as HTMLElement));
|
||||
workinfoBgs[index].drawRect(workinfoRect.x, workinfoRect.y, workinfoRect.width, workinfoRect.height);
|
||||
workinfoBgs[index].endFill();
|
||||
workinfoBgs[index].rect(workinfoRect.x, workinfoRect.y, workinfoRect.width, workinfoRect.height);
|
||||
workinfoBgs[index].fill(thisElemBgColor(workinfo as HTMLElement));
|
||||
workinfoBgs[index].pivot.set(workinfoRect.x, workinfoRect.y);
|
||||
workinfoBgs[index].position.set(workinfoRect.x - ((tween.x - 0.5) * 50), workinfoRect.y - ((tween.y - 0.5) * 50));
|
||||
workinfoBgs[index].alpha = window.getComputedStyle(workinfo).opacity as unknown as number;
|
||||
|
|
@ -224,8 +225,8 @@ onMount( () => {
|
|||
* ----------------------------------*/
|
||||
let elapsed = 0.0;
|
||||
|
||||
app.ticker.add((delta) => {
|
||||
elapsed += delta;
|
||||
app.ticker.add((ticker) => {
|
||||
elapsed += ticker.deltaTime;
|
||||
// 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];
|
||||
|
|
@ -233,7 +234,19 @@ onMount( () => {
|
|||
updateImgs();
|
||||
updateText();
|
||||
updateWorkInfoBgs();
|
||||
})
|
||||
});
|
||||
|
||||
cleanup = () => {
|
||||
gsap.killTweensOf(imgElems);
|
||||
gsap.killTweensOf(elems);
|
||||
gsap.killTweensOf(tween);
|
||||
ScrollTrigger.getAll().forEach( instance => instance.kill() );
|
||||
};
|
||||
})();
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
};
|
||||
}) // <- end onMount
|
||||
|
||||
onDestroy(() => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
:root {
|
||||
--spacing-outer: 5vw;
|
||||
--spacing-nav: 5vw;
|
||||
--color-bg: rgb(207, 63, 63);
|
||||
--color-bg: rgb(63, 111, 207);
|
||||
--color-text: rgb(255, 234, 217);
|
||||
--color-highlight: rgb(29, 12, 18);
|
||||
--aspect-ratio-heroes: 1.5;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default function createCanvasText(
|
|||
|
||||
if (elemSrc.includes('.svg')) {
|
||||
scalefactor = Number(elem.attributes.getNamedItem('data-svgscale')?.value);
|
||||
const canvasImgTexture = PIXI.Texture.from(elemSrc, { resourceOptions: { scale: scalefactor } });
|
||||
const canvasImgTexture = PIXI.Texture.from(elemSrc, true as any);
|
||||
|
||||
// Use provided sprite object or create a new one
|
||||
if (spriteObject) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export default function createCanvasText(
|
|||
// Use provided text object or create a new one
|
||||
const canvasText = textObject || new Text(elem.textContent as string, {
|
||||
fontFamily: elemFontFamily,
|
||||
fontSize: elemFontSize,
|
||||
fontSize: parseInt(elemFontSize),
|
||||
fontWeight: elemFontWeight as PIXI.TextStyleFontWeight,
|
||||
fontStyle: elemFontStyle as PIXI.TextStyleFontStyle,
|
||||
letterSpacing: elemLetterSpacing,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,4 @@ import * as PIXI from 'pixi.js';
|
|||
|
||||
// Set default resolution early to avoid deprecation warnings
|
||||
// from filter packages that use the deprecated settings.FILTER_RESOLUTION
|
||||
PIXI.Filter.defaultResolution = 2;
|
||||
|
||||
// Also set the deprecated setting for backward compatibility with older packages
|
||||
// This prevents warnings from packages like @pixi/filter-advanced-bloom that
|
||||
// still check the deprecated settings.FILTER_RESOLUTION during module initialization
|
||||
if (PIXI.settings && 'FILTER_RESOLUTION' in PIXI.settings) {
|
||||
(PIXI.settings as any).FILTER_RESOLUTION = 2;
|
||||
}
|
||||
(PIXI.Filter as any).defaultResolution = 2;
|
||||
|
|
|
|||
|
|
@ -13,21 +13,24 @@
|
|||
const textPool: PIXI.Text[] = [];
|
||||
|
||||
onMount(() => {
|
||||
let highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
|
||||
let cleanup: (() => void) | undefined;
|
||||
|
||||
let is_fine = window.matchMedia('(pointer:fine)').matches
|
||||
let is_landscape = window.matchMedia('(orientation:landscape)').matches
|
||||
(async () => {
|
||||
let highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
|
||||
let is_landscape = window.matchMedia('(orientation:landscape)').matches;
|
||||
let isDestroyed = false;
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
let app = new PIXI.Application({
|
||||
let app = new PIXI.Application();
|
||||
await app.init({
|
||||
canvas: canvas,
|
||||
resizeTo: window,
|
||||
antialias: true,
|
||||
autoDensity: true,
|
||||
resolution: 2,
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
backgroundColor: 'rgb(29, 12, 18)',
|
||||
backgroundAlpha: 0,
|
||||
view: canvas,
|
||||
});
|
||||
|
||||
let textgroup = new PIXI.Container();
|
||||
|
|
@ -134,9 +137,11 @@
|
|||
ease: 'power2.inOut',
|
||||
overwrite: true,
|
||||
onComplete: () => {
|
||||
if (!isDestroyed) {
|
||||
introDone = true;
|
||||
createScrollTrigger();
|
||||
}
|
||||
}
|
||||
})
|
||||
tweens.push(textgroupIn);
|
||||
|
||||
|
|
@ -166,6 +171,10 @@
|
|||
let scrollTriggerTweens: Array<GSAPTween> = [];
|
||||
|
||||
function createScrollTrigger() {
|
||||
|
||||
// Check if component is destroyed before creating ScrollTriggers
|
||||
if (isDestroyed) return;
|
||||
|
||||
const tween1 = gsap.to([textRows[0], textRows[1]], {
|
||||
y: '+=' + -textRows[0][0].height * 0.9,
|
||||
duration: 1,
|
||||
|
|
@ -193,8 +202,10 @@
|
|||
scrollTriggerTweens.push(tween1, tween2);
|
||||
}
|
||||
|
||||
return () => {
|
||||
cleanup = () => {
|
||||
isDestroyed = true; // Flag to prevent further actions
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
|
||||
// IMPORTANT: Kill ScrollTrigger and GSAP animations FIRST,
|
||||
// before destroying PixiJS objects they reference
|
||||
scrollTriggerTweens.forEach(tween => {
|
||||
|
|
@ -205,10 +216,23 @@
|
|||
});
|
||||
// Kill all other tweens
|
||||
tweens.forEach(tween => tween.kill());
|
||||
|
||||
// Kill orphanes ScrollTriggers
|
||||
ScrollTrigger.getAll().forEach((trigger) => {
|
||||
if (trigger.trigger === document.querySelector('article')) {
|
||||
trigger.kill();
|
||||
}
|
||||
});
|
||||
|
||||
// Return objects to pools
|
||||
allTexts.forEach(text => returnTextToPool(text));
|
||||
// Now safe to destroy PixiJS app
|
||||
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
||||
app.destroy(true, { children: true, texture: true });
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
backgroundAlpha: 0,
|
||||
resizeTo: window
|
||||
});
|
||||
PIXI.Filter.defaultResolution = window.devicePixelRatio;
|
||||
(PIXI.Filter as any).defaultResolution = window.devicePixelRatio;
|
||||
|
||||
const container = new PIXI.Container();
|
||||
app.stage.addChild(container);
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
export let data;
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
let visible = false;
|
||||
|
||||
function animForDesktop() {
|
||||
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, x: "-10%", ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
xPercent: -100,
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
delay: .2
|
||||
})
|
||||
|
||||
let heroheight = document.querySelector('.heromask')?.getBoundingClientRect().height || 100;
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 60% 0, 35% 100%, 0% 100%)",
|
||||
duration: 1,
|
||||
ease: "power4.out",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "none",
|
||||
clipPath: "polygon(0 0, 50% 0, 50% 100%, 0% 100%)",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
start: 'top top',
|
||||
end: `200px top`,
|
||||
scrub: true
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForMobile() {
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
gsap.to('.heromask, .coverclone', { duration: .6, y: -20, ease: "cubic.inOut" })
|
||||
|
||||
gsap.to('.work', {
|
||||
opacity: 1,
|
||||
yPercent: -100,
|
||||
duration: .4,
|
||||
ease: "expo.out",
|
||||
delay: .2,
|
||||
})
|
||||
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "cubic.inOut",
|
||||
onStart: () => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.coverclone')?.remove();
|
||||
}, 100);
|
||||
},
|
||||
onComplete: () => {
|
||||
gsap.to('.heromask', {
|
||||
ease: "power1.out",
|
||||
scrollTrigger: {
|
||||
trigger: '.work',
|
||||
markers: false,
|
||||
start: '0px -10px',
|
||||
end: `0px -20px`,
|
||||
scrub: false,
|
||||
onEnterBack: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 75%, 0% 100%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
},
|
||||
onEnter: () => {
|
||||
gsap.to('.heromask', {
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 0%, 0% 0%)",
|
||||
duration: .6,
|
||||
ease: "expo.out",
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function animForSize(){
|
||||
if ( window.matchMedia("(min-width: 768px) and (orientation: landscape)").matches ) {
|
||||
animForDesktop();
|
||||
} else {
|
||||
animForMobile();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
visible = true;
|
||||
|
||||
document.querySelector('.heromask img')?.addEventListener('load', () => {
|
||||
animForSize();
|
||||
|
||||
let portrait = window.matchMedia("(orientation: portrait)");
|
||||
|
||||
portrait.addEventListener("change", function(e) {
|
||||
animForSize();
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="heromask">
|
||||
<CldImage
|
||||
src={data.header_bg_image}
|
||||
alt="{data.title}"
|
||||
sizes="100vw"
|
||||
width={2100}
|
||||
height={1400}
|
||||
placeholder="blur"
|
||||
loading="eager"
|
||||
objectFit="fill"
|
||||
/>
|
||||
</div>
|
||||
<div class="subnav">
|
||||
<a href="/work" class="subnav-item">← Back</a>
|
||||
</div>
|
||||
<article class="work">
|
||||
{#if visible}
|
||||
<div class="work-content">
|
||||
{#if data.tags != undefined && data.tags.length > 0 }
|
||||
<div class="tags">
|
||||
{#each data.tags as tag }
|
||||
<div class="tag">{tag}</div>
|
||||
{ /each }
|
||||
</div>
|
||||
{/if}
|
||||
<h1><span class="svg-logo">{@html data.svg}</span><span class="name">{data.title}</span></h1>
|
||||
<div class="work-content-text">
|
||||
{@html data.Content.html}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
|
||||
<style lang="scss">
|
||||
.work {
|
||||
width: 100vw;
|
||||
min-height: 100svh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
transform: translateY(100%);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
.subnav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
padding: var(--spacing-outer);
|
||||
|
||||
& a {
|
||||
text-decoration: none;
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
}
|
||||
.heromask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left:0;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
|
||||
}
|
||||
:global(.heromask img) {
|
||||
z-index: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
margin: 0;
|
||||
object-fit: fill;
|
||||
}
|
||||
.work-content {
|
||||
padding: 0 var(--spacing-outer);
|
||||
padding-top: calc(66.6vw + 1em);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--color-text);
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
@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);
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 0 0 1em 0;
|
||||
}
|
||||
& .name {
|
||||
display: none;
|
||||
}
|
||||
& .svg-logo :global(svg) {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 250px;
|
||||
max-height: 80px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 400px;
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tags {
|
||||
padding-bottom: .25em;
|
||||
font-size: 1em;
|
||||
margin-bottom: 2em;
|
||||
line-height: 1.1;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% + var(--spacing-outer) * 2.5);
|
||||
height: 1px;
|
||||
background-color: var(--color-text);
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
.tag {
|
||||
display: inline-block;
|
||||
margin-right: .5em;
|
||||
padding: .125em 0;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -.005em;
|
||||
|
||||
&:after {
|
||||
content: ','
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none
|
||||
}
|
||||
}
|
||||
|
||||
:global(.header-nav){
|
||||
transition: all .3s cubic-bezier(0.075, 0.82, 0.165, 1);
|
||||
}
|
||||
:global(.work .header-nav){
|
||||
transform: translateY(100%);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<script lang="ts">
|
||||
import WorkCanvas from '$lib/components/WorkCanvas.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { workClickHandler, initWorkPage } from './workUtils.js';
|
||||
import { workbulge } from '$lib/utils/stores.js';
|
||||
import { CldImage } from 'svelte-cloudinary';
|
||||
export let data;
|
||||
|
||||
const orderedPosts = data.posts.sort((a, b) => {
|
||||
return a.meta.order - b.meta.order;
|
||||
});
|
||||
|
||||
let canvasTextElems: Array<HTMLElement>;
|
||||
let canvasImgElems: Array<HTMLElement>;
|
||||
let bulge = {factor: 0};
|
||||
|
||||
workbulge.subscribe((val) => {
|
||||
bulge.factor = val;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const headline: HTMLElement = document.querySelector('.headline') as HTMLElement;
|
||||
const headlines: Array<HTMLElement> = Array.from(document.querySelectorAll('h2, li'));
|
||||
const images: Array<HTMLElement> = Array.from(document.querySelectorAll('.work img'));
|
||||
let canvasElems = initWorkPage( headlines, images );
|
||||
canvasTextElems = canvasElems.text as Array<HTMLElement>;
|
||||
canvasImgElems = canvasElems.images;
|
||||
});
|
||||
</script>
|
||||
<div class="works-wrapper">
|
||||
|
||||
<h1 class="headline"><span>Outstanding work</span></h1>
|
||||
<div class="works">
|
||||
|
||||
{#each orderedPosts as work, i}
|
||||
<a
|
||||
data-sveltekit-preload-data
|
||||
href="{work.path}"
|
||||
class="work"
|
||||
on:click={ (e) => workClickHandler(e) }
|
||||
>
|
||||
<CldImage
|
||||
src={work.meta.header_bg_image}
|
||||
sizes={ i === 0 ? `(min-width: 768px) 60vw, 50vw` : `(min-width: 768px) 20vw, 50vw`}
|
||||
alt={work.meta.title}
|
||||
width="2100"
|
||||
height="1400"
|
||||
objectFit="fill"
|
||||
loading= "lazy"
|
||||
/>
|
||||
<div class="work-info">
|
||||
<h2 class="title"><span class="title-words">{work.meta.title}</span></h2>
|
||||
{#if work.meta.tags}
|
||||
<ul class="tags">
|
||||
{#each work.meta.tags as tag, index}
|
||||
<li class="tag">{tag}{index < work.meta.tags.length-1 ? ' | ' : ''}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
<WorkCanvas
|
||||
textsToCanvas={canvasTextElems}
|
||||
imgsToCanvas={canvasImgElems}
|
||||
bulgeFactor={bulge.factor}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style src="./work.scss" lang="scss"></style>
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
.works-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 16vw;
|
||||
margin: var(--spacing-nav) var(--spacing-nav) .25em var(--spacing-nav);
|
||||
padding: 0 0 calc(var(--spacing-nav) / 3) 0;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border-bottom: 1px solid;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 7vw;
|
||||
}
|
||||
}
|
||||
.works {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
padding: 0 var(--spacing-nav);
|
||||
margin: 0 auto;
|
||||
padding-bottom: 20svh;
|
||||
}
|
||||
}
|
||||
.work-info,
|
||||
.work:visited .work-info {
|
||||
position: absolute;
|
||||
left: 0.25em;
|
||||
top: 0.25em;
|
||||
padding: 0.5em .75em;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-text);
|
||||
visibility: hidden;
|
||||
|
||||
& h2 {
|
||||
text-transform: none;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.2em;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
visibility: hidden;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
& .tags {
|
||||
visibility: hidden;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 10px 0 0 0 ;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.15em;
|
||||
transition: all .4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
& .tag {
|
||||
font-size: .6em;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.01em;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
text-transform: uppercase;
|
||||
// font-weight: 800;
|
||||
// font-style: italic;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.work:hover .work-info {
|
||||
background-color: var(--color-bg);
|
||||
& h2 {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.work {
|
||||
flex: 0 0 calc(50% - 0.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;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
grid-column-end: span 1;
|
||||
|
||||
&:first-child {
|
||||
grid-column-end: span 2;
|
||||
grid-row-end: span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.work .tag {
|
||||
opacity: 0;
|
||||
}
|
||||
:global(.work img) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: var(--aspect-ratio-heroes);
|
||||
object-fit: fill;
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
opacity: 0;
|
||||
transform: translateZ(700px);
|
||||
}
|
||||
.work-logo {
|
||||
:global(.work-logo) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--color-bg);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue