Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: physics items desktop only #236

Merged
merged 6 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions components.198185.json
Original file line number Diff line number Diff line change
Expand Up @@ -2095,7 +2095,7 @@
"name": "physics-balloon-card",
"display_name": null,
"created_at": "2023-04-14T10:39:02.963Z",
"updated_at": "2023-05-12T15:06:10.366Z",
"updated_at": "2023-05-18T07:26:57.000Z",
"id": 3811407,
"schema": {
"text": {
Expand Down Expand Up @@ -2128,7 +2128,7 @@
],
"exclude_empty_option": true
},
"isDesktopOnly": {
"is_desktop_only": {
"type": "boolean"
}
},
Expand All @@ -2149,13 +2149,13 @@
"name": "physics-input",
"display_name": null,
"created_at": "2023-04-17T15:13:53.017Z",
"updated_at": "2023-05-12T15:06:42.156Z",
"updated_at": "2023-05-18T07:27:49.440Z",
"id": 3830600,
"schema": {
"placeholder": {
"type": "text"
},
"isDesktopOnly": {
"is_desktop_only": {
"type": "boolean"
}
},
Expand All @@ -2176,11 +2176,13 @@
"name": "physics-rectangle-card",
"display_name": null,
"created_at": "2023-04-14T10:37:48.156Z",
"updated_at": "2023-05-12T15:05:52.122Z",
"updated_at": "2023-05-18T07:25:20.056Z",
"id": 3811406,
"schema": {
"text": {
"type": "text"
"type": "text",
"key": "text",
"pos": 0
},
"theme": {
"type": "option",
Expand All @@ -2197,10 +2199,14 @@
"name": "Yellow"
}
],
"exclude_empty_option": true
"exclude_empty_option": true,
"key": "theme",
"pos": 1
},
"isDesktopOnly": {
"type": "boolean"
"is_desktop_only": {
"type": "boolean",
"key": "isDesktopOnly",
"pos": 2
}
},
"image": null,
Expand All @@ -2220,16 +2226,20 @@
"name": "physics-sticker",
"display_name": null,
"created_at": "2023-04-26T13:15:00.766Z",
"updated_at": "2023-05-12T15:04:09.272Z",
"updated_at": "2023-05-18T07:26:39.476Z",
"id": 3889837,
"schema": {
"photo": {
"type": "asset",
"filetypes": ["images"],
"asset_folder_id": 234280
"asset_folder_id": 234280,
"key": "photo",
"pos": 0
},
"isDesktopOnly": {
"type": "boolean"
"is_desktop_only": {
"type": "boolean",
"key": "isDesktopOnly",
"pos": 1
}
},
"image": null,
Expand Down
117 changes: 80 additions & 37 deletions src/components/physics-section.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { browser } from '$app/environment';
import { device } from '$lib/stores/device';
import clsx from 'clsx';
import { getScrollSpeed } from '$lib/utils/dom';
import BalloonCard from './physics-blocks/balloon-card.svelte';
Expand Down Expand Up @@ -33,14 +33,31 @@

// Quantity of initial items
let initialBoxesN = items?.length ?? 0;
let currentBodies = 0;

$: if (currentBodies > 0) {
const newBoxes = getBoxes(matterInstance);

// add limits to engine
matterInstance.Composite.add(engine.world, [
...newBoxes.filter((bx) => bx !== null).map((b) => b!.body)
]);

// remove old boxes from engine
boxes
.filter((bx) => bx !== null)
.forEach((box) => matterInstance.Composite.remove(engine.world, box!.body));

// update boxes state as source of truth
boxes = newBoxes;
}

// Active engine
let engine: Engine;
let isAddingBox: boolean;

// Mobile behaviour
let timeout: ReturnType<typeof setTimeout>;
let isTouchDevice: boolean;

// Limits for resize handling
let boxGround: Body;
Expand All @@ -52,11 +69,11 @@

// Items where Matter will be applied
let refs: HTMLElement[] = Array(items?.length);
let boxes: {
let boxes: ({
body: Body;
elem: HTMLElement;
render(): void;
}[];
} | null)[];

/**
* Should only react when:
Expand Down Expand Up @@ -104,6 +121,38 @@
}
}

const getBoxes = (matter: typeof Matter) => {
// Create boxes from current rendered items
const boxes = refs.map((boxRef) => {
if (getComputedStyle(boxRef).display === 'block') {
return {
body: matter.Bodies.rectangle(
containerRef.clientWidth * Math.random(),
containerRef.clientHeight / 2 - boxRef.clientHeight,
boxRef.clientWidth,
boxRef.clientHeight,
{
friction: 1
}
),
elem: boxRef,
render() {
if (boxRef?.clientHeight && boxRef?.clientWidth) {
const { x, y } = this.body.position;
this.elem.style.top = `${y - boxRef.clientHeight / 2}px`;
this.elem.style.left = `${x - boxRef.clientWidth / 2}px`;
this.elem.style.transform = `rotate(${this.body.angle}rad)`;
}
}
};
} else {
return null;
}
});

return boxes;
};

const getLimits = (matter: typeof Matter) => {
const ceil = matter.Bodies.rectangle(
containerRef.clientWidth / 2,
Expand Down Expand Up @@ -144,33 +193,13 @@
const Engine = matter.Engine,
MouseConstraint = matter.MouseConstraint,
Mouse = matter.Mouse,
Composite = matter.Composite,
Bodies = matter.Bodies;
Composite = matter.Composite;

// Create engine
engine = Engine.create({ gravity: { scale: GRAVITY_DEFAULT_VALUE } });

// Create boxes from current rendered items
const initialBoxes = refs.map((_, i) => ({
body: Bodies.rectangle(
containerRef.clientWidth * Math.random(),
containerRef.clientHeight / 2 - refs[i].clientHeight,
refs[i].clientWidth,
refs[i].clientHeight,
{
friction: 1
}
),
elem: refs[i],
render() {
if (refs?.[i]?.clientHeight && refs?.[i]?.clientWidth) {
const { x, y } = this.body.position;
this.elem.style.top = `${y - refs[i].clientHeight / 2}px`;
this.elem.style.left = `${x - refs[i].clientWidth / 2}px`;
this.elem.style.transform = `rotate(${this.body.angle}rad)`;
}
}
}));
const initialBoxes = getBoxes(matter);

// Update boxes state with created boxes
boxes = initialBoxes;
Expand Down Expand Up @@ -225,7 +254,7 @@

// Add all of the bodies and walls to the world
Composite.add(engine.world, [
...initialBoxes.map((b) => b.body),
...initialBoxes.filter((bx) => bx !== null).map((b) => b!.body),
leftWall,
rightWall,
ceil,
Expand All @@ -235,13 +264,15 @@

// Main loop
(function run() {
boxes.forEach((box) => box.render());
boxes.forEach((box) => box?.render());
window.requestAnimationFrame(run);
Engine.update(engine, 1000 / 60);
})();
};

const handleResize = () => {
currentBodies = refs?.filter((ref) => getComputedStyle(ref).display === 'block').length || 0;

// re-calculate limits
const { ground, leftWall, rightWall } = getLimits(matterInstance);

Expand All @@ -261,7 +292,7 @@

// Touch devices behaved badly with element drag so we do a small gravity effect on scroll instead
const handleTouchDeviceScroll = () => {
if (isTouchDevice) {
if ($device === 'touch') {
engine.gravity = {
x: 0,
y: 1,
Expand Down Expand Up @@ -331,10 +362,6 @@
};

onMount(() => {
if (browser) {
isTouchDevice = !window.matchMedia('(hover: hover)').matches;
}

if (items) {
import('matter-js').then((Matter) => {
matterInstance = Matter;
Expand All @@ -357,13 +384,21 @@
<BalloonCard
block={item}
bind:ref={refs[i]}
class={clsx('absolute w-fit', !engine && 'invisible')}
class={clsx(
'absolute w-fit',
!engine && 'invisible',
item.is_desktop_only && 'hidden md:block'
)}
/>
{:else if item.component === 'physics-rectangle-card'}
<RectangleCard
block={item}
bind:ref={refs[i]}
class={clsx('absolute w-fit', !engine && 'invisible')}
class={clsx(
'absolute w-fit',
!engine && 'invisible',
item.is_desktop_only && 'hidden md:block'
)}
/>
{:else if item.component === 'physics-input'}
<form on:submit|preventDefault={onSubmit} id={i + item._uid}>
Expand All @@ -372,14 +407,22 @@
form={i + item._uid}
block={item}
bind:ref={refs[i]}
class={clsx('absolute w-fit', !engine && 'invisible')}
class={clsx(
'absolute w-fit',
!engine && 'invisible',
item.is_desktop_only && 'hidden md:block'
)}
/>
</form>
{:else if item.component === 'physics-sticker' && item.photo}
{@const { width, height, src, alt } = getImageAttributes(item.photo)}
<img
bind:this={refs[i]}
class={clsx('absolute select-none drop-shadow-md', !engine && 'invisible')}
class={clsx(
'absolute select-none drop-shadow-md',
!engine && 'invisible',
item.is_desktop_only && 'hidden md:block'
)}
{src}
{alt}
width={+width / 2}
Expand Down
8 changes: 4 additions & 4 deletions src/types/bloks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,15 +569,15 @@ export interface PhotoCardStoryblok {
export interface PhysicsBalloonCardStoryblok {
text?: string;
theme?: 'inverted' | 'panel' | 'offset' | 'yellow';
isDesktopOnly?: boolean;
is_desktop_only?: boolean;
_uid: string;
component: 'physics-balloon-card';
[k: string]: any;
}

export interface PhysicsInputStoryblok {
placeholder?: string;
isDesktopOnly?: boolean;
is_desktop_only?: boolean;
_uid: string;
component: 'physics-input';
[k: string]: any;
Expand All @@ -586,15 +586,15 @@ export interface PhysicsInputStoryblok {
export interface PhysicsRectangleCardStoryblok {
text?: string;
theme?: 'transparent' | 'yellow';
isDesktopOnly?: boolean;
is_desktop_only?: boolean;
_uid: string;
component: 'physics-rectangle-card';
[k: string]: any;
}

export interface PhysicsStickerStoryblok {
photo?: AssetStoryblok;
isDesktopOnly?: boolean;
is_desktop_only?: boolean;
_uid: string;
component: 'physics-sticker';
[k: string]: any;
Expand Down