How to add CSS AnimationEnd event handler to GWT widget?
Asked Answered
B

3

5

I would like my GWT widget to be notified when its CSS animation is over.

In plain HTML/Javascript this is easily done by registering an event handler like so:

elem.addEventListener("webkitAnimationEnd", function(){
    // do something
}, false);
// add more for Mozilla etc.

How can I do this in GWT?

This type of event is unknown to GWT's DOMImpl classes, so I keep getting an error:

"Trying to sink unknown event type webkitAnimationEnd".

Bathetic answered 23/11, 2011 at 22:21 Comment(0)
K
1

You can always write some of the native (JavaScript) code yourself:

public class CssAnimation {
  public static native void registerCssCallback(
      Element elem, AsyncCallback<Void> callback) /*-{
    elem.addEventListener("webkitAnimationEnd", function() {
      $entry(@CssAnimation::cssCallback(Lcom/google/gwt/user/client/rpc/AsyncCallback;)(callback));
    }, false);
  }-*/;


  protected static void cssCallback(AsyncCallback<Void> callback) {
    callback.onSuccess(null);
  }
}

I haven't tried the code above. Let me know if it works as expected.


You can use GWT's Animation class to achieve the same effect. For example,

  new com.google.gwt.animation.client.Animation() {
    final com.google.gwt.dom.client.Style es = widget.getElement().getStyle();

    @Override
    protected void onUpdate(double progress) {
      setOpacity(1 - interpolate(progress));
    }

    private void setOpacity(double opacity) {
      es.setProperty("opacity", Double.toString(opacity));
      es.setProperty("filter", "alpha(opacity=" + 100 * opacity + ")");
    }

    @Override
    protected void onComplete() {
      /* ... run some code when animation completes ... */
    }
  }.run(2000, 5000);
Klong answered 24/11, 2011 at 10:8 Comment(3)
Interesting, I didn't know that. But doing it this way will result in a scripted animation, not a CSS animation, right? GWT still does not know the event. Scripted animations have some drawbacks when it comes to smoothness of performance under heavy load or on some mobile devices.Bathetic
Yes, this will result in scripted animation, which might not be as CPU efficient as pure CSS animation.Tintoretto
@Kev It would have been better to keep these two answers separate ... The comments above refer to the section below the line (the scripted solution). The accepted answer is only the top part above the line, which describes a totally different approach.Bathetic
B
5

Based on Darthenius' answer and Clay Lenhart's Blog, I finally settled for this solution:

private native void registerAnimationEndHandler(final Element pElement,
    final CbAnimationEndHandlerIF pHandler)
/*-{
    var callback = function(){
       [email protected]::onAnimationEnd()();
    }
    if (navigator.userAgent.indexOf('MSIE') < 0) {  // no MSIE support
       pElement.addEventListener("webkitAnimationEnd", callback, false); // Webkit
       pElement.addEventListener("animationend", callback, false); // Mozilla
    }
}-*/;

The CbAnimationEndHandlerIF is a simple custom EventHandler interface:

public interface CbAnimationEndHandlerIF extends EventHandler
{
    void onAnimationEnd();
}

Works like a charm! Thanks Darthenius!

If anyone can spot a weakness in this, of course I'd be happy to know.

Bathetic answered 24/11, 2011 at 21:16 Comment(2)
You are welcome. Nice abstraction for callback, by the way.Tintoretto
I just modified my code above to exclude MSIE, because MSIE cannot do CSS keyframe animations (at least up to version 9), and would also require a different syntax for adding the listener.Bathetic
K
1

You can always write some of the native (JavaScript) code yourself:

public class CssAnimation {
  public static native void registerCssCallback(
      Element elem, AsyncCallback<Void> callback) /*-{
    elem.addEventListener("webkitAnimationEnd", function() {
      $entry(@CssAnimation::cssCallback(Lcom/google/gwt/user/client/rpc/AsyncCallback;)(callback));
    }, false);
  }-*/;


  protected static void cssCallback(AsyncCallback<Void> callback) {
    callback.onSuccess(null);
  }
}

I haven't tried the code above. Let me know if it works as expected.


You can use GWT's Animation class to achieve the same effect. For example,

  new com.google.gwt.animation.client.Animation() {
    final com.google.gwt.dom.client.Style es = widget.getElement().getStyle();

    @Override
    protected void onUpdate(double progress) {
      setOpacity(1 - interpolate(progress));
    }

    private void setOpacity(double opacity) {
      es.setProperty("opacity", Double.toString(opacity));
      es.setProperty("filter", "alpha(opacity=" + 100 * opacity + ")");
    }

    @Override
    protected void onComplete() {
      /* ... run some code when animation completes ... */
    }
  }.run(2000, 5000);
Klong answered 24/11, 2011 at 10:8 Comment(3)
Interesting, I didn't know that. But doing it this way will result in a scripted animation, not a CSS animation, right? GWT still does not know the event. Scripted animations have some drawbacks when it comes to smoothness of performance under heavy load or on some mobile devices.Bathetic
Yes, this will result in scripted animation, which might not be as CPU efficient as pure CSS animation.Tintoretto
@Kev It would have been better to keep these two answers separate ... The comments above refer to the section below the line (the scripted solution). The accepted answer is only the top part above the line, which describes a totally different approach.Bathetic
E
0

I expanded a bit on the solution from Darthenius. This code also includes a mechanism to remove the event handler when it is finished. This is what I needed for my application but may not be what you want in all contexts. YMMV!

My final code looks like this:

import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class CssAnimation {
    public static native void registerCssCallback(Element elem, AsyncCallback<Void> callback) /*-{
        var eventListener = function () {
            $entry(@CssAnimation::cssCallback(Lcom/google/gwt/user/client/rpc/AsyncCallback;)(callback));
            elem.removeEventListener("webkitAnimationEnd", eventListener);
        };

        elem.addEventListener("webkitAnimationEnd", eventListener, false);
    }-*/;

    protected static void cssCallback(AsyncCallback<Void> callback) {
        callback.onSuccess(null);
    }
}
Electrolytic answered 25/2, 2015 at 23:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.