267 lines
No EOL
7.6 KiB
Svelte
267 lines
No EOL
7.6 KiB
Svelte
<script lang="ts">
|
|
import '$lib/utils/pixiInit'; // Initialize PixiJS settings before importing filters
|
|
import * as PIXI from 'pixi.js';
|
|
import * as filters from 'pixi-filters';
|
|
import gsap from 'gsap';
|
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
import { SplitText } from 'gsap/SplitText';
|
|
import { PixiPlugin } from "gsap/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';
|
|
|
|
let {
|
|
textsToCanvas = [],
|
|
imgsToCanvas = []
|
|
}: {
|
|
textsToCanvas?: Array<HTMLElement>;
|
|
imgsToCanvas?: Array<HTMLElement>;
|
|
} = $props();
|
|
let app: PIXI.Application;
|
|
let canvas: HTMLCanvasElement;
|
|
|
|
onMount(()=>{
|
|
let cleanup: (() => void) | undefined;
|
|
|
|
(async ()=>{
|
|
|
|
function xFrac(x: number){
|
|
return window.innerWidth * x;
|
|
}
|
|
function yFrac(y: number){
|
|
return window.innerHeight * y;
|
|
}
|
|
|
|
let is_fine = window.matchMedia('(pointer:fine)').matches
|
|
let is_coarse = window.matchMedia('(pointer:coarse)').matches
|
|
let is_landscape = window.matchMedia('(orientation:landscape)').matches
|
|
let is_portrait = window.matchMedia('(orientation:portrait)').matches
|
|
let is_twisted = false;
|
|
|
|
gsap.registerPlugin(PixiPlugin, ScrollTrigger, SplitText);
|
|
|
|
app = new PIXI.Application();
|
|
await app.init({
|
|
canvas: canvas,
|
|
resizeTo: window,
|
|
antialias: true,
|
|
autoDensity: true,
|
|
resolution: 2,
|
|
backgroundAlpha: 0,
|
|
});
|
|
|
|
//for debugging but Typescript has an issue with this:
|
|
(globalThis as any).__PIXI_APP__ = app;
|
|
|
|
PixiPlugin.registerPIXI(PIXI);
|
|
|
|
let bulgegroup = new PIXI.Container();
|
|
bulgegroup.pivot.set(xFrac(0.5), yFrac(0.5));
|
|
bulgegroup.x = xFrac(0.5);
|
|
bulgegroup.y = yFrac(0.5);
|
|
app.stage.addChild(bulgegroup);
|
|
|
|
let bulgebg = new PIXI.Graphics();
|
|
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);
|
|
bulgebg.y = yFrac(0.5);
|
|
bulgegroup.addChild(bulgebg);
|
|
|
|
let center = [0.5, 0.5];
|
|
|
|
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 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);
|
|
twistfilter.resolution = 2;
|
|
|
|
bulgegroup.filters = [(bulgefilter as unknown as PIXI.Filter), (twistfilter as unknown as PIXI.Filter)];
|
|
|
|
gsap.to(twistfilter, {
|
|
angle: -1.33,
|
|
duration: 1,
|
|
ease: 'elastic.out(2, 0.4)',
|
|
delay: 2,
|
|
onComplete: () => {
|
|
is_twisted = true;
|
|
}
|
|
})
|
|
|
|
|
|
/*----------------------------------
|
|
* 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, bulgegroup);
|
|
canvasTexts.push(canvasText);
|
|
})
|
|
}
|
|
|
|
/*----------------------------------
|
|
* Function to update text on canvas
|
|
* runs in the Ticker
|
|
----------------------------------*/
|
|
function updateText(){
|
|
canvasTexts.forEach((text, index) => {
|
|
let headlinePosition = elems[index].getBoundingClientRect();
|
|
headlinePosition.x = headlinePosition.x ;
|
|
headlinePosition.y = headlinePosition.y ;
|
|
text.position.set(headlinePosition.x, headlinePosition.y);
|
|
text.alpha = elems[index].style.opacity as unknown as number;
|
|
text.style.fill = window.getComputedStyle(elems[index]).color;
|
|
})
|
|
}
|
|
|
|
/*----------------------------------
|
|
* 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 as HTMLImageElement, app.stage);
|
|
canvasImgs.push(canvasImg);
|
|
})
|
|
}
|
|
|
|
setTimeout(() => {
|
|
convertImgs();
|
|
convertText();
|
|
}, 100);
|
|
|
|
/*----------------------------------
|
|
* Function to update text on canvas
|
|
* runs in the Ticker
|
|
----------------------------------*/
|
|
function updateImgs(){
|
|
canvasImgs.forEach((image, index) => {
|
|
let imagePosition = imgElems[index].getBoundingClientRect();
|
|
imagePosition.x = imagePosition.x + xFrac(0.1);
|
|
imagePosition.y = imagePosition.y + yFrac(0.1);
|
|
image.position.set(imagePosition.x, imagePosition.y);
|
|
image.width = imagePosition.width;
|
|
image.height = imagePosition.height;
|
|
})
|
|
}
|
|
|
|
/*----------------------------------
|
|
* Mousemove events
|
|
*----------------------------------*/
|
|
let tween = {
|
|
x: 0.5,
|
|
y: is_fine ? 0.5 : 250/window.innerHeight,
|
|
};
|
|
const mouse = {
|
|
x: xFrac(0.5),
|
|
y: yFrac(0.5),
|
|
}
|
|
|
|
if (is_fine) {
|
|
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;
|
|
|
|
if (is_twisted){
|
|
gsap.to(mouse, {
|
|
duration: 0.5,
|
|
ease: 'power3.out',
|
|
overwrite: true,
|
|
x: e.clientX,
|
|
y: e.clientY,
|
|
})
|
|
}
|
|
|
|
gsap.to(tween, {
|
|
duration: .5,
|
|
ease: 'power3.out',
|
|
overwrite: true,
|
|
x: 0.5 + pointerXfrac/2,
|
|
y: 0.5 + pointerYfrac/6,
|
|
})
|
|
}, { passive: true })
|
|
}
|
|
|
|
|
|
/*----------------------------------
|
|
* The Ticker
|
|
* ----------------------------------*/
|
|
let elapsed = 0.0;
|
|
|
|
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 )];
|
|
bulgefilter.center = movingCenter;
|
|
twistfilter.offset = new PIXI.Point(
|
|
window.innerWidth * movingCenter[0] ,
|
|
window.innerHeight * movingCenter[1]
|
|
);
|
|
// bulgefilter.center = [0.5, 0.25];
|
|
} else {
|
|
bulgefilter.center = [tween.x, tween.y];
|
|
is_twisted ? twistfilter.offset = new PIXI.Point(mouse.x, window.innerHeight/3) : null;
|
|
}
|
|
updateImgs();
|
|
updateText();
|
|
})
|
|
|
|
window.addEventListener('resize', (e) => {
|
|
tween.y = is_fine ? 0.5 : 250/window.innerHeight;
|
|
})
|
|
|
|
cleanup = () => {
|
|
gsap.killTweensOf(imgElems);
|
|
gsap.killTweensOf(elems);
|
|
ScrollTrigger.getAll().forEach( instance => instance.kill() );
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
if (cleanup) cleanup();
|
|
}
|
|
}) // <- 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;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
z-index: -1;
|
|
}
|
|
</style> |