5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiveViewとthree.jsを使ってアニメーションを作ろう

Last updated at Posted at 2025-11-21

このコラムはLiveViewを使って3Dアニメーションをする事を
目標にします
LiveViewを使って3Dアニメーションがなかったので自作しました

環境構築

$ git clone https://github.com/ymn-t-yamanashi/3js.git
$ cd 3js/three/assets
$ npm i
$ cd ../
$ mix deps.get
$ mix phx.server

下記のURLアクセス

http://localhost:4000/

実行結果
image.png

ソースコード

defmodule ThreeWeb.CgLive.Index do
  use ThreeWeb, :live_view
  import ThreeWeb.Cg.CgHelper

  @impl true
  def mount(_params, _session, socket) do
    Process.send_after(self(), :update, 500)

    socket =
      socket
      |> assign(data: initialization_character_data())
      |> add_cube("cube", 1, 1, 1, "#555555")
      |> position("cube", 1, 2, 0)
      |> add_cube("cube1", 1, 1, 1, "#777777")
      |> position("cube1", -2, -1, 0)
      |> add_planes()
      |> load_texture("texture", "https://threejs.org/examples/textures/crate.gif")
      |> load_texture("t1", "images/t1.jpg")
      |> load_model("test", "images/test.vrm")
      |> load_model("test1", "images/test.vrm")

    {:ok, main(socket)}
  end

  @impl true
  def handle_info(:update, socket) do
    Process.send_after(self(), :update, 12)
    {:noreply, main(socket)}
  end

  def handle_event("load_model", %{"name" => "test", "status" => "completion"}, socket) do
    socket =
      socket
      |> position("test", 1, -1, 3)

    {:noreply, socket}
  end

  def handle_event("load_model", %{"name" => "test1", "status" => "completion"}, socket) do
    socket =
      socket
      |> position("test1", 0, 0, 0)

    {:noreply, socket}
  end

  def handle_event("load_texture", %{"name" => "texture", "status" => "completion"}, socket) do
    socket =
      socket
      |> set_texture("cube1", "texture")

    {:noreply, socket}
  end

  def handle_event("load_texture", %{"name" => "t1", "status" => "completion"}, socket) do
    socket =
      socket
      |> set_textures()

    {:noreply, socket}
  end

  defp initialization_character_data() do
    0
  end

  defp main(socket) do
    character_data = update(socket.assigns.data)
    # |> IO.inspect()

    socket
    |> rotation("cube1", character_data, 0, 0)
    |> rotation("cube", 0, 0, character_data)
    |> rotation("test", 0, character_data / 4, 0)
    |> rotation("test1", 0, -character_data * 6, 0)
    |> positions(-character_data)
    |> assign(data: character_data)
  end

  defp update(character_data) do
    character_data + 0.05
  end

  defp add_planes(socket) do
    Enum.reduce(1..1_000, socket, fn x, acc ->
      acc
      |> add_plane("bg_#{x}", 2, 2, "#666666")
      |> add_plane("bg_a#{x}", 2, 2, "#666666")
      |> add_plane("bg_b#{x}", 2, 2, "#666666")
    end)
  end

  defp set_textures(socket) do
    Enum.reduce(1..1_000, socket, fn x, acc ->
      set_texture(acc, "bg_#{x}", "t1")
      |> set_texture("bg_a#{x}", "t1")
      |> set_texture("bg_b#{x}", "t1")
    end)
  end

  defp positions(socket, add_x) do
    Enum.reduce(1..1_000, socket, fn x, acc ->
      position(acc, "bg_#{x}", -100 + x * 2 + add_x * 8, -4, -2)
      |> position("bg_a#{x}", -100 + x * 2 + add_x * 4, -2, -2)
      |> position("bg_b#{x}", -100 + x * 2 + add_x * 2, 0, -2)
    end)
  end
end

add_cube

キューブ(立方体)を追加する関数

add_cube(socket, name, x, y, z, color)
  • socket: ソケット
  • name: 3Dオブジェクトの名前 (文字列)
  • x, y, z: 座標
  • color: 色 例: "#555555"

rotation

3Dオブジェクトを回転する関数

rotation(socket, name, x, y, z)
  • socket: ソケット
  • name: 3Dオブジェクトの名前 (文字列)
  • x, y, z: 回転させる数値

position

position(socket, name, x, y, z)

3Dオブジェクトを位置を指定する関数

  • socket: ソケット
  • name: 3Dオブジェクトの名前 (文字列)
  • x, y, z: オブジェクトを移動させたい座標の数値

load_model

3Dモデルをロードする関数

load_model(socket, name, file_name)
  • socket: ソケット
  • name: 3Dオブジェクトの名前 (文字列)
  • file_name: ファイル名(vrm)

3Dモデルのロード完了するとhandle_eventが呼ばれます

handle_event("load_model", %{"name" => name, "status" => "completion"}, socket)

load_texture

テクスチャをロードする関数

load_model(socket, name, file_name)
  • socket: ソケット
  • name: オブジェクトの名前 (文字列)
  • file_name: ファイル名(jpg)

テクスチャをロードがロード完了するとhandle_eventが呼ばれます

handle_event("load_texture", %{"name" => name, "status" => "completion"}, socket)

set_texture

テクスチャをオブジェクトに設定する関数

set_texture(socket, obj_name, texture_name)
  • socket: ソケット
  • obj_name: オブジェクトの名前 (文字列)
  • texture_name: テクスチャ名 (文字列)

これらを使ってLiveViewでも3D表示できました
今はこのレベルですが、技術的にはLiveViewにthree.jsの機能を移植はできると思います

DevelopGameXというグループでElixirを使ったゲームを作る会を不定期で開催してます

5
1
0

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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?