[Tauri #02] Astroで作ったアナログ時計を完全透過デスクトップガジェット化

[Tauri #02] Astroで作ったアナログ時計を完全透過デスクトップガジェット化

はじめに

前回の【#01 環境構築編】に引き続き、今回はTauriの実装編です。

目標は、以前Astroで作成した「サイバーパンク風アナログ時計」を、背景が完全に透けた、デスクトップを自由に動かせるガジェットアプリとして移植すること。

結論から言うと、開発開始からわずか1時間ちょっと(お昼ご飯を食べる前)で完成してしまいました。Tauri、恐るべし。とはいえ、いくつかの「デスクトップアプリ特有の壁」にぶつかったので、その解決策を備忘録として残しておきます。

Astroで製作したアナログ時計に関する記事:

スクリーンショット(完成品):

[Tauri #02] Astroで作ったアナログ時計を完全透過デスクトップガジェット化

1. コードの移植と「ドラッグ移動」の壁

まずはAstroで書いていたHTML/CSS/JSを、TauriのVanilla JSテンプレートにそのまま移植しました。この時点では「白い背景のウィンドウの中に時計がある」という、ただのWebブラウザと同じ状態です。

[Tauri #02] 1. コードの移植と「ドラッグ移動」の壁

これをガジェットらしく「時計を掴んで自由に移動できる」ようにするため、TauriのAPI startDragging() を呼び出したのですが……いきなりエラーが発生。

Dragging failed window.start_dragging not allowed.
Permissions associated with this command: core:window:allow-start-dragging

解決策:Tauri v2の厳格な権限管理

Tauri(特にv2)はセキュリティが非常に厳しく、デフォルトではフロントエンド(JS)からOSのウィンドウを操作することが許されていません。 これを許可するために、src-tauri/capabilities/default.jsonpermissions に権限を追記する必要があります。

{
  "permissions": [
    "core:default",
    "core:window:allow-start-dragging", // ドラッグ移動を許可
    "core:window:allow-close"           // アプリの終了を許可
  ]
}

これで、JSから appWindow.startDragging() を呼ぶだけで、OSレベルのヌルヌルなドラッグ移動ができるようになりました。

2. 完全透過への道:OSの「影」を消し去る

ドラッグができるようになったら、次は「時計以外の背景を完全に消し去る」作業です。

まず、CSSで body の背景を透明にし、tauri.conf.jsontransparent: true を設定しました。しかし、結果は「うっすらとした四角い影」が残るという謎の現象に。

[Tauri #02] 2. 完全透過への道:OSの「影」を消し去る [Tauri #02] 2. 完全透過への道:OSの「影」を消し去る

実はこれ、Windows OSが「ウィンドウにはドロップシャドウをつけるものだ」と気を利かせて、透明なウィンドウに影をつけてしまっていたのが原因でした。

解決策:shadow設定をオフにする

src-tauri/tauri.conf.jsonwindows セクションに "shadow": false を追加します。

"windows": [
  {
    "title": "lain-clock",
    "width": 300,
    "height": 300,
    "transparent": true,
    "decorations": false,
    "shadow": false,      // ← これを追加!四角い影を消し去る
    "alwaysOnTop": true
  }
]

これで、不自然な枠や影が一切ない、デスクトップに時計だけが浮いている状態が完成しました!

[Tauri #02] 2. 完全透過への道:OSの「影」を消し去る(shadow設定をオフにする)

3. スライダーでウィンドウサイズを動的に変える

時計には「サイズ変更用のスライダー」を実装していましたが、Webの感覚でCSSの widthheight を変えるだけではダメでした。時計の絵は大きくなっても、Tauriの透明なウィンドウサイズ(枠)自体はそのままなので、はみ出した部分が切れてしまいます。

解決策:Tauri APIでLogicalSizeを変更する

JSからTauriのAPIを叩き、時計のサイズ変更に合わせてOS側のウィンドウサイズもリアルタイムに変更するように書き換えました。

// ウィンドウサイズ変更の権限 "core:window:allow-set-size" を追加した上で実行

async function applySize(size) {
  const numSize = parseInt(size);

  // 1. CSS側の時計サイズを更新
  clock.style.width = `${numSize}px`;
  clock.style.height = `${numSize}px`;

  // 2. Tauri側のウィンドウサイズを同期する
  try {
    const { LogicalSize } = window.__TAURI__.window;
    const win = window.__TAURI__.window;
    const current = win.getCurrentWindow ? win.getCurrentWindow() : win.appWindow;

    // メニュー表示用に少しだけ余白(+20px)を持たせてリサイズ
    await current.setSize(new LogicalSize(numSize + 20, numSize + 20));
  } catch (err) {
    console.error("Failed to resize window:", err);
  }
}

ハマりポイント:スライダーの最大値

最初はウィンドウサイズが巨大化するのを防ぐため、スライダーの最大値を window.innerWidth ベースで計算していました。 しかし、ウィンドウ自体を小さくすると innerWidth も小さくなり、「一度小さくすると二度と大きくできない呪いの縮小ループ」に陥りました。

ここはWebの文脈ではなく、OSの文脈でディスプレイの解像度(screen.width)を基準にするのが正解です。

function updateSliderRange() {
  // ウィンドウ内ではなく、モニター全体のサイズを基準にする
  const maxSafeSize = Math.floor(Math.min(screen.width, screen.height) * 0.8);
  sizeSlider.max = maxSafeSize.toString();
}
[Tauri #02] 3. スライダーでウィンドウサイズを動的に変える

まとめ:Webフロントエンドの知識がそのままデスクトップへ

開発開始から1時間少々。

  • TypeScript/JSでのロジック移植
  • ウィンドウの完全透過化
  • OSレベルでのドラッグ移動
  • リアルタイムなウィンドウリサイズ

これらをWebフロントエンドの知識+ちょっとしたTauriの設定だけで実現できてしまいました。デスクトップに馴染む「俺専用ガジェット」を作る体験は、想像以上にテンションが上がります。

次は「クリック透過(Click Through)」の設定や、PC起動時の自動起動(スタートアップ)など、さらにアプリとしての完成度を高めていこうと思います。

[Tauri #02] 3. スライダーでウィンドウサイズを動的に変える

COMM_LOG: tauri-02-desktop-clock-widget

NO DATA FOUND IN THIS SECTOR.