GWT 中实现流畅的类 jQuery 动画效果

17

我正在使用GWT的Animation类进行练习。我的目标不是为GWT创建另一个widget/animation库。而且,我很清楚有许多库可以做到这一点。 我尝试做的是学习如何完成这个动作,而不是如何使用某个库。 这仅用于教育目的....

话虽如此,我已经实现了一个简单的类,可以对某些小部件执行4种不同的动画:淡入、淡出、滑入和滑出(遵循jQuery的动画模型)。请参见下面的代码,了解我如何实现它。

在线演示: http://www.rodrigo-silveira.com/gwt-custom-animation/

我的问题: 如何平稳地[成功地]停止动画,只要另一个动画被触发,就可以在当前动画留下的位置继续运行?

例如,如果slideIn()在一半时调用slideOut(),我该如何从小部件的自然高度50%开始向外滑动?我试着保持一个成员变量,始终跟踪当前进度,以便我可以在新动画中使用它,但似乎做不对。有什么建议吗?

import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Element;

public class RokkoAnim extends Animation {

    private Element element;
    private int currentOp;
    private final static int FADE_OUT = 0;
    private final static int FADE_IN = 1;
    private final static int SLIDE_IN = 2;
    private final static int SLIDE_OUT = 3;


    public RokkoAnim(Element element) {
        this.element = element;
    }

    public void fadeOut(int durationMilli) {
        cancel();
        currentOp = RokkoAnim.FADE_OUT;
        run(durationMilli);
    }

    public void fadeIn(int durationMilli) {
        cancel();
        currentOp = RokkoAnim.FADE_IN;
        run(durationMilli);
    }

    public void slideIn(int durationMilli) {
        cancel();
        currentOp = RokkoAnim.SLIDE_IN;
        run(durationMilli);
    }

    public void slideOut(int durationMilli) {
        cancel();
        currentOp = RokkoAnim.SLIDE_OUT;
        run(durationMilli);
    }

    @Override
    protected void onUpdate(double progress) {
        switch (currentOp) {
        case RokkoAnim.FADE_IN:
            doFadeIn(progress);
            break;
        case RokkoAnim.FADE_OUT:
            doFadeOut(progress);
            break;
        case RokkoAnim.SLIDE_IN:
            doSlideIn(progress);
            break;
        case RokkoAnim.SLIDE_OUT:
            doSlideOut(progress);
            break;
        }
    }

    private void doFadeOut(double progress) {
        element.getStyle().setOpacity(1.0d - progress);
    }

    private void doFadeIn(double progress) {
        element.getStyle().setOpacity(progress);
    }

    private void doSlideIn(double progress) {
        double height = element.getScrollHeight();
        element.getStyle().setHeight(height * (1.0d - progress), Unit.PX);
    }

    private void doSlideOut(double progress) {
            // Hard coded value. How can I find out what
            // the element's max natural height is if it's 
            // currently set to height: 0 ?
        element.getStyle().setHeight(200 * progress, Unit.PX);
    }
}

使用方法

// 1. Get some widget
FlowPanel div = new FlowPanel();

// 2. Instantiate the animation, passing the widget
RokkoAnim anim = new RokkoAnim(div.getElement());

// 3. Perform the animation

// >> inside mouseover event of some widget:
anim.fadeIn(1500);

// >> inside mouseout event of some widget:
anim.fadeOut(1500);

你能分享一下你开始动画的代码吗? - Sam
@Sam 刚刚编辑了帖子。请查看使用示例。 - rodrigo-silveira
1
应该是 anim.fadeIn(1500) 而非 div.fadeIn(1500)fadeOut 同理),对吗? - Eliran Malka
糟糕...你真是个细心的人。谢谢。 - rodrigo-silveira
3个回答

8

根据当前值更新透明度(等同于+=-=赋值):

private void doFadeOut(double progress) {
    double opacity = element.getStyle().getOpacity();
    element.getStyle().setOpacity(opacity - 1.0d - progress);
}

private void doFadeIn(double progress) {
    double opacity = element.getStyle().getOpacity();
    element.getStyle().setOpacity(opacity + progress);
}

2
请注意,更改 CSS 值过多会导致浏览器性能不佳。如果可以的话,您应该使用 CSS 转换,例如 transition: opacity .25s ease-in-out;,并在 doFadeIndoFadeOut 中设置或取消类。 - Maarten

1
  1. 在你的动画类中添加一个实例方法 bool canceled。
  2. 只有在未取消时才运行动画。
  3. 添加一个方法 cancelAnnimation,并在每次启动新动画时调用此方法。

    private bool canceled = false;
    
    public void cancelAnnimation() {
        canceled = true;
    }
    
    private void doFadeOut(double progress) {
        if(!canceled) {
           element.getStyle().setOpacity(1.0d - progress);
        }
    }
    
    ... etc.
    

