M-028 Animation medium

Radar Signal Scanner レーダー信号スキャナー

Circular sweeping radar scope simulating target detection blips with pause/resume controls and telemetry list. レーダースイープと静的架空信号データの走査・一時停止UI。

ライブデモ Live Demo

SECTOR SCAN
  • SIG-ALPHA-01Range: 42 km • Brg: 045°
  • SIG-BRAVO-02Range: 88 km • Brg: 160°
  • SIG-GAMMA-03Range: 115 km • Brg: 290°

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

Circular sweeping radar scope simulating target detection blips with pause/resume controls and telemetry list.

レーダースイープと静的架空信号データの走査・一時停止UI。

どこで使うかWhere to use

Portfolio, product showcase, landing page.

ポートフォリオ、製品紹介、ランディングページ。

特徴Key features

Circular sweeping radar scope simulating target detection blips with pause/resume controls and telemetry list.

レーダースイープと静的架空信号データの走査・一時停止UI。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--demo-accent#4ade80Accent color for the panel border and keyboard focus ring.

実装コード Implementation Code

<div class="demo-stage rr-root" role="region" aria-label="Radar Signal Scanner"><div class="rr-console"><div class="rr-scope-wrap"><div class="rr-scope"><div class="rr-grid-ring rr-ring-1"></div><div class="rr-grid-ring rr-ring-2"></div><div class="rr-grid-crosshair rr-axis-x"></div><div class="rr-grid-crosshair rr-axis-y"></div><div class="rr-sweep-beam" aria-hidden="true"></div><div class="rr-blip rr-blip-1" aria-hidden="true"></div><div class="rr-blip rr-blip-2" aria-hidden="true"></div><div class="rr-blip rr-blip-3" aria-hidden="true"></div></div></div><div class="rr-sidebar"><div class="rr-header"><span class="rr-title">SECTOR SCAN</span><button type="button" class="rr-pause-btn" aria-pressed="false" aria-label="Pause radar sweep animation">Pause</button></div><ul aria-label="Fictional example signals" class="rr-signal-list" role="list"><li class="rr-signal-item"><span class="rr-dot rr-dot-1"></span><div class="rr-info"><span class="rr-id">SIG-ALPHA-01</span><span class="rr-dist">Range: 42 km &bull; Brg: 045&deg;</span></div></li><li class="rr-signal-item"><span class="rr-dot rr-dot-2"></span><div class="rr-info"><span class="rr-id">SIG-BRAVO-02</span><span class="rr-dist">Range: 88 km &bull; Brg: 160&deg;</span></div></li><li class="rr-signal-item"><span class="rr-dot rr-dot-3"></span><div class="rr-info"><span class="rr-id">SIG-GAMMA-03</span><span class="rr-dist">Range: 115 km &bull; Brg: 290&deg;</span></div></li></ul></div></div></div>
<script>
document.querySelectorAll('.rr-root').forEach(root => {
  const pauseBtn = root.querySelector('.rr-pause-btn');
  if (!pauseBtn) return;
  const motionOk = window.matchMedia('(prefers-reduced-motion: no-preference)').matches;
  if (!motionOk) {
    pauseBtn.disabled = true;
    pauseBtn.setAttribute('aria-label', 'Animation disabled due to reduced motion preference');
    pauseBtn.textContent = 'Reduced Motion';
    return;
  }
  pauseBtn.addEventListener('click', () => {
    const isPaused = root.classList.toggle('is-paused');
    pauseBtn.setAttribute('aria-pressed', isPaused ? 'true' : 'false');
    pauseBtn.textContent = isPaused ? 'Resume' : 'Pause'; pauseBtn.setAttribute('aria-label', isPaused ? 'Resume radar sweep animation' : 'Pause radar sweep animation');
  });
});
</script>
.rr-root {
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: 420px;
  min-width: 0;
  margin: 0 auto;
  padding: 16px;
  background: radial-gradient(circle at 40% 40%, #061f14, #020b07);
  border-radius: 16px;
  border: 1px solid #144930;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.75);
  font-family: system-ui, -apple-system, sans-serif;
  color: #86efac;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.rr-root *, .rr-root *::before, .rr-root *::after {
  box-sizing: border-box;
  min-width: 0;
}
.rr-root .rr-console {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}
.rr-root .rr-scope-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rr-root .rr-scope {
  position: relative;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: radial-gradient(circle, #0a2f1c, #03140b);
  border: 2px solid #10b981;
  box-shadow: inset 0 0 16px rgba(0, 0, 0, 0.8), 0 0 12px rgba(16, 185, 129, 0.3);
  overflow: hidden;
}
.rr-root .rr-grid-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px dashed rgba(16, 185, 129, 0.35);
}
.rr-root .rr-ring-1 {
  width: 90px;
  height: 90px;
}
.rr-root .rr-ring-2 {
  width: 45px;
  height: 45px;
}
.rr-root .rr-grid-crosshair {
  position: absolute;
  background: rgba(16, 185, 129, 0.3);
}
.rr-root .rr-axis-x {
  top: 50%;
  left: 0;
  width: 100%;
  height: 1px;
}
.rr-root .rr-axis-y {
  left: 50%;
  top: 0;
  width: 1px;
  height: 100%;
}
.rr-root .rr-sweep-beam {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg, transparent 310deg, rgba(16, 185, 129, 0.5) 360deg);
}
.rr-root.is-paused .rr-sweep-beam {
  animation-play-state: paused;
}
.rr-root .rr-blip {
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #4ade80;
  box-shadow: 0 0 8px #4ade80;
}
.rr-root .rr-blip-1 {
  top: 36px;
  right: 38px;
}
.rr-root .rr-blip-2 {
  bottom: 30px;
  right: 50px;
}
.rr-root .rr-blip-3 {
  top: 60px;
  left: 28px;
}
.rr-root .rr-sidebar {
  flex: 1;
  min-width: 160px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.rr-root .rr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.rr-root .rr-title {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  color: #4ade80;
}
.rr-root .rr-pause-btn {
  appearance: none;
  border: 1px solid #10b981;
  background: #064e3b;
  color: #86efac;
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  cursor: pointer;
}
.rr-root .rr-pause-btn:hover:not(:disabled) {
  background: #047857;
  color: #ffffff;
}
.rr-root .rr-pause-btn:focus-visible {
  outline: 2px solid #4ade80;
  outline-offset: 2px;
}
.rr-root .rr-pause-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.rr-root .rr-signal-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.rr-root .rr-signal-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: #042416;
  border-radius: 6px;
  border: 1px solid #0f5132;
}
.rr-root .rr-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #22c55e;
  flex-shrink: 0;
}
.rr-root .rr-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rr-root .rr-id {
  font-size: 10px;
  font-weight: 700;
  color: #f0fdf4;
}
.rr-root .rr-dist {
  font-size: 8px;
  color: #6ee7b7;
}
@media (prefers-reduced-motion: no-preference) {
  .rr-root .rr-sweep-beam {
    animation: rr-sweep 3s linear infinite;
  }
  .rr-root .rr-pause-btn {
    transition: background 0.2s ease, transform 0.1s ease;
  }
  .rr-root .rr-pause-btn:active:not(:disabled) {
    transform: scale(0.95);
  }
}
@keyframes rr-sweep {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.rr-root{max-height:none;--demo-accent:#4ade80;border-color:var(--demo-accent)}.rr-root button:focus-visible{outline-color:var(--demo-accent)}
.rr-root .rr-header{gap:8px;flex-wrap:wrap}.rr-root .rr-sidebar{min-width:0;flex-basis:140px}.rr-root .rr-scope{width:130px;height:130px}.rr-root .rr-console{gap:10px}
import { useState, useEffect } from "react";
import "./M-028.css";
function useReducedMotion() {
  const [reduced, setReduced] = useState(true);
  useEffect(() => {
    const media = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduced(media.matches);
    sync();
    media.addEventListener("change", sync);
    return () => media.removeEventListener("change", sync);
  }, []);
  return reduced;
}
function RadarSignalScanner() {
  const [paused, setPaused] = useState(false);
  const reduced = useReducedMotion();
  return <div role="region" aria-label="Radar Signal Scanner" className={"demo-stage rr-root" + (paused || reduced ? " is-paused" : "")}><div className="rr-console"><div className="rr-scope-wrap"><div className="rr-scope"><div className="rr-grid-ring rr-ring-1" /><div className="rr-grid-ring rr-ring-2" /><div className="rr-grid-crosshair rr-axis-x" /><div className="rr-grid-crosshair rr-axis-y" /><div className="rr-sweep-beam" aria-hidden="true" /><div className="rr-blip rr-blip-1" aria-hidden="true" /><div className="rr-blip rr-blip-2" aria-hidden="true" /><div className="rr-blip rr-blip-3" aria-hidden="true" /></div></div><div className="rr-sidebar"><div className="rr-header"><span className="rr-title">{"SECTOR SCAN"}</span><button type="button" className="rr-pause-btn" disabled={reduced} aria-pressed={paused || reduced} onClick={() => setPaused((value) => !value)} aria-label={reduced ? "Animation disabled by reduced motion preference" : "Toggle animation playback"}>{reduced ? "REDUCED MOTION" : paused ? "Resume" : "Pause"}</button></div><ul aria-label="Fictional example signals" className="rr-signal-list" role="list"><li className="rr-signal-item"><span className="rr-dot rr-dot-1" /><div className="rr-info"><span className="rr-id">{"SIG-ALPHA-01"}</span><span className="rr-dist">{"Range: 42 km \u2022 Brg: 045\xB0"}</span></div></li><li className="rr-signal-item"><span className="rr-dot rr-dot-2" /><div className="rr-info"><span className="rr-id">{"SIG-BRAVO-02"}</span><span className="rr-dist">{"Range: 88 km \u2022 Brg: 160\xB0"}</span></div></li><li className="rr-signal-item"><span className="rr-dot rr-dot-3" /><div className="rr-info"><span className="rr-id">{"SIG-GAMMA-03"}</span><span className="rr-dist">{"Range: 115 km \u2022 Brg: 290\xB0"}</span></div></li></ul></div></div></div>;
}
export {
  RadarSignalScanner as default
};
.rr-root {
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: 420px;
  min-width: 0;
  margin: 0 auto;
  padding: 16px;
  background: radial-gradient(circle at 40% 40%, #061f14, #020b07);
  border-radius: 16px;
  border: 1px solid #144930;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.75);
  font-family: system-ui, -apple-system, sans-serif;
  color: #86efac;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.rr-root *, .rr-root *::before, .rr-root *::after {
  box-sizing: border-box;
  min-width: 0;
}
.rr-root .rr-console {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}
.rr-root .rr-scope-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rr-root .rr-scope {
  position: relative;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: radial-gradient(circle, #0a2f1c, #03140b);
  border: 2px solid #10b981;
  box-shadow: inset 0 0 16px rgba(0, 0, 0, 0.8), 0 0 12px rgba(16, 185, 129, 0.3);
  overflow: hidden;
}
.rr-root .rr-grid-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px dashed rgba(16, 185, 129, 0.35);
}
.rr-root .rr-ring-1 {
  width: 90px;
  height: 90px;
}
.rr-root .rr-ring-2 {
  width: 45px;
  height: 45px;
}
.rr-root .rr-grid-crosshair {
  position: absolute;
  background: rgba(16, 185, 129, 0.3);
}
.rr-root .rr-axis-x {
  top: 50%;
  left: 0;
  width: 100%;
  height: 1px;
}
.rr-root .rr-axis-y {
  left: 50%;
  top: 0;
  width: 1px;
  height: 100%;
}
.rr-root .rr-sweep-beam {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg, transparent 310deg, rgba(16, 185, 129, 0.5) 360deg);
}
.rr-root.is-paused .rr-sweep-beam {
  animation-play-state: paused;
}
.rr-root .rr-blip {
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #4ade80;
  box-shadow: 0 0 8px #4ade80;
}
.rr-root .rr-blip-1 {
  top: 36px;
  right: 38px;
}
.rr-root .rr-blip-2 {
  bottom: 30px;
  right: 50px;
}
.rr-root .rr-blip-3 {
  top: 60px;
  left: 28px;
}
.rr-root .rr-sidebar {
  flex: 1;
  min-width: 160px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.rr-root .rr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.rr-root .rr-title {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  color: #4ade80;
}
.rr-root .rr-pause-btn {
  appearance: none;
  border: 1px solid #10b981;
  background: #064e3b;
  color: #86efac;
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  cursor: pointer;
}
.rr-root .rr-pause-btn:hover:not(:disabled) {
  background: #047857;
  color: #ffffff;
}
.rr-root .rr-pause-btn:focus-visible {
  outline: 2px solid #4ade80;
  outline-offset: 2px;
}
.rr-root .rr-pause-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.rr-root .rr-signal-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.rr-root .rr-signal-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: #042416;
  border-radius: 6px;
  border: 1px solid #0f5132;
}
.rr-root .rr-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #22c55e;
  flex-shrink: 0;
}
.rr-root .rr-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rr-root .rr-id {
  font-size: 10px;
  font-weight: 700;
  color: #f0fdf4;
}
.rr-root .rr-dist {
  font-size: 8px;
  color: #6ee7b7;
}
@media (prefers-reduced-motion: no-preference) {
  .rr-root .rr-sweep-beam {
    animation: rr-sweep 3s linear infinite;
  }
  .rr-root .rr-pause-btn {
    transition: background 0.2s ease, transform 0.1s ease;
  }
  .rr-root .rr-pause-btn:active:not(:disabled) {
    transform: scale(0.95);
  }
}
@keyframes rr-sweep {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.rr-root{max-height:none;--demo-accent:#4ade80;border-color:var(--demo-accent)}.rr-root button:focus-visible{outline-color:var(--demo-accent)}
.rr-root .rr-header{gap:8px;flex-wrap:wrap}.rr-root .rr-sidebar{min-width:0;flex-basis:140px}.rr-root .rr-scope{width:130px;height:130px}.rr-root .rr-console{gap:10px}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

Scoped CSS creates the visual design; small vanilla JavaScript handlers update the local demo state.

スコープ化したCSSで描画し、Vanilla JavaScriptでデモ内の状態だけを更新します。

カスタマイズ方法Customization

Accent color for the panel border and keyboard focus ring.

パネルの枠線とキーボードフォーカスの色を変更します。

注意点Caveats

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

よくある質問 Frequently Asked Questions

Does this demo need external libraries? 外部ライブラリは必要ですか?

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.