raquo / Laminar

Simple, expressive, and safe UI library for Scala.js

Home Page:https://laminar.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support animation events

nghuuphuoc opened this issue · comments

The event handlers look like as following

div(
  onAnimationStart --> ...,
  onAnimationonIteration --> ...,
  onAnimationEnd --> ...,
)

Hey, this is actually pretty easy. Someone would need to add these events to MiscellaneousEventProps.scala in the Scala DOM Types project.

  • Add a new DomAnimationEvent <: DomEvent type param to the trait
  • Add four lazy vals in alphabetic order, with comments from MDN, similar to others already there

The four animation related events are listed here.

PR welcome!

I found the current workarounds are

  1. Handle the event manually:
private def handleAnimationEnd(event: AnimationEvent) = {
  ...
}

// Render
onMountCallback { ctx =>
  ctx.thisNode.ref.addEventListener[AnimationEvent]("animationend", handleAnimationEnd)
},
onUnmountCallback { ctx =>
  ctx.ref.removeEventListener[AnimationEvent]("animationend", handleAnimationEnd)
}
  1. Follow the Missing keys section:
private val onAnimationEnd = new ReactiveEventProp[dom.AnimationEvent]("animationend")

private def handleAnimationEnd = (event: AnimationEvent) => {
  ...
}

// Render
div(
  onAnimationEnd --> handleAnimationEnd
)

Out of these, the second one is definitely better.

If anyone has the time, it would be great to add these properly to Scala DOM Types. Over time this will mean we need less and less workarounds.

I am going to create the PR 👍

btw, forgot to mention in case anyone does something like this.

In the example above, for removeEventListener to work, you need to define your def handleAnimationEnd as a val handleAnimationEnd: js.Function1[dom.AnimationEvent, Unit] instead.

Otherwise, because addEventListener and removeEventListener accept js.Function1, this conversion from a Scala method to to js.Function1 will happen implicitly at the two usage sites of your handleAnimationEnd, and each of those conversions will create a new instance of js.Function1. So when you call removeEventListener, you will be passing a newly created instance of js.Function1 that has not been added as a listener, so the callback that was added previously will not be removed. This would become apparent if you tried to re-mount the component later, as you'd now have two callbacks registered that do the same thing twice.

Hope that makes sense. Just a small scala-js interop hiccup. When using Laminar methods (-->) you don't have to worry about this.

Thank you @raquo 👍