38
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Tauri製アプリでネイティブ感を出す8つのTips

Last updated at Posted at 2022-10-11

本記事はhttps://blog.physalis.net/2022/how-to-make-tauri-app-look-and-feel-nativeからの転載です。

先日Tauriで作ったJomaiをリリースしました。Markdownに特化したデスクトップサーチアプリです。Tauri製のアプリはもちろんネイティブアプリなのですが、UI部分がWebViewで動作するため使用感がWebアプリっぽくなりがちです。Jomaiではなるべくネイティブアプリらしく振る舞えるように工夫したので、ここでまとめてみます。
なおJomaiは今のところmacOS専用のためこの記事もmacOSを対象としています。

環境

  • Tauri: 1.1.1
  • Platform: macOS

非入力項目でのキー入力でビープ音がなる問題

<input><textarea> 以外の非入力項目にフォーカスがあるときにキー入力するとビープ音が鳴ります。macOSのWebViewで発生する問題でGitHubのissueがあり、2022年10月6日現在解決していません。
https://github.com/tauri-apps/tauri/issues/2626

keydownイベントをpreventDefault()すれば抑制できるのですが、その場合<input>などへの入力も全部無効になってしまうため選択的に抑制する必要があります。

必要なkeydownイベントもあると思うので、listenして処理してから不要だったら捨てるようにします。Jomaiでは次のコードを使いました。

export const useKey = (callback: UseKeyCallback) => {
  const onKeydown = useCallback(
    (event: KeyboardEvent) => {
      // コールバックは処理したら true を返す
      const consumed = callback(event.code, {
        ctrl: event.ctrlKey,
        shift: event.shiftKey,
      });

      const e = event.composedPath()[0];
      if (
        !(e instanceof HTMLInputElement || e instanceof HTMLAreaElement)
        || consumed
      ) {
        // 入力項目じゃなかったら preventDefault()
        // コールバックにより処理された場合も preventDefault()
        applyBeepSoundWorkaround(event);
      }
    },
    [callback],
  );

  useEffect(() => {
    window.addEventListener('keydown', onKeydown);
    return () => {
      window.removeEventListener('keydown', onKeydown);
    };
  }, [onKeydown]);
};

イベントのソースがHTMLInputElementまたはHTMLAreaEelement以外の場合にpreventDefault()すればほぼ大丈夫なのですが、これら入力項目にフォーカスが当たっている時にキーボードショートカットを使ってフォーカスが非入力項目に移動した時(ややこしい)も音が鳴ってほしくないので少々複雑な処理をしています。

UIのテキストを選択できないようにする

Webページのテキストは選択できるのが当たり前ですがネイティブアプリは一般的にそうではありません。UIを操作しようとして意図せずテキスト選択になるを避けるために<body>などDOM要素の根元の方でuser-select: noneとしておくことでテキスト選択を防げます。

body {
  user-select: none;
}

マウスカーソルをdefaultに設定する

前項によりUI要素の操作はボタンを押すなどに限定したので、マウスカーソルでもそのように表現しましょう。

body {
  cursor: default;
}

cursor: defaultにより、たとえばテキストにホバーしたときにIビームになるのを防ぎます。ボタンなど操作可能な場合はcursor属性を明示的に指定してください。

button {
  cursor: pointer;
}

画面全体をスクロールさせない

画面のサイズよりコンテンツの方が大きい場合、多くのWebページでは画面全体がスクロールします。ネイティブアプリではUI部品を固定表示して、その一部の部品だけスクロール可能とするのが自然です。
Jomaiでは画面の上部のタブやフォームは常時表示され、下部のコンテンツ部分だけがスクロール可能です。
scrollable-area.png
実際の動作をご覧ください。
scrollable-area.gif

これを抑制するには画面全体にoverflow: hiddenを設定し、スクロール可能な部分にoverflow: scrollheightを設定します。

.page {
  overflow: hidden;
}

.contents {
  overflow: scroll;
  height: calc(100vh - 40px)
}

画面全体のバウンススクロールを抑制する

バウンススクロール、画面の端までスクロールするとバネのような動きをするアレです。デフォルトでは画面全体がスクロール可能で、バウンススクロールになります。この動画を見てください。
bounce-scroll.gif

バウンススクロールはスクロールが可能であることを連想させますので、画面全体がスクロールするべきコンテンツでない場合には抑制しましょう。

body {
  overflow: hidden;
}

メニューを用意する

Tauriはデフォルトで基本的なメニューを用意してくれます。
menu.png

カスタマイズが必要な場合にはちょっと面倒ですが自分でこれらのメニューを構築する必要があります。tauri::Menu::os_defaultが参考になります。

キーボードショートカットを用意する

Webページでは独自のキーボードショートカットはユーザーを混乱させることがあるので使うときは注意が必要ですが、ネイティブアプリには積極的に導入しましょう。

ダークモードをサポートする

私はダークモードのファンなので常用するアプリにはダークモードをサポートしてもらいたいです。
Tauriには現在のテーマを得るappWindow.theme()と変更イベントを監視するためのappWindow.onThemeChanged()があります。これらを使えばOSのテーマ設定に合わせてアプリを変更できます。

Reactでの実装例です。

import { appWindow, Theme } from '@tauri-apps/api/window';

export const useTheme = () => {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  useEffect(() => {
    let unlisten: UnlistenFn | undefined;

    (async () => {
      setTheme(await appWindow.theme());

      unlisten = await appWindow.onThemeChanged(({ payload: theme }) => {
        console.log(`theme changed to ${theme}`);
        setTheme(theme);
      });
    })();

    return () => {
      if (unlisten != null) {
        unlisten();
      }
    };
  }, []);

  return theme;
};

まとめ

この記事ではTauri製のアプリを、よりネイティブらしく仕上げるTipsを紹介しました。ちょっとした手間でアプリの使い勝手が向上します。参考になれば幸いです。

これらのテクニックを使って制作したJomaiもよろしくお願いします。

38
28
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?