ブラウザ崩壊のJSスクリプト(meltdown.js)を探したら、消失しかかってた話
昔ブックマークレットを使ってブラウザのHTML要素を崩壊させるmeltdown.jsが流行っていた時期があった。
それを思い出して調べてみると、どこもかしこもリンク切れを起こしており、ソースコードに辿り着くのが至難だった。
幸いにも、以下FMND BLOG様にソースコードが掲載されているサイトへのリンクがあり、
てっく煮ブログ様へたどりつくことができた。
しかし、こちらもFlashを使っているコードであるため、現在のブラウザ環境では残念ながら動作しない。
Claudeくんに聞いてみたところ、Matter.jsという物理演算をしてくれるJSライブラリがあるようなので、それを利用してそのままClaudeくんに実装してもらった。
BookMarklet版を作りました。
以下が元の崩壊用コード。
code:meltdown-html
<script>
let engine = null;
let render = null;
let runner = null;
let originalElements = [];
let bodies = [];
function getElementsToFall() {
const elements = [];
// ここで自分が崩壊させたい要素を指定する
const imgs = document.querySelectorAll('img');
const cards = document.querySelectorAll('.section');
const icons = document.querySelectorAll('i');
const links = document.querySelectorAll('a');
const ths = document.querySelectorAll('th');
const tds = document.querySelectorAll('td');
const lis = document.querySelectorAll('li');
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
const buttons = document.querySelectorAll('button');
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
elements.push({
element: el,
rect: rect,
originalPosition: {
position: window.getComputedStyle(el).position,
left: el.style.left,
top: el.style.top,
transform: el.style.transform
}
});
}
});
return elements;
}
function startMeltdown() {
if (engine) return; // 既に実行中
// Matter.js setup
engine = Matter.Engine.create();
engine.world.gravity.y = 1.5; // 重力を強めに
const elements = getElementsToFall();
originalElements = elements;
// Canvas作成
const canvas = document.createElement('canvas');
canvas.className = 'physics-canvas';
canvas.width = window.innerWidth;
canvas.height = window.innerHeight * 2; // 下まで落ちるように
document.body.appendChild(canvas);
render = Matter.Render.create({
canvas: canvas,
engine: engine,
options: {
width: canvas.width,
height: canvas.height,
wireframes: false,
background: 'transparent',
showVelocity: false
}
});
// 床を作成
const ground = Matter.Bodies.rectangle(
canvas.width / 2,
canvas.height - 10,
canvas.width,
20,
{ isStatic: true, render: { visible: false } }
);
// 壁を作成
const leftWall = Matter.Bodies.rectangle(
-10,
canvas.height / 2,
20,
canvas.height,
{ isStatic: true, render: { visible: false } }
);
const rightWall = Matter.Bodies.rectangle(
canvas.width + 10,
canvas.height / 2,
20,
canvas.height,
{ isStatic: true, render: { visible: false } }
);
// 要素を物理オブジェクトに変換
elements.forEach((item, index) => {
setTimeout(() => {
const rect = item.rect;
const body = Matter.Bodies.rectangle(
rect.left + rect.width / 2,
rect.top + rect.height / 2,
rect.width,
rect.height,
{
angle: (Math.random() - 0.5) * 0.2,
render: { visible: false }
}
);
// ランダムな初速を与える
Matter.Body.setVelocity(body, {
x: (Math.random() - 0.5) * 5,
y: Math.random() * -2
});
Matter.World.add(engine.world, body);
bodies.push({ body: body, element: item.element });
// 要素のスタイルを変更
item.element.style.position = 'fixed';
item.element.style.zIndex = '9998';
item.element.classList.add('falling-element');
}, index * 50); // 順番に落とす
});
// アニメーションループ
runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);
// DOM要素の位置を更新
(function updateLoop() {
bodies.forEach(item => {
const pos = item.body.position;
const angle = item.body.angle;
item.element.style.left = (pos.x - item.body.bounds.max.x + item.body.bounds.min.x) / 2 + 'px';
item.element.style.top = (pos.y - item.body.bounds.max.y + item.body.bounds.min.y) / 2 + 'px';
item.element.style.transform = rotate(${angle}rad);
});
if (engine) {
requestAnimationFrame(updateLoop);
}
})();
}
// ウィンドウリサイズ対応
window.addEventListener('resize', () => {
if (render && render.canvas) {
render.canvas.width = window.innerWidth;
render.canvas.height = window.innerHeight * 2;
}
});
</script>