MaxLyman

MaxLyman

Dynamic JS in a Phoenix live component

I am learning Phoenix and Elixir and making myself a website. I know this is probably not the best way to do what I want but I am up for the challenge. Part of the website is an animation of a bike. There are two main animations that I want to do, the first is just one that goes across the window and stops. The second will follow the user’s scroll and go off the page.

The animation of it going across the screen works:

defmodule MaxLymanWeb.Landing.BikeAnimation do
  use MaxLymanWeb, :live_component

  require Logger

  def mount(socket) do
    {:ok, socket}
  end

  def render(assigns) do
    direction = assigns.direction || "right-to-left"
    animation_number = assigns.animation_number || "1"
    id = "bike-animation-#{animation_number}"

    image = case direction do
      "right-to-left" -> "/images/rtl-road-bike.png"
      "left-to-right" -> "/images/ltr-road-bike.png"
      _ -> {:error, "Invalid direction"}
    end

    padding = case direction do
      "right-to-left" -> "pl-80"
      "left-to-right" -> "pr-80"
      _ -> {:error, "Invalid direction"}
    end

    ride_direction = case direction do
      "right-to-left" -> "scrolling-div-rtl"
      "left-to-right" -> "scrolling-div-ltr"
      _ -> {:error, "Invalid direction"}
    end

    assigns =
      assigns
      |> Map.put(:id, id)
      |> Map.put(:image, image)
      |> Map.put(:padding, padding)
      |> Map.put(:tailwind_class, "#{padding} #{ride_direction}")

    if assigns.scroll do
      render_with_scroll(assigns)
    else
      render_without_scroll(assigns)
    end


  end

  defp render_without_scroll(assigns) do
    Logger.info("Rendering without scroll") # WORKING
    ~H"""
      <div id={@id} class={@tailwind_class}>
        <div name="bike">
          <div class="w-24 h-24">
            <img
              name="bike_img"
              src={@image}
              class={"w-24 h-24"}
              alt="bike"/>
          </div>
        </div>
      </div>
    """
  end

it is the scroll that is giving me the most trouble:


  defp render_with_scroll(assigns) do
    Logger.info("Rendering with scroll")

    ~H"""
     <div>
        <a name="direction" class="invisible"> <%= @direction%></a>
        <a name="id" class="invisible"> <%= @id%></a>
        <div class="overflow-hidden">
          <div id={@id} class={@padding}>
            <div class="w-24 h-24">
              <img
                name="bike_img"
                src={@image}
                class="w-24 h-24"
                alt="bike"/>
            </div>
          </div>
          <script>
            window.addEventListener("scroll", function() {
              var direction = document.getElementsByName("direction")[0].textContent;
              var id = document.getElementsByName("id")[0].textContent;
              var bike = document.getElementById(id);
              var scroll = window.scrollY;
              var translation = direction === "right-to-left" ? scroll/2 : -scroll/2;
              console.log("direction", direction, "bike", id, "scroll", scroll, "translation", translation);
              if (bike !== null) {bike.style.transform = "translateX(" + translation + "px)";}

            });
          </script>
        </div>
     </div>
    """
  end
end

there are no issues in my console and the console.log statement shows that the translation is changing but the bike is still not moving.

The answer could just be this is not what I should be using Phoenix for but I’m too deep now to not give it one more try. If anyone knows a way to make this work I am all ears!

Thanks!

Marked As Solved

sodapopcan

sodapopcan

You’re doing several things wrong here and could be any one or all of them:

  • To get the gotcha out of the way, I think you have stumbled across this.
  • You should be using js hooks as opposed to putting the script tag in heex like that. Hooks ensure the JS fires at appropriate times in the LV lifecycle.
  • Further to the last point, I’m unsure if this is all your code (and I haven’t fully taken in all of what’s there) but if there is anything updating assigns as it scrolls the UI will be updated to whatever the server thinks it should be. This will result in anything JS has done to be overwritten.
  • Always use an appropriate Phoenix function for manipulating assigns (assign, assign_new, update, etc)—they are not merely wrappers around Map.put and are required for change tracking to work.

Also Liked

MaxLyman

MaxLyman

Here is my solution thanks to @sodapopcan for the help:

//app.js
// Hooks
let Hooks = {}
Hooks.BikeAnimation = {
  direction() {return this.el.dataset.direction},
  mounted(){
    // set images based on direction
    var img = this.direction() === "rtl" ? "/images/rtl-road-bike.png" : "/images/ltr-road-bike.png";
    var imageElement = document.getElementById("bike-image");
    imageElement.src = img;

    // set animation based on direction
    window.addEventListener("scroll", e => {
      var scroll = window.scrollY;
      var translation = this.direction() === "rtl" ? -scroll/2: scroll/2;
      var bike = document.getElementById("bike-div");
      if (imageElement !== null ) {
        imageElement.style.transform = `translateX(${translation}px)`;
      }
    });
  }
} 

//bike_animation.ex
  defp render_with_scroll(assigns) do
    Logger.info("Rendering with scroll")
    ~H"""
     <div>
        <div class="overflow-hidden">
          <div id="bike-div" phx-hook="BikeAnimation" data-direction="rtl" class={@padding}>
            <div class="w-24 h-24" >
              <img
                id="bike-image"
                src="/images/rtl-road-bike.png"
                class="w-24 h-24"
                alt="bike"/>
            </div>
          </div>
        </div>
     </div>
    """
  end
MaxLyman

MaxLyman

Thank you, this is helpful. I am new to Phoenix and haven’t used JS that much either so this will help me get back on track.

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
vegabook
I'm brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 47849 226
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement