## ロック画面またはホーム画面に「指定時刻までのカウントダウン」を出す方法
* 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
### スクショ
#### ロックスクリーン

#### ホーム画面
