From 5485f35be04bc9bf5d06d704f71cfcb001a583b7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Manuel=20Carrasco=20Mo=C3=B1ino?= Date: Sun, 17 Nov 2013 22:36:14 +0100 Subject: [PATCH] Use CSS3 animations when available. --- .../gwt/query/client/plugins/Effects.java | 28 +++-- .../client/plugins/effects/ClipAnimation.java | 12 +- .../gwt/query/client/plugins/effects/Fx.java | 2 + .../plugins/effects/PropertiesAnimation.java | 4 +- .../client/plugins/effects/Transitions.java | 105 ++++++++++-------- .../plugins/effects/TransitionsAnimation.java | 99 ++++++++++++++--- 6 files changed, 165 insertions(+), 85 deletions(-) diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java index 39d792dd..19d49b6b 100755 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java @@ -27,6 +27,8 @@ import com.google.gwt.query.client.plugins.effects.Fx; import com.google.gwt.query.client.plugins.effects.PropertiesAnimation; import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing; import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.EasingCurve; +import com.google.gwt.query.client.plugins.effects.TransitionsAnimation; +import com.google.gwt.query.client.plugins.effects.TransitionsAnimation.TransitionsClipAnimation; /** * Effects plugin for Gwt Query. @@ -43,13 +45,13 @@ public class Effects extends QueuePlugin { // Each Animation is associated to one element protected Element e; protected Properties prps; - + protected GQAnimation setElement(Element element) { e = element; return this; } protected GQAnimation setProperties(Properties properties) { - prps = properties; + prps = properties == null ? Properties.create() : properties; return this; } protected void onStart() { @@ -78,7 +80,6 @@ public class Effects extends QueuePlugin { public static final int SLOW = 600; } - public static final Class Effects = GQuery.registerPlugin( Effects.class, new Plugin() { public Effects init(GQuery gq) { @@ -168,15 +169,24 @@ public class Effects extends QueuePlugin { public Effects animate(Object stringOrProperties, final int duration, final Easing easing, final Function... funcs) { + final Properties p = (stringOrProperties instanceof String) ? $$((String) stringOrProperties) : (Properties) stringOrProperties; for (Element e: elements()) { - queueAnimation(e, new PropertiesAnimation(easing, e, p, funcs), duration); + if (Fx.css3) { + new TransitionsAnimation(easing, e, p, funcs).run(duration); + } else { + queueAnimation(e, new PropertiesAnimation(easing, e, p, funcs), duration); + } } return this; } + private static native void set(Element e) /*-{ + $wnd.eee = e; + }-*/; + /** * * The animate() method allows you to create animation effects on any numeric @@ -318,7 +328,11 @@ public class Effects extends QueuePlugin { final ClipAnimation.Corner c, final ClipAnimation.Direction d, final int duration, final Function... f) { for (Element e : elements()) { - queueAnimation(e, new ClipAnimation(e, a, c, d, f), duration); + if (Fx.css3) { + new TransitionsClipAnimation(e, a, c, d, null, null, f).run(duration); + } else { + queueAnimation(e, new ClipAnimation(e, a, c, d, f), duration); + } } return this; } @@ -468,7 +482,7 @@ public class Effects extends QueuePlugin { public Effects fadeOut(int millisecs, Function... f) { return animate("opacity: 'hide'", millisecs, f); }; - + /** * Fade the opacity of all matched elements to a specified opacity and firing * an optional callback after completion. Only the opacity is adjusted for @@ -478,7 +492,7 @@ public class Effects extends QueuePlugin { public Effects fadeTo(double opacity, Function... f) { return fadeTo(Speed.DEFAULT, opacity, f); } - + /** * Fade the opacity of all matched elements to a specified opacity and firing * an optional callback after completion. Only the opacity is adjusted for diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java index 2f928284..e0047fc0 100755 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java @@ -15,7 +15,6 @@ */ package com.google.gwt.query.client.plugins.effects; - import com.google.gwt.dom.client.Element; import com.google.gwt.query.client.Function; import com.google.gwt.query.client.GQuery; @@ -71,18 +70,14 @@ public class ClipAnimation extends PropertiesAnimation { direction = Direction.valueOf(getNormalizedValue("clip-direction", p)); } catch (Exception e) { } - action = Action.TOGGLE; try { action = Action.valueOf(getNormalizedValue("clip-action", p)); } catch (Exception e) { } - - this.funcs = funcs; - e = elem; g = GQuery.$(e).as(Effects.Effects); } - private String getNormalizedValue(String value, Properties p) { + private static String getNormalizedValue(String value, Properties p) { return JsUtils.hyphenize(p.getStr("clip-direction")).replace("-", "_").toUpperCase(); } @@ -92,8 +87,6 @@ public class ClipAnimation extends PropertiesAnimation { this.action = a; this.corner = c; this.direction = d; - this.funcs = funcs; - e = elem; g = GQuery.$(e).as(Effects.Effects); } @@ -175,7 +168,6 @@ public class ClipAnimation extends PropertiesAnimation { bottom = h; } - String rect = top + "px " + right + "px " + bottom + "px " + left + "px"; - g.css("clip", "rect(" + rect + ")"); + g.css("clip", "rect(" + top + "px " + right + "px " + bottom + "px " + left + "px)"); } } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java index a2a374b8..e497d8f3 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java @@ -18,6 +18,8 @@ public class Fx { */ public static boolean off = false; + public static boolean css3 = Transitions.transition != null; + /** * A pojo to store css3 transition values. */ diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java index 36c5ddc8..d18a220a 100755 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java @@ -140,7 +140,7 @@ public class PropertiesAnimation extends GQAnimation { protected static final RegExp REGEX_SYMBOL_NUMBER_UNIT = RegExp.compile("^([+-]=)?([0-9+-.]+)(.*)?$"); protected static final RegExp REGEX_NON_PIXEL_ATTRS = - RegExp.compile("z-?index|font-?weight|opacity|zoom|line-?height|^\\$", "i"); + RegExp.compile("z-?index|font-?weight|opacity|zoom|line-?height|scale|rotation|^\\$", "i"); private static final RegExp REGEX_COLOR_ATTR = RegExp.compile(".*color$", "i"); @@ -270,7 +270,7 @@ public class PropertiesAnimation extends GQAnimation { protected Easing easing; protected JsObjectArray effects; - protected Function[] funcs; + private Function[] funcs; private Effects g; public PropertiesAnimation(Element elem, Properties p, Function... funcs) { diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java index 4d195e21..dd23c3d1 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java @@ -34,11 +34,11 @@ import com.google.gwt.user.client.DOM; /** * Transitions and transformation plugin for gQuery. - * + * * It is inspired on jquery.transit (http://github.com/rstacruz/jquery.transit) * * Usage examples: - *
 
+ * 
 
     $("#foo")
      .transition("{ opacity: 0.1, scale: 2, x: 50, y: 50 }", 5000, "easeOutBack", 0)
@@ -53,25 +53,35 @@ import com.google.gwt.user.client.DOM;
  * 
*/ public class Transitions extends GQuery { - + /** * A dictionary class with all the properties of an element transform * which is able to return the correct syntax for setting css properties. */ public static class Transform { - + private static final RegExp transform3dRegex = RegExp.compile("^(rotate([XY]|3d)|perspective)$"); - + private HashMap> map = new HashMap>(); public Transform(String s) { parse(s); } - - public List get(String prop) { - return map.get(prop); + + public String get(String prop) { + return listToStr(map.get(prop), ","); + } + + private String listToStr(List l, String sep) { + String v = ""; + if (l != null) { + for (String s : l) { + v += (v.isEmpty() ? "" : sep) + s; + } + } + return v; } - + private void parse(String s) { if (s != null) { RegExp re = RegExp.compile("([a-zA-Z0-9]+)\\((.*?)\\)", "g"); @@ -80,11 +90,11 @@ public class Transitions extends GQuery { } } } - + public void set(String prop, String ...val) { setter(prop, val); } - + public void setFromString(String prop, String ...val) { if (val.length == 1 && val[0] instanceof String) { String[] vals = ((String)val[0]).split("[\\s*,\\s*]"); @@ -93,7 +103,7 @@ public class Transitions extends GQuery { set(prop, val); } } - + private void setter(String prop, String ...val) { if (prop.matches("(rotate[XY]?|skew[XY])")) { map.put(prop, unit(val[0], "deg")); @@ -123,7 +133,7 @@ public class Transitions extends GQuery { map.put("translate", Arrays.asList(map.get("translateX").get(0), map.get("translateY").get(0))); } } - + /** * Converts the dictionary to a transition css string. */ @@ -133,44 +143,41 @@ public class Transitions extends GQuery { String ret = ""; for (Entry> e: map.entrySet()) { if (has3d || !transform3dRegex.test(e.getKey())) { - String v = ""; - for (String s : e.getValue()) { - v += (v.isEmpty() ? "" : ",") + s; - } + String v = listToStr(e.getValue(), ","); ret += (ret.isEmpty() ? "" : " ") + e.getKey() + "(" + v + ")"; } } return ret; } - + private List unit(String val, String unit) { return Arrays.asList(val + (val.endsWith(unit) ? "" : unit)); } } - + // Used to check supported properties in the browser private static Style divStyle = DOM.createDiv().getStyle(); - + private static final String prefix = browser.msie ? "ms" : browser.opera ? "o" : browser.mozilla ? "moz" : browser.webkit ? "webkit" : ""; private static final String transform = getVendorPropertyName("transform"); private static final String TRANSFORM = "_t_"; private static final String transformOrigin = getVendorPropertyName("transformOrigin"); - + protected static final RegExp transformRegex = RegExp.compile("^(scale|translate|rotate([XY]|3d)?|perspective|skew[XY]|x|y)$"); - private static final String transition = getVendorPropertyName("transition"); - + protected static final String transition = getVendorPropertyName("transition"); + private static final String transitionDelay = getVendorPropertyName("transitionDelay"); private static final String transitionEnd = browser.mozilla || browser.msie ? "transitionend" : (prefix + "transitionEnd"); - + public static boolean has3d = supportsTransform3d(); - + public static final Class Transitions = GQuery.registerPlugin( Transitions.class, new Plugin() { public Transitions init(GQuery gq) { return new Transitions(gq); } }); - + private static String getVendorPropertyName(String prop) { // we prefer vendor specific names by default String vendorProp = JsUtils.camelize("-" + prefix + "-" + prop); @@ -186,40 +193,42 @@ public class Transitions extends GQuery { } return null; } - + private static String property(String prop) { if (transformRegex.test(prop)) { return transform; } return prop.replaceFirst("^(margin|padding).+$", "$1"); } - + private static boolean supportsTransform3d() { String rotate = "rotateY(1deg)"; divStyle.setProperty(transform, rotate); rotate = divStyle.getProperty(transform); return rotate != null && !rotate.isEmpty(); } - + protected Transitions(GQuery gq) { super(gq); } - @Override + @Override public String css(String prop, boolean force) { if ("transform".equals(prop)) { - Transform t = data(TRANSFORM); - return t == null ? "" : t.toString(); + return isEmpty() ? "" : getTransform(get(0), null).toString(); } else if ("transformOrigin".equals(prop)) { return super.css(transformOrigin, force); } else if ("transition".equals(prop)) { return super.css(transition, force); + } else if (transformRegex.test(prop)) { + return isEmpty() ? "" : getTransform(get(0), null).get(prop); } else { - return super.css(prop, force); + String ret = super.css(prop, force); + return ret; } } - - @Override + + @Override public Transitions css(String prop, String value) { if ("transform".equals(prop)) { for (Element e : elements()) { @@ -241,7 +250,7 @@ public class Transitions extends GQuery { } return this; } - + private List filterPropertyNames(Properties p) { List ret = new ArrayList(); for (String s : p.keys()) { @@ -251,7 +260,7 @@ public class Transitions extends GQuery { if (m != null) { c = m; } - // chrome needs transition:-webkit-transform instead of transition:transform + // chrome needs transition:-webkit-transform instead of transition:transform c = JsUtils.hyphenize(c); if (!ret.contains(c)) { ret.add(c); @@ -259,7 +268,7 @@ public class Transitions extends GQuery { } return ret; } - + private Transform getTransform(Element e, String initial) { Transform t = data(e, TRANSFORM); if (t == null || initial != null && !initial.isEmpty() ) { @@ -268,11 +277,11 @@ public class Transitions extends GQuery { } return t; } - + /** * The transition() method allows you to create animation effects on any numeric HTML Attribute, * CSS property, or color using CSS3 transformations and transitions. - * + * * It works similar to animate(), supports chainning and queueing and an extra parameter for * delaying the animation. * @@ -289,7 +298,7 @@ public class Transitions extends GQuery { if (isEmpty()) { return this; } - + final Properties p = (stringOrProperties instanceof String) ? $$((String) stringOrProperties) : (Properties) stringOrProperties; final String oldTransitions = css(transition); @@ -297,14 +306,14 @@ public class Transitions extends GQuery { if (easing == null) { easing = EasingCurve.ease; } - + String attribs = duration + "ms" + " " + easing.toString() + " " + delay + "ms"; List props = filterPropertyNames(p); String value = ""; for (String s : props) { value += (value.isEmpty() ? "" : ", ") + s + " " + attribs; } - + final String transitionValue = value; // Use gQuery queue, so as we can chain transitions, animations etc. @@ -313,12 +322,10 @@ public class Transitions extends GQuery { $(this) // Configure animation using transition property .css(transition, transitionValue) - // Set all css properties for this transition using the css method in this class - .as(Transitions).css(p) - // prevent memory leak - .removeData(TRANSFORM); + // Set all css properties for this transition using the css method in this class + .as(Transitions).css(p); }}); - + // restore oldTransitions in the element, and use the queue to prevent more effects being run. // TODO: Use transitionEnd events once GQuery supports non-bit events delay(duration + delay, new Function(){public void f() { @@ -327,7 +334,7 @@ public class Transitions extends GQuery { .css(transition, oldTransitions) .each(funcs); }}); - + return this; } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java index 32e689d7..14865119 100755 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java @@ -21,6 +21,9 @@ import com.google.gwt.dom.client.Element; import com.google.gwt.query.client.Function; import com.google.gwt.query.client.Properties; import com.google.gwt.query.client.js.JsObjectArray; +import com.google.gwt.query.client.plugins.effects.ClipAnimation.Action; +import com.google.gwt.query.client.plugins.effects.ClipAnimation.Corner; +import com.google.gwt.query.client.plugins.effects.ClipAnimation.Direction; import com.google.gwt.query.client.plugins.effects.Fx.TransitFx; import com.google.gwt.regexp.shared.MatchResult; @@ -29,7 +32,66 @@ import com.google.gwt.regexp.shared.MatchResult; * using CSS3 transitions */ public class TransitionsAnimation extends PropertiesAnimation { - + + public static class TransitionsClipAnimation extends TransitionsAnimation { + + private Action action; + private Corner corner; + private Direction direction; + private Action currentAction; + + public TransitionsClipAnimation(Element elem, Action a, Corner c, Direction d, Easing easing, + Properties p, final Function... funcs) { + super(easing, elem, p, funcs); + this.action = a; + this.corner = c; + this.direction = d; + } + + public void onStart() { + boolean hidden = !g.isVisible(); + + super.onStart(); + + currentAction = action != Action.TOGGLE ? action : hidden ? Action.SHOW : Action.HIDE; + int bit = currentAction == Action.HIDE ? 1 : 0; + + String originX = "left", originY = "top"; + int scaleXini = 0^bit, scaleYini = scaleXini; + int scaleXend = 1^bit, scaleYend = scaleXend; + + if (direction == Direction.VERTICAL) { + scaleXini = scaleXend = 1; + } + if (direction == Direction.HORIZONTAL) { + scaleYini = scaleYend = 1; + } + if (corner == Corner.CENTER) { + originX = originY = "center"; + } + if (corner == Corner.TOP_RIGHT || corner == Corner.BOTTOM_RIGHT) { + originX = "right"; + } + if (corner == Corner.BOTTOM_LEFT || corner == Corner.BOTTOM_RIGHT) { + originY = "bottom"; + } + + g.show().css("transformOrigin", originX + " " + originY); + + effects.add(new TransitFx("scale", "", scaleXini + " " + scaleYini, scaleXend + " " + scaleYend, "")); + } + + @Override + public void onComplete() { + super.onComplete(); + if (currentAction == Action.HIDE) { + g.hide(); + } + g.css("transformOrigin", ""); + g.css("transform", "scale(1 1)"); + } + } + public static Fx computeFxProp(Element e, String key, String val, boolean hidden) { Transitions g = $(e).as(Transitions.Transitions); String unit = ""; @@ -47,35 +109,28 @@ public class TransitionsAnimation extends PropertiesAnimation { String cur = g.css(key, true); String trsStart = cur, trsEnd = trsStart; - + if ("show".equals(val)) { g.saveCssAttrs(key); trsStart = "0"; } else if ("hide".equals(val)) { - if (hidden) { - return null; - } g.saveCssAttrs(key); trsEnd = "0"; } else { MatchResult parts = REGEX_SYMBOL_NUMBER_UNIT.exec(val); if (parts != null) { unit = REGEX_NON_PIXEL_ATTRS.test(key) || Transitions.transformRegex.test(key) ? "" : "px"; - + String part1 = parts.getGroup(1); String part2 = parts.getGroup(2); String part3 = parts.getGroup(3); trsEnd = "" + Double.parseDouble(part2); - + if (unit.isEmpty() && part3 != null) { unit = part3; } if (trsStart.isEmpty()) { - trsStart = "0"; - } - - if (!trsStart.endsWith(unit)) { - trsStart += unit; + trsStart = key.matches("scale") ? "1" : "0"; } if (part1 != null && !part1.isEmpty()) { @@ -85,14 +140,15 @@ public class TransitionsAnimation extends PropertiesAnimation { trsEnd = "" + (st + (n*en)); } } else { - trsStart = trsEnd = val; + trsStart = ""; + trsEnd = val; } } return new TransitFx(key, val, trsStart, trsEnd, unit); } - private Transitions g; - + protected Transitions g; + public TransitionsAnimation(Element elem, Properties p, Function... funcs) { this(null, elem, p, funcs); } @@ -106,7 +162,10 @@ public class TransitionsAnimation extends PropertiesAnimation { Properties p = $$(); for (int i = 0; i < effects.length(); i++) { TransitFx fx = (TransitFx)effects.get(i); - p.set(fx.cssprop, (isStart ? fx.transitStart : fx.transitEnd) + fx.unit); + String val = isStart ? fx.transitStart : fx.transitEnd; + if (!val.isEmpty()) { + p.set(fx.cssprop, val + fx.unit); + } } return p; } @@ -135,13 +194,19 @@ public class TransitionsAnimation extends PropertiesAnimation { } } + @Override + public void onUpdate(double progress) { + } + @Override public void run(int duration) { onStart(); Properties p = getFxProperties(true); g.css(p); + // TODO: Reflow, it seems it is not needed in chrome and FF, check other browsers + // g.css("offsetHeight"); p = getFxProperties(false); - g.transition(p, duration - 150, easing, 0, new Function(){public void f() { + g.transition(p, duration, easing, 0, new Function(){public void f() { onComplete(); }}); } -- 2.39.5