meow meow meow
This commit is contained in:
Liu Xiaoyi 2023-03-31 23:46:12 +08:00
parent 1ed3fc2de4
commit 6602b8d249
5 changed files with 287 additions and 0 deletions

View File

@ -61,6 +61,9 @@ permalink: /404.html
mrLink.href = mrURL.href;
})();
</script>
<script src="/static/js/matter.min.js?{{ site.data['hash'] }}"></script>
<script src="/static/js/meow.js?{{ site.data['hash'] }}"></script>
<link rel="stylesheet" href="/static/css/meow.css?{{ site.data['hash'] }}">
</body>
</html>
<!--

View File

@ -216,6 +216,9 @@
{% endraw %}
{% unless page.legacy or site.issue %}
<script src="/static/js/index.js?{{ site.data['hash'] }}"></script>
<script src="/static/js/matter.min.js?{{ site.data['hash'] }}"></script>
<script src="/static/js/meow.js?{{ site.data['hash'] }}"></script>
<link rel="stylesheet" href="/static/css/meow.css?{{ site.data['hash'] }}">
{% endunless %}
</html>
<!--

49
static/css/meow.css Normal file
View File

@ -0,0 +1,49 @@
* {
overflow: visible;
}
#mirror-list {
overflow-x: visible !important;
overflow-y: visible !important;
}
.meow-floating {
z-index: 1000;
}
.meow-floating > * {
z-index: 1000;
position: relative;
}
.meow-floating::before {
content: '';
display: block;
position: absolute;
left: 0;
top: 0;
width: var(--real-width);
height: var(--real-height);
box-shadow: rgba(0,0,0,.3) 0 4px 12px;
}
.meow-floating::before {
background: #F9F9F9;
z-index: -1;
}
/*
.meow-floating::after {
transform: var(--inverse);
box-shadow: rgba(0,0,0,.3) 0 2px 6px inset;
background: rgba(0,0,0,.5);
z-index: -10 !important;
}
*/
@media (prefers-color-scheme: dark) {
.meow-floating::before {
background: #333;
}
}

6
static/js/matter.min.js vendored Normal file

File diff suppressed because one or more lines are too long

226
static/js/meow.js Normal file
View File

