Skip to content

Instantly share code, notes, and snippets.

@shimarin
Created June 6, 2025 23:48
Show Gist options
  • Save shimarin/6a17578ae950698f7f13f347b8f6dbd1 to your computer and use it in GitHub Desktop.
Save shimarin/6a17578ae950698f7f13f347b8f6dbd1 to your computer and use it in GitHub Desktop.
ティウンティウン
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tiuntiun</title>
</head>
<body>
<rockman-explosion style="--particle-color: blue;" count="30">
<img src="rockman.png" width="300" height="300" alt="Rockman">
</rockman-explosion>
<script type="module" src="tiuntiun.js"></script>
</body>
</html>
import { LitElement, html, css } from 'https://unpkg.com/lit?module';
export class RockmanExplosion extends LitElement {
static properties = {
count: { type: Number },
};
static styles = css`
:host {
position: relative;
cursor: pointer;
/* スロット内の要素を中央に収める */
display: inline-flex;
align-items: center;
justify-content: center;
}
:host(.exploding) ::slotted(*) {
visibility: hidden;
}
.particle {
position: absolute;
top: 50%;
left: 50%;
/* 粒子サイズは --particle-size で制御 */
width: var(--particle-size, 4px);
height: var(--particle-size, 4px);
background: var(--particle-color, #48c);
border-radius: 50%;
animation: explode 600ms ease-out forwards;
}
@keyframes explode {
to {
transform: translate(var(--dx), var(--dy)) scale(0);
opacity: 0;
}
}
`;
constructor() {
super();
this.count = 20;
}
render() {
return html`<slot></slot>`;
}
firstUpdated() {
this.addEventListener('click', () => this._explode());
}
_explode() {
this.classList.add('exploding');
this._playSound();
// 要素サイズ取得・最小辺を基準に
const { width, height } = this.getBoundingClientRect();
const minSide = Math.min(width, height);
// 半径は最小辺の 0.4~0.8 倍の範囲でランダム
const baseRadius = minSide * 0.6;
// 粒子サイズは最小辺の 5% ~ 10%
const particleSize = Math.max(2, minSide * 0.06);
// CSS 変数として粒子サイズをセット
this.style.setProperty('--particle-size', `${particleSize}px`);
for (let i = 0; i < this.count; i++) {
const p = document.createElement('div');
p.classList.add('particle');
// ランダム方向と距離
const angle = Math.random() * Math.PI * 2;
const distance = baseRadius * (0.6 + Math.random() * 0.4);
p.style.setProperty('--dx', `${Math.cos(angle) * distance}px`);
p.style.setProperty('--dy', `${Math.sin(angle) * distance}px`);
this.shadowRoot.appendChild(p);
p.addEventListener('animationend', () => p.remove());
}
}
_playSound() {
const ctx = new AudioContext();
const osc = ctx.createOscillator();
osc.type = 'square';
osc.frequency.setValueAtTime(880, ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(440, ctx.currentTime + 0.15);
osc.connect(ctx.destination);
osc.start();
osc.stop(ctx.currentTime + 0.2);
}
}
customElements.define('rockman-explosion', RockmanExplosion);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment