001 package org.gwtbootstrap3.extras.animate.client.ui;
002
003 /*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 - 2014 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023 import com.google.gwt.core.client.Scheduler;
024 import com.google.gwt.dom.client.Element;
025 import com.google.gwt.dom.client.StyleInjector;
026 import com.google.gwt.user.client.ui.UIObject;
027 import org.gwtbootstrap3.extras.animate.client.ui.constants.Animation;
028
029 import java.util.ArrayList;
030
031 /**
032 * Utility class to dynamically animate objects using CSS animations.
033 *
034 * @author Pavel Zlámal
035 */
036 public class Animate {
037
038 // store used styles, so they are not injected to the DOM everytime.
039 private static final ArrayList<String> usedStyles = new ArrayList<String>();
040
041 /**
042 * Animate any element with specific animation. Animation is done by CSS and runs only once.
043 *
044 * Animation is started when element is appended to the DOM or new (not same) animation is added
045 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
046 * when element is set as hidden.
047 *
048 * @param widget Widget to apply animation to.
049 * @param animation Type of animation to apply.
050 * @param <T> Any object extending UIObject class (typically Widget).
051 * @return Animation's CSS class name, which can be removed to stop animation.
052 */
053 public static <T extends UIObject> String animate(final T widget, final Animation animation) {
054 return animate(widget, animation, 1, -1, -1);
055 }
056
057 /**
058 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
059 *
060 * Animation is started when element is appended to the DOM or new (not same) animation is added
061 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
062 * when element is set as hidden.
063 *
064 * @param widget Widget to apply animation to.
065 * @param animation Type of animation to apply.
066 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
067 * @param <T> Any object extending UIObject class (typically Widget).
068 * @return Animation's CSS class name, which can be removed to stop animation.
069 */
070 public static <T extends UIObject> String animate(final T widget, final Animation animation, final int count) {
071 return animate(widget, animation, count, -1, -1);
072 }
073
074 /**
075 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
076 *
077 * Animation is started when element is appended to the DOM or new (not same) animation is added
078 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
079 * when element is set as hidden.
080 *
081 * @param widget Widget to apply animation to.
082 * @param animation Type of animation to apply.
083 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
084 * @param duration Animation duration in ms. 0 disables animation, any negative value keeps default of original animation.
085 * @param <T> Any object extending UIObject class (typically Widget).
086 * @return Animation's CSS class name, which can be removed to stop animation.
087 */
088 public static <T extends UIObject> String animate(final T widget, final Animation animation, final int count, final int duration) {
089 return animate(widget, animation, count, duration, -1);
090 }
091
092 /**
093 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
094 *
095 * Animation is started when element is appended to the DOM or new (not same) animation is added
096 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
097 * when element is set as hidden.
098 *
099 * @param widget Widget to apply animation to.
100 * @param animation Type of animation to apply.
101 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
102 * @param duration Animation duration in ms. 0 disables animation, any negative value keeps default of original animation.
103 * @param delay Delay before starting the animation loop in ms. Value <= 0 means no delay.
104 * @param <T> Any object extending UIObject class (typically Widget).
105 * @return Animation's CSS class name, which can be removed to stop animation.
106 */
107 public static <T extends UIObject> String animate(final T widget, final Animation animation, final int count, final int duration, final int delay) {
108
109 if (widget != null && animation != null) {
110 // on valid input
111 if (widget.getStyleName().contains(animation.getCssName())) {
112 // animation is present, remove it and run again.
113 stopAnimation(widget, animation.getCssName() + " " + getStyleNameFromAnimation(animation.getCssName(),count,duration,delay));
114 Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
115 @Override
116 public boolean execute() {
117 styleElement(widget.getElement(), animation.getCssName(), count, duration, delay);
118 return false;
119 }
120 }, 200);
121 return animation.getCssName() + " " + getStyleNameFromAnimation(animation.getCssName(),count,duration,delay);
122 } else {
123 // animation was not present, run immediately
124 return styleElement(widget.getElement(), animation.getCssName(), count, duration, delay);
125 }
126 } else {
127 return null;
128 }
129
130 }
131
132 /**
133 * Animate any element with specific animation. Animation is done by CSS and runs only once.
134 *
135 * Animation is started when element is appended to the DOM or new (not same) animation is added
136 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
137 * when element is set as hidden.
138 *
139 * @param widget Widget to apply animation to.
140 * @param animation Custom CSS class name used as animation.
141 * @param <T> Any object extending UIObject class (typically Widget).
142 * @return Animation's CSS class name, which can be removed to stop animation.
143 */
144 public static <T extends UIObject> String animate(final T widget, final String animation) {
145 return animate(widget, animation, 1, -1, -1);
146 }
147
148 /**
149 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
150 *
151 * Animation is started when element is appended to the DOM or new (not same) animation is added
152 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
153 * when element is set as hidden.
154 *
155 * @param widget Widget to apply animation to.
156 * @param animation Custom CSS class name used as animation.
157 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
158 * @param <T> Any object extending UIObject class (typically Widget).
159 * @return Animation's CSS class name, which can be removed to stop animation.
160 */
161 public static <T extends UIObject> String animate(final T widget, final String animation, final int count) {
162 return animate(widget, animation, count, -1, -1);
163 }
164
165 /**
166 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
167 *
168 * Animation is started when element is appended to the DOM or new (not same) animation is added
169 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
170 * when element is set as hidden.
171 *
172 * @param widget Widget to apply animation to.
173 * @param animation Custom CSS class name used as animation.
174 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
175 * @param duration Animation duration in ms. 0 disables animation, any negative value keeps default of original animation.
176 * @param <T> Any object extending UIObject class (typically Widget).
177 * @return Animation's CSS class name, which can be removed to stop animation.
178 */
179 public static <T extends UIObject> String animate(final T widget, final String animation, final int count, final int duration) {
180 return animate(widget, animation, count, duration, -1);
181 }
182
183 /**
184 * Animate any element with specific animation. Animation is done by CSS and runs multiple times.
185 *
186 * Animation is started when element is appended to the DOM or new (not same) animation is added
187 * to already displayed element. Animation runs on hidden elements too and is not paused/stopped
188 * when element is set as hidden.
189 *
190 * @param widget Widget to apply animation to.
191 * @param animation Custom CSS class name used as animation.
192 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
193 * @param duration Animation duration in ms. 0 disables animation, any negative value keeps default of original animation.
194 * @param delay Delay before starting the animation loop in ms. Value <= 0 means no delay.
195 * @param <T> Any object extending UIObject class (typically Widget).
196 * @return Animation's CSS class name, which can be removed to stop animation.
197 */
198 public static <T extends UIObject> String animate(final T widget, final String animation, final int count, final int duration, final int delay) {
199
200 if (widget != null && animation != null) {
201 // on valid input
202 if (widget.getStyleName().contains(animation)) {
203 // animation is present, remove it and run again.
204 stopAnimation(widget, animation);
205 Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
206 @Override
207 public boolean execute() {
208 styleElement(widget.getElement(), animation, count, duration, delay);
209 return false;
210 }
211 }, 200);
212 return animation + " " + getStyleNameFromAnimation(animation,count,duration,delay);
213 } else {
214 // animation was not present, run immediately
215 return styleElement(widget.getElement(), animation, count, duration, delay);
216 }
217 } else {
218 return null;
219 }
220
221 }
222
223 /**
224 * Styles element with animation class. New class name is generated to customize count, duration and delay.
225 * Style is removed on animation end (if not set to infinite).
226 *
227 * @param element Element to apply animation to.
228 * @param animation Type of animation to apply.
229 * @param count Number of animation repeats. 0 disables animation, any negative value set repeats to infinite.
230 * @param duration Animation duration in ms. 0 disables animation, any negative value keeps default of original animation.
231 * @param delay Delay before starting the animation loop in ms. Value <= 0 means no delay.
232 * @param <T> Any object extending UIObject class (typically Widget).
233 * @return Animation's CSS class name, which can be removed to stop animation.
234 */
235 private static <T extends UIObject> String styleElement(Element element, String animation, int count, int duration, int delay) {
236
237 if (!usedStyles.contains(animation + " " + getStyleNameFromAnimation(animation,count,duration,delay))) {
238
239 String styleSheet = "." + getStyleNameFromAnimation(animation, count, duration, delay) + " {";
240
241 // 1 is default, 0 disable animation, any negative -> infinite loop
242 if (count >= 0) {
243
244 styleSheet += "-webkit-animation-iteration-count: " + count + ";" +
245 "-moz-animation-iteration-count:" + count + ";" +
246 "-ms-animation-iteration-count:" + count + ";" +
247 "-o-animation-iteration-count:" + count + ";" +
248 "animation-iteration-count:" + count + ";";
249
250 } else {
251
252 styleSheet += "-webkit-animation-iteration-count: infinite;" +
253 "-moz-animation-iteration-count: infinite;" +
254 "-ms-animation-iteration-count: infinite;" +
255 "-o-animation-iteration-count: infinite;" +
256 "animation-iteration-count: infinite;";
257
258 }
259
260 // if not default (any negative -> use default)
261 if (duration >= 0) {
262
263 styleSheet += "-webkit-animation-duration: " + duration + "ms;" +
264 "-moz-animation-duration:" + duration + "ms;" +
265 "-ms-animation-duration:" + duration + "ms;" +
266 "-o-animation-duration:" + duration + "ms;" +
267 "animation-duration:" + duration + "ms;";
268
269 }
270
271 // if not default (any negative -> use default)
272 if (delay >= 0) {
273
274 styleSheet += "-webkit-animation-delay: " + delay + "ms;" +
275 "-moz-animation-delay:" + delay + "ms;" +
276 "-ms-animation-delay:" + delay + "ms;" +
277 "-o-animation-delay:" + delay + "ms;" +
278 "animation-delay:" + delay + "ms;";
279
280 }
281
282 styleSheet += "}";
283
284 // inject new style
285 StyleInjector.injectAtEnd(styleSheet, true);
286
287 usedStyles.add(animation + " " + getStyleNameFromAnimation(animation, count, duration, delay));
288
289 }
290
291 // start animation
292 element.addClassName(animation + " " + getStyleNameFromAnimation(animation,count,duration,delay));
293
294 // remove animation on end so we could start it again
295 // removeAnimationOnEnd(element, animation + " anim-"+count+"-"+duration+"-"+delay);
296
297 return animation + " " + getStyleNameFromAnimation(animation,count,duration,delay);
298
299 }
300
301 /**
302 * Removes custom animation class on animation end.
303 *
304 * @param widget Element to remove style from.
305 * @param animation Animation CSS class to remove.
306 */
307 public static final <T extends UIObject> void removeAnimationOnEnd(final T widget, final String animation) {
308 if (widget != null && animation != null) {
309 removeAnimationOnEnd(widget.getElement(), animation);
310 }
311 }
312
313 /**
314 * Removes custom animation class on animation end.
315 *
316 * @param element Element to remove style from.
317 * @param animation Animation CSS class to remove.
318 */
319 private static final native void removeAnimationOnEnd(Element element, String animation) /*-{
320
321 var elem = $wnd.jQuery(element);
322 elem.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', { elem: elem }, function(event) {
323 event.data.elem.removeClass(animation);
324 });
325
326 }-*/;
327
328 /**
329 * Removes custom animation class and stops animation.
330 *
331 * @param widget Element to remove style from.
332 * @param animation Animation CSS class to remove.
333 */
334 public static final <T extends UIObject> void stopAnimation(final T widget, final String animation){
335 if (widget != null && animation != null) {
336 stopAnimation(widget.getElement(), animation);
337 }
338 }
339
340 /**
341 * Removes custom animation class and stops animation.
342 *
343 * @param element Element to remove style from.
344 * @param animation Animation CSS class to remove.
345 */
346 private static final native void stopAnimation(Element element, String animation) /*-{
347 $wnd.jQuery(element).removeClass(animation);
348 }-*/;
349
350 /**
351 * Helper method, which returns unique class name for combination of animation and it's settings.
352 *
353 * @param animation Animation CSS class name.
354 * @param count Number of animation repeats.
355 * @param duration Animation duration in ms.
356 * @param delay Delay before starting the animation loop in ms.
357 * @return String representation of class name like "animation-count-duration-delay".
358 */
359 private static String getStyleNameFromAnimation(final String animation, int count, int duration, int delay) {
360
361 // fix input
362 if (count < 0) count = -1;
363 if (duration < 0) duration = -1;
364 if (delay < 0) delay = -1;
365
366 String styleName = "";
367
368 // for all valid animations
369 if (animation != null && !animation.isEmpty() && animation.split(" ").length > 1) {
370
371 styleName += animation.split(" ")[1]+"-"+count+"-"+duration+"-"+delay;
372
373 // for all custom animations
374 } else if (animation != null && !animation.isEmpty() && animation.split(" ").length == 1) {
375
376 styleName += animation+"-"+count+"-"+duration+"-"+delay;
377
378 }
379
380 return styleName;
381
382 }
383
384 }