自爆ボタン

delaunay-triangulation

view source

JavaScript

document.title = '自爆ボタン';
//delaunay-triangulation


let { canvas, ctx, cx, cy, w, h, min, demo } = readyCanvas();

document.body.style.overflow = 'hidden';

let isPressed = false;

function drawStripe(){
  var diag = Math.sqrt(w * w + h * h); // 対角線の長さ(キャンバスを斜めにカバー)

    const stripeWidth = min/12; // 縞の幅
    const stripeCount = Math.ceil(diag / stripeWidth) * 2;

    ctx.save();

    // 原点をキャンバスの中央に移動し、45度回転
    ctx.translate(w / 2, h / 2);
    ctx.rotate(Math.PI / 4); // 45度 = π/4 ラジアン

    // 左上隅に戻ってから塗り始める
    ctx.translate(-diag / 2, -diag / 2);

    for (let i = 0; i < stripeCount; i++) {
        ctx.fillStyle = (i % 2 === 0) ? 'black' : 'yellow';
        ctx.fillRect(i * stripeWidth, 0, stripeWidth, diag);
    }

    ctx.restore();
}

function drawBlackSquare() {
    const size = min * 5/6;
    const x = (w - size) / 2;
    const y = (h - size) / 2;

    ctx.fillStyle = 'black';
    ctx.fillRect(x, y, size, size);
}

function drawCircle() {
  const outerRadius = Math.min(w, h) / 4;
  const middleRadius = outerRadius * 0.9;

  // 外円:左上から右下への線形グラデーション(光が左上から)
  let gradOuter = ctx.createLinearGradient(cx - outerRadius, cy - outerRadius, cx + outerRadius, cy + outerRadius);
  gradOuter.addColorStop(0, '#ffffff');   // 光が当たる部分(左上)
  gradOuter.addColorStop(1, '#aaaaaa');   // 影になる部分(右下)

  ctx.beginPath();
  ctx.arc(cx, cy, outerRadius, 0, Math.PI * 2);
  ctx.fillStyle = gradOuter;
  ctx.fill();

  // 中円:逆方向のグラデーション(右下が明るい)
  let gradInner = ctx.createLinearGradient(cx + middleRadius, cy + middleRadius, cx - middleRadius, cy - middleRadius);
  gradInner.addColorStop(0,   '#eeeeee');   // 右下が明るく
  gradInner.addColorStop(0.8, '#aaaaaa');   // 左上が暗く
  gradInner.addColorStop(1,   '#888888');   // 左上が暗く

  ctx.beginPath();
  ctx.arc(cx, cy, middleRadius, 0, Math.PI * 2);
  ctx.fillStyle = gradInner;
  ctx.fill();
}


function drawButton() {
    const outerRadius = Math.min(w, h) / 4;
    const innerRadius = isPressed ? outerRadius * 0.65 : outerRadius * 0.7;
    const offset = isPressed ? 4 : 0;

    // 内円(赤いボタン)
    ctx.beginPath();
    ctx.arc(cx, cy + offset, innerRadius, 0, Math.PI * 2);
    const gradient = ctx.createRadialGradient(cx, cy + offset, innerRadius * 0.2, cx, cy + offset, innerRadius);
    gradient.addColorStop(0, '#ff6666');
    gradient.addColorStop(1, '#990000');
    ctx.fillStyle = gradient;
    ctx.fill();
}


let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let audioBuffer = null;
let sourceNode = null;
let isPlaying = false;
let playInterval = null;

fetch('buzzer.mp3?t=1')
  .then(res => res.arrayBuffer())
  .then(data => audioCtx.decodeAudioData(data))
  .then(buffer => {
    audioBuffer = buffer;
  });

function startBuzz() {
  if (!audioBuffer || isPlaying) return;

  isPlaying = true;
  const duration = audioBuffer.duration;
  const half = duration / 2;
  const fadeTime = 0.3;

  function playInstance(startTime) {
      let source = audioCtx.createBufferSource();
      let gain = audioCtx.createGain();

      source.buffer = audioBuffer;
      source.connect(gain).connect(audioCtx.destination);

      // フェードイン
      gain.gain.setValueAtTime(0, startTime);
      gain.gain.linearRampToValueAtTime(1, startTime + fadeTime);

      // フェードアウト
      gain.gain.setValueAtTime(1, startTime + duration - fadeTime);
      gain.gain.linearRampToValueAtTime(0, startTime + duration);

      source.start(startTime);

      // 最初の音源だけ記録しておく(即停止用)
      if (!sourceNode) {
          sourceNode = source;
      }
  }

  const now = audioCtx.currentTime;
  playInstance(now);             // 最初の再生
  playInstance(now + half);      // 半分経過で次の再生

  // 以降も定期的に繰り返す
  playInterval = setInterval(() => {
    if (sourceNode) {
      const now = audioCtx.currentTime;
      playInstance(now);
    }
  }, half * 1000);
}

function stopBuzz() {
  if (playInterval) {
      clearInterval(playInterval);
      playInterval = null;
  }
  if (sourceNode) {
      try {
          sourceNode.stop();
      } catch (e) {
          // 既に停止している場合の安全処理
      }
      sourceNode.disconnect();
      sourceNode = null;
  }
  isPlaying = false;
}

function redraw() {
  ctx.clearRect(0, 0, w, h);
  drawStripe();
  drawBlackSquare();
  drawCircle();
  drawButton();
}
redraw();



canvas.addEventListener('mousedown', function(e) {
  isPressed = true;
  startBuzz();
  redraw();
});

canvas.addEventListener('mouseup', function(e) {
  isPressed = false;
  stopBuzz();
  redraw();
});



window.addEventListener('resize', function(e) {
  redraw();
});

CSS

HTML

ページのソースを表示 : Ctrl+U , DevTools : F12

view-source:https://hi0a.com/game/btn-danger/

ABOUT

hi0a.com 「ひまあそび」は無料で遊べるミニゲームや便利ツールを公開しています。

プログラミング言語の動作デモやWEBデザイン、ソースコード、フロントエンド等の開発者のための技術を公開しています。

必要な機能の関数をコピペ利用したり勉強に活用できます。

プログラムの動作サンプル結果は画面上部に出力表示されています。

環境:最新のブラウザ GoogleChrome / Windows / Android / iPhone 等の端末で動作確認しています。

画像素材や音素材は半分自作でフリー素材配布サイトも利用しています。LINK参照。

仕様変更、API廃止等、実験途中で動かないページもあります。