@ -0,0 +1,226 @@
const Matter = window.Matter;
function findNearestBlock(elem) {
if(elem === null || elem === document.body) return null;
const styleSet = getComputedStyle(elem);
if(elem.tagName !== 'svg' && styleSet['display'] !== 'inline') return elem;
return findNearestBlock(elem.parentNode);
}
function bootstrap() {
const engine = Matter.Engine.create({
gravity: {
x: 0,
y: -0.1,
}
});
const floatings = new Map();
// Create walls
let walls = null;
function buildWalls() {
const WALL_DEPTH = 10000;
const topWall = Matter.Bodies.rectangle(
window.innerWidth / 2 + window.scrollX,
-WALL_DEPTH / 2 + window.scrollY,
window.innerWidth + 2 * WALL_DEPTH,
WALL_DEPTH,
{
isStatic: true,
restitution: 0.8,
}
);
const bottomWall = Matter.Bodies.rectangle(
window.innerWidth / 2 + window.scrollX,
window.innerHeight + WALL_DEPTH / 2 + window.scrollY,
window.innerWidth + 2 * WALL_DEPTH,
WALL_DEPTH,
{
isStatic: true,
restitution: 0.8,
}
);
const leftWall = Matter.Bodies.rectangle(
-WALL_DEPTH / 2 + window.scrollX,
window.innerHeight / 2 + window.scrollY,
WALL_DEPTH,
window.innerHeight + 2 * WALL_DEPTH,
{
isStatic: true,
restitution: 0.8,
}
);
const rightWall = Matter.Bodies.rectangle(
window.innerWidth + WALL_DEPTH / 2 + window.scrollX,
window.innerHeight / 2 + window.scrollY,
WALL_DEPTH,
window.innerHeight + 2 * WALL_DEPTH,
{
isStatic: true,
restitution: 0.8,
}
);
if(walls) Matter.Composite.remove(engine.world, walls);
walls = Matter.Composite.create();
Matter.Composite.add(walls, [topWall, bottomWall, leftWall, rightWall]);
Matter.Composite.add(engine.world, walls);
}
buildWalls();
window.addEventListener('resize', buildWalls);
// Create overlay
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.left = 0;
overlay.style.top = 0;
overlay.style.width = '100vw';
overlay.style.height = '100vh';
overlay.style['pointer-events'] = 'none';
document.body.appendChild(overlay);
// const bounds = Matter.Bounds.create([{ x: 0, y: 0 }, { x: window.innerWidth, y: window.innerHeight }]);
/*
const debugRender = Matter.Render.create({
element: overlay,
engine,
bounds,
options: {
background: 'transparent',
wireframeBackground: 'transparent',
width: window.innerWidth,
height: window.innerHeight,
hasBounds: true,
},
});
Matter.Render.run(debugRender);
*/
let lastRender = performance.now();
let lastScrollX = window.scrollX;
let lastScrollY = window.scrollY;
let lastScreenX = window.screenX;
let lastScreenY = window.screenY;
function render(ts) {
const diff = ts - lastRender;
lastRender = ts;
const curScrollX = window.scrollX;
const curScrollY = window.scrollY;
if(walls)
Matter.Composite.translate(walls, {
x: curScrollX - lastScrollX,
y: curScrollY - lastScrollY,
});
const diffScreenX = window.screenX - lastScreenX;
const diffScreenY = window.screenY - lastScreenY;
for(const body of engine.world.bodies) {
if(body === walls) continue;
Matter.Body.translate(body, {
x: -diffScreenX,
y: -diffScreenY,
});
}
lastScreenX = window.screenX;
lastScreenY = window.screenY;
/*
Matter.Bounds.translate(bounds, {
x: curScrollX - lastScrollX,
y: curScrollY - lastScrollY,
});
*/
lastScrollX = curScrollX;
lastScrollY = curScrollY;
Matter.Engine.update(engine, diff);
const bodies = Matter.Composite.allBodies(engine.world);
for(const body of bodies) {
const id = body.id;
const query = floatings.get(id);
if(!query) continue;
const [floating, width, height] = query;
if(!floating || !floating.offsetParent) {
// DOM is removed
floatings.delete(id);
Matter.Composite.remove(engine.world, body);
continue;
}
const { top, left } = floating.offsetParent.getBoundingClientRect();
const x = left + floating.offsetLeft + width / 2;
const y = top + floating.offsetTop + height / 2;
const toX = body.position.x - curScrollX;
const toY = body.position.y - curScrollY;
const rotation = body.angle;
floating.style.transform = `translate(${toX - x}px, ${toY - y}px) rotate(${rotation}rad)`;
// floating.style.setProperty('--inverse', `rotate(${-rotation}rad) translate(${- toX + x}px, ${- toY + y}px)`);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
document.body.addEventListener('click', e => {
const floating = findNearestBlock(e.target);
if(floating.classList.contains('meow-floating')) return true;
// filter if has floating childern
if(floating.querySelector('.meow-floating')) return true;
e.preventDefault();
e.stopImmediatePropagation();
// Flush
const computed = getComputedStyle(floating);
// const width = computed.width;
// const height = computed.height;
const { top, left, width, height } = floating.getBoundingClientRect();
if(height > 600) return true;
const scrollX = window.scrollX;
const scrollY = window.scrollY;
const worldX = scrollX + left;
const worldY = scrollY + top;
floating.style.setProperty('--real-width', `${width}px`);
floating.style.setProperty('--real-height', `${height}px`);
const box = Matter.Bodies.rectangle(worldX + width / 2, worldY + height / 2, width, height, { restitution: 0.8 });
const id = box.id;
// Apply random velocity at start
const initVelX = Math.random() * 0.5 - 0.25;
const initVelY = Math.random() * 0.5 - 0.25;
const initVelAng = Math.random() * 0.01 - 0.005;
Matter.Body.setVelocity(box, { x: initVelX, y: initVelY });
Matter.Body.setAngularVelocity(box, initVelAng);
floating.classList.add('meow-floating');
// Hacks for popovers
const triggers = floating.querySelectorAll('a[data-toggle=popover]');
for(const a of triggers) {
a.addEventListener('mouseover', e => {
e.stopImmediatePropagation();
}, true);
}
const popovers = floating.querySelectorAll('.popover');
for(const popover of popovers) popover.remove();
floatings.set(id, [floating, width, height]);
Matter.Composite.add(engine.world, box);
return false;
}, true);
}
document.body.onload = bootstrap;