🐡

Tailwind CSS 前編 -- YouTube チャンネル namba.ex 第3回解説

2024/07/04に公開

動画

https://youtu.be/XxJnEcrbiQk

前回の続きです。

ライブコーディング(1)

前回の動画では、ブラウザの画面に表示されている円板をユーザーがクリックするとランダムに大きさが変わっていくところまでを作りました。

今回の動画では、ユーザーがクリックすると円板の色がランダムに切り替わるようにアプリケーションを作り変えていきます。

修正対象となるファイルは、次の2つです:

  • lib/dynamic_disc_web/live/demo_live.html.heex
  • lib/dynamic_disc_web/live/demo_live.ex

修正前の demo_live.html.heex は次の通りです:

<main>
  <div class="p-4">
    <%= @diameter %>,
    "<%= @bg_color %>"
  </div>

  <div class="flex justify-around">
    <div
      phx-click="change_state"
      class="bg-red-500 rounded-full cursor-pointer"
      style={"width: #{@diameter}px; height: #{@diameter}px"}
    >
    </div>
  </div>
</main>

まず、10行目の class 属性の値("bg-red-500 rounded-full cursor-pointer")をプライベート関数 get_class/0 として抜き出し、demo_live.html.ex の末尾に追加しました。

  ...

  def handle_event("change_state", _params, socket) do
    socket =
      socket
      |> assign(:diameter, Enum.random(50..300))
      |> assign(:bg_color, "")

    {:noreply, socket}
  end

  defp get_class() do
    "bg-red-500 rounded-full cursor-pointer"
  end
end

そして、このプライベート関数 get_class/0 を用いて、demo_live.html.heex を修正しました。

<main>
  <div class="p-4">
    <%= @diameter %>,
    "<%= @bg_color %>"
  </div>

  <div class="flex justify-around">
    <div
      phx-click="change_state"
      class={get_class()}
      style={"width: #{@diameter}px; height: #{@diameter}px"}
    >
    </div>
  </div>
</main>

続いて、get_class 関数を後で使いやすいように少し書き換えました。

  ...

  def handle_event("change_state", _params, socket) do
    socket =
      socket
      |> assign(:diameter, Enum.random(50..300))
      |> assign(:bg_color, "")

    {:noreply, socket}
  end
  
  @class_tokens ~w(
    bg-red-500
    rounded-full
    cursor-pointer
  )

  defp get_class() do
    Enum.join(@class_tokens, " ")
  end
end

@class_tokens で始まる4行では、モジュール属性を定義しています。他のプログラミング言語で言う「定数」あるいは「マクロ」に当たります。

~w( ... ) のように書くと、カッコで囲まれた部分が文字列のリストとして解釈されます。つまり、
"bg-red-500", "rounded-full", "cursor-pointer" という 3 個の文字列を要素として持つリストがで
きます。これを関数 Enum.join/2 で連結しています。

この時点では、アプリケーションの振る舞いに変化はありません。

ライブコーディング(2)

次に、demo_live.ex を次のように書き換えました。

  ...

  def handle_event("change_state", _params, socket) do
    socket =
      socket
      |> assign(:diameter, Enum.random(50..300))
      |> assign(:bg_color, "bg-red-500")

    {:noreply, socket}
  end
  
  @class_tokens ~w(
    rounded-full
    cursor-pointer
  )

  defp get_class(bg_color) do
    tokens = [bg_color | @class_tokens]
    Enum.join(tokens, " ")
  end

関数 get_class のアリティ(引数の数)を 0 から 1 に変えました。

リスト @class_tokens の先頭に token を加えてできる新たなリストを変数 tokens に加えています。仮引数 bg_color には "bg-red-500" が渡ってくるので、実質的に関数 get_class の振る舞いは変化していません。

tokens = [bg_color | @class_tokens] は、Elixirの独特の書き方です。次の例を見ると、角カッコとパイプ文字の使い方が理解できるでしょう:

a = ["x", "y", "z"]
b = ["w" | a]

上記のコードを評価すると、変数 b["w", "x", "y", "z"] がセットされます。

続いて、demo_live.html.heex を修正しました。

<main>
  <div class="p-4">
    <%= @diameter %>,
    "<%= @bg_color %>"
  </div>

  <div class="flex justify-around">
    <div
      phx-click="change_state"
      class={get_class(@bg_color)}
      style={"width: #{@diameter}px; height: #{@diameter}px"}
    >
    </div>
  </div>
</main>

ライブコーディング(3)

最後に、円板の色がランダムに変化するように、ソースコードを書き換えました。

  ...

  @background_colors ~w(
    bg-red-500
    bg-green-500
    bg-blue-500
    bg-cyan-500
  )

  def handle_event("change_state", _params, socket) do
    bg_color = Enum.random(@background_colors)

    socket =
      socket
      |> assign(:diameter, Enum.random(50..300))
      |> assign(:bg_color, bg_color)

    {:noreply, socket}
  end
  
  @class_tokens ~w(
    rounded-full
    cursor-pointer
  )

  defp get_class(bg_color) do
    tokens = [bg_color | @class_tokens]
    Enum.join(tokens, " ")
  end

注目すべきは、bg_color = Enum.random(@background_colors) です。モジュール属性 @background_colors には、赤、緑、青、シアンにする Tailwind CSS のクラストークンのリストがセットされています。関数 Enum.random/1 を用いてその中からランダムに1つを取り出しています。

上記の書き換えの結果、ユーザーが円板内をクリックするたびに、色がランダムに変化するようになりました。

ただし、動画内でも触れたように、4 分の 1 の確率で、現在の色と同じ色が選ばれてしまうので、クリックしたのに色が変化しないという現象が発生します。この問題には次回の動画で対応します。

Discussion