add gravity easter egg
25
Caddyfile
|
@ -1,6 +1,7 @@
|
|||
(https_redirect) {
|
||||
@do_https_redirect {
|
||||
not header_regexp veryoldbrowser User-Agent Navigator|MSIE|Mosaic|Kindle|^curl|NintendoBrowser/
|
||||
not host *.onion *.*.onion
|
||||
protocol http
|
||||
}
|
||||
redir @do_https_redirect https://{host}{uri}
|
||||
|
@ -205,7 +206,7 @@ Disallow: /"
|
|||
}
|
||||
}
|
||||
}
|
||||
matrix.matdoes.dev, matrix.matdoes.dev:8448 {
|
||||
matrix.matdoes.dev matrix.matdoes.dev:8448 {
|
||||
handle /.well-known/matrix/server {
|
||||
header content-type application/json
|
||||
respond "{\"m.server\":\"matrix.matdoes.dev\"}"
|
||||
|
@ -229,7 +230,7 @@ fedi.matdoes.dev {
|
|||
}
|
||||
}
|
||||
|
||||
f.matdoes.dev {
|
||||
f.matdoes.dev media.f.matdoes.dev cache.f.matdoes.dev {
|
||||
@chrome {
|
||||
header_regexp chrome User-Agent Chrome\/[0-9./]+\s(Mobile\s)?Safari\/[0-9./]+$
|
||||
not header User-Agent *Googlebot/*
|
||||
|
@ -241,7 +242,9 @@ f.matdoes.dev {
|
|||
git.matdoes.dev {
|
||||
reverse_proxy 127.0.0.1:3000
|
||||
}
|
||||
s.matdoes.dev {
|
||||
http://s.matdoes.dev https://s.matdoes.dev {
|
||||
import https_redirect
|
||||
|
||||
reverse_proxy 127.0.0.1:28019
|
||||
}
|
||||
|
||||
|
@ -296,3 +299,19 @@ hetzner.matdoes.dev {
|
|||
xmpp.matdoes.dev {
|
||||
respond ""
|
||||
}
|
||||
|
||||
mcassets.matdoes.dev {
|
||||
reverse_proxy 127.0.0.1:10573
|
||||
}
|
||||
|
||||
www.www.matdoes.dev {
|
||||
header {
|
||||
Content-Type text/html
|
||||
Server "meow"
|
||||
}
|
||||
respond "<meta http-equiv=\"refresh\" content=\"15;url=https://matdoes.dev\">nyaaaaaaaaaaaaaaaaaaa" 200
|
||||
}
|
||||
|
||||
www.matdoes.dev {
|
||||
redir https://matdoes.dev{uri}
|
||||
}
|
22
package.json
|
@ -14,38 +14,40 @@
|
|||
"postinstall": "patch-package"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@img/sharp-linux-x64": "^0.33.2",
|
||||
"@img/sharp-linux-x64": "^0.33.3",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/html-minifier": "^4.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"eslint": "^8.57.0",
|
||||
"@types/matter-js": "^0.19.6",
|
||||
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
||||
"@typescript-eslint/parser": "^7.6.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"mdsvex": "^0.11.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-svelte": "^3.2.2",
|
||||
"sharp": "^0.33.2",
|
||||
"sharp": "^0.33.3",
|
||||
"svelte": "4.2.12",
|
||||
"svelte-check": "^3.6.7",
|
||||
"svelte-check": "^3.6.9",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.4.2"
|
||||
"typescript": "^5.4.4"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@lukeed/uuid": "^2.0.1",
|
||||
"@sveltejs/adapter-node": "^5.0.1",
|
||||
"@sveltejs/adapter-static": "^3.0.1",
|
||||
"@sveltejs/kit": "^2.5.4",
|
||||
"@sveltejs/kit": "^2.5.5",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"cbor-x": "^1.5.8",
|
||||
"cbor-x": "^1.5.9",
|
||||
"cookie": "^0.6.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"matter-js": "^0.19.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"svelte-body": "^1.4.0",
|
||||
"vite": "5.1.6"
|
||||
"vite": "5.2.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
--text-font: 'Atkinson Hyperlegible', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#page {
|
||||
|
|
157
src/lib/gravity.ts
Normal file
|
@ -0,0 +1,157 @@
|
|||
import { browser } from '$app/environment'
|
||||
import Matter from 'matter-js'
|
||||
|
||||
const GRAVITY_QUERY_SELECTOR = 'p, h1, h2, .button, .icon'
|
||||
|
||||
export function initGravity(): () => void {
|
||||
const { Engine, Bodies, Composite, Runner, Mouse, MouseConstraint } = Matter
|
||||
|
||||
console.log('gravity enabled')
|
||||
|
||||
// create an engine
|
||||
const engine = Engine.create({ enableSleeping: true })
|
||||
|
||||
const floorBody = Bodies.rectangle(
|
||||
window.innerWidth / 2,
|
||||
window.innerHeight - 5,
|
||||
window.innerWidth,
|
||||
10,
|
||||
{
|
||||
isStatic: true,
|
||||
}
|
||||
)
|
||||
Composite.add(engine.world, [floorBody])
|
||||
|
||||
const trackedElements = new Map<HTMLElement, Matter.Body>()
|
||||
|
||||
async function enableGravityOnElement(el: HTMLElement) {
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve))
|
||||
|
||||
let originalTextContent: string | null = null
|
||||
if (el.classList.contains('copyright') || el.id === 'main-title') {
|
||||
el.style.width = 'fit-content'
|
||||
el.style.margin = '0 auto'
|
||||
if (el.id === 'main-title') {
|
||||
originalTextContent = el.textContent
|
||||
el.textContent = 'matdoesdev'
|
||||
}
|
||||
}
|
||||
const bb = el.getBoundingClientRect()
|
||||
if (originalTextContent) el.textContent = originalTextContent
|
||||
|
||||
const body = Bodies.rectangle(
|
||||
bb.left + bb.width / 2,
|
||||
bb.top + bb.height / 2,
|
||||
bb.width,
|
||||
bb.height,
|
||||
{ restitution: 1 }
|
||||
)
|
||||
el.style.position = 'absolute'
|
||||
el.style.left = bb.left + 'px'
|
||||
el.style.top = bb.top + 'px'
|
||||
el.style.width = bb.width + 'px'
|
||||
el.style.height = bb.height + 'px'
|
||||
el.style.userSelect = 'none'
|
||||
// el.style.outline = '1px solid red'
|
||||
|
||||
Composite.add(engine.world, [body])
|
||||
trackedElements.set(el, body)
|
||||
}
|
||||
|
||||
function makeAnchorUndraggable(el: HTMLAnchorElement) {
|
||||
// el.style.pointerEvents = 'none'
|
||||
// el.style.cursor = 'pointer'
|
||||
el.draggable = false
|
||||
|
||||
// document.addEventListener('click', (e) => {
|
||||
// el.style.pointerEvents = ''
|
||||
// const clickedEl = document.elementFromPoint(e.clientX, e.clientY)
|
||||
// el.style.pointerEvents = 'none'
|
||||
// // if (clickedEl instanceof HTMLAnchorElement) {
|
||||
// // e.preventDefault()
|
||||
// // goto(clickedEl.href)
|
||||
// // }
|
||||
// if (el.contains(clickedEl)) {
|
||||
// e.preventDefault()
|
||||
// const url = new URL(el.href)
|
||||
// if (url.origin === window.location.origin) {
|
||||
// goto(el.href)
|
||||
// } else {
|
||||
// window.location.href = el.href
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
document.querySelectorAll(GRAVITY_QUERY_SELECTOR).forEach((el) => {
|
||||
enableGravityOnElement(el as HTMLElement)
|
||||
})
|
||||
document.querySelectorAll('a').forEach((el) => {
|
||||
makeAnchorUndraggable(el as HTMLAnchorElement)
|
||||
})
|
||||
|
||||
// update positions every frame
|
||||
Matter.Events.on(engine, 'beforeUpdate', function (event) {
|
||||
trackedElements.forEach((body, el) => {
|
||||
if (body.position.y < 0) {
|
||||
// move up
|
||||
body.position.y = window.innerHeight
|
||||
}
|
||||
|
||||
el.style.left = body.position.x - el.offsetWidth / 2 + 'px'
|
||||
el.style.top = body.position.y - el.offsetHeight / 2 + 'px'
|
||||
el.style.transform = `rotate(${body.angle}rad)`
|
||||
|
||||
// rotate
|
||||
// el.style.transform = ''
|
||||
|
||||
// const [originalWidthStyle, originalHeightStyle] = [el.style.width, el.style.height]
|
||||
// el.style.maxWidth = originalWidthStyle
|
||||
// el.style.maxHeight = originalHeightStyle
|
||||
// el.style.width = ''
|
||||
// el.style.height = ''
|
||||
// const bb = el.getBoundingClientRect()
|
||||
// el.style.transform = `rotate(${body.angle}rad)`
|
||||
// el.style.width = originalWidthStyle
|
||||
// el.style.height = originalHeightStyle
|
||||
// el.style.maxWidth = ''
|
||||
// el.style.maxHeight = ''
|
||||
|
||||
// const fakeBody = Bodies.rectangle(
|
||||
// bb.left + bb.width / 2,
|
||||
// bb.top + bb.height / 2,
|
||||
// bb.width,
|
||||
// bb.height,
|
||||
// {
|
||||
// angle: body.angle,
|
||||
// }
|
||||
// )
|
||||
// body.vertices = fakeBody.vertices
|
||||
})
|
||||
})
|
||||
|
||||
// create runner
|
||||
const runner = Runner.create()
|
||||
|
||||
// run the engine
|
||||
Runner.run(runner, engine)
|
||||
|
||||
// add mouse control
|
||||
var mouse = Mouse.create(document.body),
|
||||
mouseConstraint = MouseConstraint.create(engine, {
|
||||
mouse: mouse,
|
||||
constraint: {
|
||||
stiffness: 0.2,
|
||||
render: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
Composite.add(engine.world, mouseConstraint)
|
||||
|
||||
console.log('running engine')
|
||||
|
||||
return () => {
|
||||
Runner.stop(runner)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
import '../../app.css'
|
||||
import { fly } from 'svelte/transition'
|
||||
import type { LayoutData } from '../$types'
|
||||
import { browser } from '$app/environment'
|
||||
|
||||
export let data: LayoutData
|
||||
|
||||
|
@ -11,7 +12,7 @@
|
|||
let previousPathname = data.pathname
|
||||
let currentPathName = data.pathname
|
||||
let flyDirection = 1 // 1 is right, -1 is left
|
||||
$: {
|
||||
$: if (browser) {
|
||||
if (previousPathname !== currentPathName) previousPathname = currentPathName
|
||||
currentPathName = data.pathname
|
||||
|
||||
|
@ -19,6 +20,35 @@
|
|||
if (previousPathname === '/') flyDirection = 1
|
||||
else if (previousPathname === '/blog' && currentPathName !== '/') flyDirection = 1
|
||||
else flyDirection = -1
|
||||
onPathChange()
|
||||
}
|
||||
|
||||
let pathChangeTimestamps: number[] = []
|
||||
|
||||
let stopGravity: (() => void) | null = null
|
||||
|
||||
async function onPathChange() {
|
||||
// if we switched paths more than 10 times in the past 5 seconds, import $lib/gravity.js
|
||||
pathChangeTimestamps.push(Date.now())
|
||||
while (pathChangeTimestamps[0] < Date.now() - 5000) pathChangeTimestamps.shift()
|
||||
console.log(pathChangeTimestamps)
|
||||
if (pathChangeTimestamps.length >= 10) {
|
||||
const { initGravity } = await import('$lib/gravity')
|
||||
// wait 200ms for the animation to finish
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
// and an animation frame
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve))
|
||||
|
||||
const lastPathChangeTimestamp = pathChangeTimestamps[pathChangeTimestamps.length - 1]
|
||||
// only if it was over 200ms ago
|
||||
if (lastPathChangeTimestamp < Date.now() - 200) {
|
||||
if (currentPathName === '/') {
|
||||
stopGravity = initGravity()
|
||||
} else {
|
||||
stopGravity?.()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -33,15 +63,12 @@
|
|||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© {copyrightYear} mat</p>
|
||||
<p class="copyright">© {copyrightYear} mat</p>
|
||||
</footer>
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
/* :global(body) {
|
||||
overflow: hidden;
|
||||
} */
|
||||
#page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1,014 B |
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 261 B |