001package 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 023import com.google.gwt.core.client.Scheduler; 024import com.google.gwt.dom.client.Element; 025import com.google.gwt.dom.client.StyleInjector; 026import com.google.gwt.user.client.ui.UIObject; 027import org.gwtbootstrap3.extras.animate.client.ui.constants.Animation; 028 029import java.util.ArrayList; 030 031/** 032 * Utility class to dynamically animate objects using CSS animations. 033 * 034 * @author Pavel Zlámal 035 */ 036public 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}