这就是你可以停止动画的方法。但现在,你需要从当前点开始启动新的动画。所以最好的方法是再次修改类。

  1. 添加两个实例变量currentProgress和offset(double)
  2. 在构造函数中添加一个双倍偏移量
  3. 取消动画时返回currentProgress。
  4. 使用第一个动画的偏移量启动新动画。

    private bool canceled = false;
    private double currentProgress = 0;
    private double offset = 0;
    
    public RokkoAnim(Element element, double offset) {
        this.element = element;
        this.offset = offset;
    }
    
    public double cancelAnimation() {
        canceled = true;
        return currentProgress;
    }
    
    private void doFadeOut(double progress) {
    
        if(!canceled && ((progress + offset) <= 1)) {
           element.getStyle().setOpacity(1.0d - (progress+offset));
           currentProgress = progress+offset;
        }
    }
    
    ... etc.
    
    
    double offset = anim1.cancelAnimation();
    RokkoAnim anim2 = new RokkoAnim(div.getElement(), offset);
    

为什么要将因子设置为-1?这个值行不通。如果一个小部件正在淡入...它从不透明度=0开始,当其透明度达到0.7(70%可见)时,小部件开始淡出。因此,我们将因子设置为-1并停止淡入。因此,淡出应该从透明度=0.7开始(而不是当前的1.0),并从那里减少透明度。根据您的建议,当首次调用doFadeOut(process〜= 0.1)时,不透明度不应超过0.7。但是您的计算将不透明度设置为(1 -(-1 * 0.1))= 1.1,这是错误的。 - rodrigo-silveira
你是对的,我犯了一个错误。我以为你想回滚正在运行的动画。但即使如此,你仍然需要跟踪当前的占用情况。 - Sam
仍然无法工作。特别是当我需要每次都创建一个新的anim类实例来进行不同的动画时。 - rodrigo-silveira
根据罗伯特·C·马丁的《代码整洁之道》一书,您应该将动画类分成4个类。一个用于fadeIn,一个用于fadeOut等等... 您不应该在一个类中创建4个动画。每个开发人员都应该阅读这本书http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882。 - Sam

0

为了实现滑入和滑出,我修改了您的类以使用小部件屏幕坐标:

public class WidgetAnimation extends Animation {

    private Element element;
    private int currentOp;
    private int startX=-1;
    private int finalX=-1;
    private final static int FADE_OUT = 0;
    private final static int FADE_IN = 1;
    private final static int SLIDE_IN = 2;
    private final static int SLIDE_OUT = 3;


    public WidgetAnimation(Element element) {
        this.element = element;
    }

    public void fadeOut(int durationMilli) {
        cancel();
        currentOp = WidgetAnimation.FADE_OUT;
        run(durationMilli);
    }

    public void fadeIn(int durationMilli) {
        cancel();
        currentOp = WidgetAnimation.FADE_IN;
        run(durationMilli);
    }

    public void slideIn(int durationMilli) {
        cancel();
        currentOp = WidgetAnimation.SLIDE_IN;
        run(durationMilli);
    }

    public void slideOut(int durationMilli) {
        cancel();
        currentOp = WidgetAnimation.SLIDE_OUT;
        run(durationMilli);
    }

    @Override
    protected void onUpdate(double progress) {
        switch (currentOp) {
        case WidgetAnimation.FADE_IN:
            doFadeIn(progress);
            break;
        case WidgetAnimation.FADE_OUT:
            doFadeOut(progress);
            break;
        case WidgetAnimation.SLIDE_IN:
            doSlideIn(progress);
            break;
        case WidgetAnimation.SLIDE_OUT:
            doSlideOut(progress);
            break;
        }
    }

    private void doFadeOut(double progress) {
        element.getStyle().setOpacity(1.0d - progress);
    }

    private void doFadeIn(double progress) {
        element.getStyle().setOpacity(progress);
    }

    private void doSlideIn(double progress) {
        if(startX==-1){
            finalX = element.getAbsoluteLeft();
            startX = - Window.getClientWidth();
        }

        int positionX = (int) (startX + (progress * (this.finalX - startX)));

        this.element.getStyle().setLeft(positionX, Unit.PX);
    }

    private void doSlideOut(double progress) {
        if(startX==-1){
            startX = element.getAbsoluteLeft();
            finalX = - Window.getClientWidth();
        }

        int positionX = (int) (startX + (progress * (this.finalX - startX)));
        GWT.log("StartX:" + startX);
        GWT.log("finalX:" + finalX);
        GWT.log("positionX:" + positionX);
        GWT.log("progress:" + progress);

        this.element.getStyle().setLeft(positionX, Unit.PX);
    }

}

我正在水平滑动,但您可以修改代码。

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接