## ロック画面またはホーム画面に「指定時刻までのカウントダウン」を出す方法 * App Storeで「Scriptable」を入れる(無料) * Scriptableで新規スクリプト → 下記コードを貼って保存。たとえばcountdownと名前をつける * ロック画面編集 → 円形ウィジェット(Scriptable)を追加 → このスクリプトを選択(countdownを選択) * またはホーム画面編集 → 横長ウィジェット(Scriptable)を追加 → このスクリプトを選択(countdownを選択) * 任意の時刻を指定する場合は、ウィジェットの「パラメータ」に終了時刻(例: `2025-07-29T11:00:00+09:00`)を入力 > 更新はOS仕様で数分おき。 > [!info] 最新のコードは[Gistに置きました](https://gist.github.com/hnsol/277a702a47d29ab32872211d58180198) > **調整:** カウントダウンをより自然に見せるため、単位切り替えの境界を変更しました(12日 / 120時間 / 24時間 / 120分)。 ```javascript // Lock Screen Countdown (Circular & Rectangular w/ smart unit switch) // ウィジェット「パラメータ」に ISO日時を入れる(例:2025-07-29T11:00:00+09:00) // 未設定 or 不正値は DEFAULT_TARGET を使用 const DEFAULT_TARGET = "2025-08-14T16:00:00+09:00"; // 切替境界 const DAY_HOUR_BORDER = 72 * 3600 * 1000; // 72h以上→day const HOUR_MIN_BORDER = 3 * 3600 * 1000; // 3h以上→hour、未満→h m // ---- 入力処理 ---- const rawParam = (args.widgetParameter || DEFAULT_TARGET).trim(); const parsed = new Date(rawParam); const TARGET = isNaN(parsed.getTime()) ? new Date(DEFAULT_TARGET) : parsed; const now = new Date(); let ms = TARGET - now; // ---- モード判定 ---- function pickMode(ms) { if (ms <= 0) return "done"; if (ms >= DAY_HOUR_BORDER) return "day"; // 72h+ if (ms >= HOUR_MIN_BORDER) return "hour"; // 3h+ ~ 72h- return "hm"; // 0+ ~ 3h- } // ---- フォーマット ---- function fmtDay(ms) { const d = ms / 86400000; // 24*60*60*1000 return d.toFixed(1); } function fmtHour(ms) { const h = ms / 3600000; return h.toFixed(1); } function splitHM(ms) { if (ms <= 0) return { h: 0, m: 0, done: true }; const totalH = Math.floor(ms / 3600000); const remM = Math.floor((ms % 3600000) / 60000); return { h: totalH, m: remM, done: false }; } // ---- ウィジェット作成 ---- const family = config.runsInAccessoryWidget ? config.widgetFamily : "accessoryRectangular"; const mode = pickMode(ms); const widget = new ListWidget(); // 共通:完了表示 function drawDone() { const t = widget.addText("終了"); t.centerAlignText(); t.font = Font.boldSystemFont(family === "accessoryCircular" ? 16 : 22); } // 円形(accessoryCircular)を確実にセンタリングする版 function drawCircular() { widget.setPadding(0, 0, 0, 0); if (mode === "done") { const tstack = widget.addStack(); tstack.addSpacer(); const t = tstack.addText("済"); t.font = Font.boldSystemFont(16); tstack.addSpacer(); return; } const v = widget.addStack(); v.layoutVertically(); v.centerAlignContent(); // 念のため // 共通: 中央寄せ1行を作るヘルパ function addCenteredLine(text, font, opacity = 1.0) { const h = v.addStack(); h.addSpacer(); const t = h.addText(text); t.font = font; t.textOpacity = opacity; h.addSpacer(); } if (mode === "day") { addCenteredLine(fmtDay(ms), Font.boldSystemFont(15)); addCenteredLine("d", Font.mediumSystemFont(12), 0.9); } else if (mode === "hour") { addCenteredLine(fmtHour(ms), Font.boldSystemFont(15)); addCenteredLine("h", Font.mediumSystemFont(12), 0.9); } else { const { h, m } = splitHM(ms); addCenteredLine(`${h}h`, Font.boldSystemFont(15)); addCenteredLine(`${m}m`, Font.mediumSystemFont(12), 0.9); } } // 横長(accessoryRectangular) function drawRect() { // タイトル const title = widget.addText(mode === "done" ? "終了" : "⏳ あと"); title.centerAlignText(); title.font = Font.mediumSystemFont(20); title.textOpacity = 0.9; // 本文 let body = ""; if (mode === "done") { body = "00:00"; } else if (mode === "day") { body = `${fmtDay(ms)} day`; } else if (mode === "hour") { body = `${fmtHour(ms)} h`; } else { const { h, m } = splitHM(ms); body = `${h}h ${m}m`; } const timeText = widget.addText(body); timeText.centerAlignText(); // 横長は少し大きめに timeText.font = Font.mediumSystemFont( mode === "hm" ? 42 : 48 // h m は若干小さめで収まり優先 ); } // ---- 描画 ---- if (family === "accessoryCircular") { drawCircular(); } else { // デフォルトは横長(accessoryRectangular)想定 drawRect(); } Script.setWidget(widget); Script.complete(); ``` ### メモ * 表示更新は数分〜十数分おき(iOS仕様) * 文字が小さい/切れる場合は `Font.boldSystemFont(15)` と `12` を1刻みで調整すればOK ### スクショ #### ロックスクリーン ![IMG_2430_opt.jpg|300](https://cdn-ak.f.st-hatena.com/images/fotolife/m/masatora_bd5/20250728/20250728023151.jpg) #### ホーム画面 ![IMG_2431_opt.jpg|300](https://cdn-ak.f.st-hatena.com/images/fotolife/m/masatora_bd5/20250728/20250728023159.jpg)