| OLD | NEW |
| (Empty) | |
| 1 /** |
| 2 * `Polymer.AppScrollEffectsBehavior` provides an interface that allows an ele
ment to use scrolls effects. |
| 3 * |
| 4 * ### Importing the app-layout effects |
| 5 * |
| 6 * app-layout provides a set of scroll effects that can be used by explicitly
importing |
| 7 * `app-scroll-effects.html`: |
| 8 * |
| 9 * ```html |
| 10 * <link rel="import" href="/bower_components/app-layout/app-scroll-effects/ap
p-scroll-effects.html"> |
| 11 * ``` |
| 12 * |
| 13 * The scroll effects can also be used by individually importing |
| 14 * `app-layout/app-scroll-effects/effects/[effectName].html`. For example: |
| 15 * |
| 16 * ```html |
| 17 * <link rel="import" href="/bower_components/app-layout/app-scroll-effects/e
ffects/waterfall.html"> |
| 18 * ``` |
| 19 * |
| 20 * ### Consuming effects |
| 21 * |
| 22 * Effects can be consumed via the `effects` property. For example: |
| 23 * |
| 24 * ```html |
| 25 * <app-header effects="waterfall"></app-header> |
| 26 * ``` |
| 27 * |
| 28 * ### Creating scroll effects |
| 29 * |
| 30 * You may want to create a custom scroll effect if you need to modify the CSS
of an element |
| 31 * based on the scroll position. |
| 32 * |
| 33 * A scroll effect definition is an object with `setUp()`, `tearDown()` and `r
un()` functions. |
| 34 * |
| 35 * To register the effect, you can use `Polymer.AppLayout.registerEffect(effec
tName, effectDef)` |
| 36 * For example, let's define an effect that resizes the header's logo: |
| 37 * |
| 38 * ```js |
| 39 * Polymer.AppLayout.registerEffect('resizable-logo', { |
| 40 * setUp: function(config) { |
| 41 * // the effect's config is passed to the setUp. |
| 42 * this._fxResizeLogo = { logo: Polymer.dom(this).querySelector('[logo]')
}; |
| 43 * }, |
| 44 * |
| 45 * run: function(progress) { |
| 46 * // the progress of the effect |
| 47 * this.transform('scale3d(' + progress + ', '+ progress +', 1)', this._
fxResizeLogo.logo); |
| 48 * }, |
| 49 * |
| 50 * tearDown: function() { |
| 51 * // clean up and reset of states |
| 52 * delete this._fxResizeLogo; |
| 53 * } |
| 54 * }); |
| 55 * ``` |
| 56 * Now, you can consume the effect: |
| 57 * |
| 58 * ```html |
| 59 * <app-header id="appHeader" effects="resizable-logo"> |
| 60 * <img logo src="logo.svg"> |
| 61 * </app-header> |
| 62 * ``` |
| 63 * |
| 64 * ### Imperative API |
| 65 * |
| 66 * ```js |
| 67 * var logoEffect = appHeader.createEffect('resizable-logo', effectConfig); |
| 68 * // run the effect: logoEffect.run(progress); |
| 69 * // tear down the effect: logoEffect.tearDown(); |
| 70 * ``` |
| 71 * |
| 72 * ### Configuring effects |
| 73 * |
| 74 * For effects installed via the `effects` property, their configuration can b
e set |
| 75 * via the `effectsConfig` property. For example: |
| 76 * |
| 77 * ```html |
| 78 * <app-header effects="waterfall" |
| 79 * effects-config='{"waterfall": {"startsAt": 0, "endsAt": 0.5}}'> |
| 80 * </app-header> |
| 81 * ``` |
| 82 * |
| 83 * All effects have a `startsAt` and `endsAt` config property. They specify at
what |
| 84 * point the effect should start and end. This value goes from 0 to 1 inclusiv
e. |
| 85 * |
| 86 * @polymerBehavior |
| 87 */ |
| 88 Polymer.AppScrollEffectsBehavior = [ |
| 89 Polymer.IronScrollTargetBehavior, |
| 90 { |
| 91 |
| 92 properties: { |
| 93 |
| 94 /** |
| 95 * A space-separated list of the effects names that will be triggered when
the user scrolls. |
| 96 * e.g. `waterfall parallax-background` installs the `waterfall` and `para
llax-background`. |
| 97 */ |
| 98 effects: { |
| 99 type: String |
| 100 }, |
| 101 |
| 102 /** |
| 103 * An object that configurates the effects installed via the `effects` pro
perty. e.g. |
| 104 * ```js |
| 105 * element.effectsConfig = { |
| 106 * "blend-background": { |
| 107 * "startsAt": 0.5 |
| 108 * } |
| 109 * }; |
| 110 * ``` |
| 111 * Every effect has at least two config properties: `startsAt` and `endsAt
`. |
| 112 * These properties indicate when the event should start and end respectiv
ely |
| 113 * and relative to the overall element progress. So for example, if `blend
-background` |
| 114 * starts at `0.5`, the effect will only start once the current element re
aches 0.5 |
| 115 * of its progress. In this context, the progress is a value in the range
of `[0, 1]` |
| 116 * that indicates where this element is on the screen relative to the view
port. |
| 117 */ |
| 118 effectsConfig: { |
| 119 type: Object, |
| 120 value: function() { |
| 121 return {}; |
| 122 } |
| 123 }, |
| 124 |
| 125 /** |
| 126 * Disables CSS transitions and scroll effects on the element. |
| 127 */ |
| 128 disabled: { |
| 129 type: Boolean, |
| 130 reflectToAttribute: true, |
| 131 value: false |
| 132 } |
| 133 }, |
| 134 |
| 135 observers: [ |
| 136 '_effectsChanged(effects, effectsConfig)' |
| 137 ], |
| 138 |
| 139 /** |
| 140 * Updates the scroll state. This method should be overridden |
| 141 * by the consumer of this behavior. |
| 142 * |
| 143 * @method _updateScrollState |
| 144 */ |
| 145 _updateScrollState: function() {}, |
| 146 |
| 147 /** |
| 148 * Returns true if the current element is on the screen. |
| 149 * That is, visible in the current viewport. This method should be |
| 150 * overridden by the consumer of this behavior. |
| 151 * |
| 152 * @method isOnScreen |
| 153 * @return {boolean} |
| 154 */ |
| 155 isOnScreen: function() { |
| 156 return false; |
| 157 }, |
| 158 |
| 159 /** |
| 160 * Returns true if there's content below the current element. This method |
| 161 * should be overridden by the consumer of this behavior. |
| 162 * |
| 163 * @method isContentBelow |
| 164 * @return {boolean} |
| 165 */ |
| 166 isContentBelow: function() { |
| 167 return false; |
| 168 }, |
| 169 |
| 170 /** |
| 171 * List of effects handlers that will take place during scroll. |
| 172 * |
| 173 * @type {Array<Function>} |
| 174 */ |
| 175 _effectsRunFn: null, |
| 176 |
| 177 /** |
| 178 * List of the effects definitions installed via the `effects` property. |
| 179 * |
| 180 * @type {Array<Object>} |
| 181 */ |
| 182 _effects: null, |
| 183 |
| 184 /** |
| 185 * The clamped value of `_scrollTop`. |
| 186 * @type number |
| 187 */ |
| 188 get _clampedScrollTop() { |
| 189 return Math.max(0, this._scrollTop); |
| 190 }, |
| 191 |
| 192 detached: function() { |
| 193 this._tearDownEffects(); |
| 194 }, |
| 195 |
| 196 /** |
| 197 * Creates an effect object from an effect's name that can be used to run |
| 198 * effects programmatically. |
| 199 * |
| 200 * @method createEffect |
| 201 * @param {string} effectName The effect's name registered via `Polymer.AppL
ayout.registerEffect`. |
| 202 * @param {Object=} effectConfig The effect config object. (Optional) |
| 203 * @return {Object} An effect object with the following functions: |
| 204 * |
| 205 * * `effect.setUp()`, Sets up the requirements for the effect. |
| 206 * This function is called automatically before the `effect` function
returns. |
| 207 * * `effect.run(progress, y)`, Runs the effect given a `progress`. |
| 208 * * `effect.tearDown()`, Cleans up any DOM nodes or element references use
d by the effect. |
| 209 * |
| 210 * Example: |
| 211 * ```js |
| 212 * var parallax = element.createEffect('parallax-background'); |
| 213 * // runs the effect |
| 214 * parallax.run(0.5, 0); |
| 215 * ``` |
| 216 */ |
| 217 createEffect: function(effectName, effectConfig) { |
| 218 var effectDef = Polymer.AppLayout._scrollEffects[effectName]; |
| 219 if (!effectDef) { |
| 220 throw new ReferenceError(this._getUndefinedMsg(effectName)); |
| 221 } |
| 222 var prop = this._boundEffect(effectDef, effectConfig || {}); |
| 223 prop.setUp(); |
| 224 return prop; |
| 225 }, |
| 226 |
| 227 /** |
| 228 * Called when `effects` or `effectsConfig` changes. |
| 229 */ |
| 230 _effectsChanged: function(effects, effectsConfig) { |
| 231 this._tearDownEffects(); |
| 232 |
| 233 if (effects === '') { |
| 234 return; |
| 235 } |
| 236 effects.split(' ').forEach(function(effectName) { |
| 237 var effectDef; |
| 238 if (effectName !== '') { |
| 239 if ((effectDef = Polymer.AppLayout._scrollEffects[effectName])) { |
| 240 this._effects.push(this._boundEffect(effectDef, effectsConfig[effect
Name])); |
| 241 } else { |
| 242 this._warn(this._logf('_effectsChanged', this._getUndefinedMsg(effec
tName))); |
| 243 } |
| 244 } |
| 245 }, this); |
| 246 |
| 247 this._setUpEffect(); |
| 248 }, |
| 249 |
| 250 /** |
| 251 * Forces layout |
| 252 */ |
| 253 _layoutIfDirty: function() { |
| 254 return this.offsetWidth; |
| 255 }, |
| 256 |
| 257 /** |
| 258 * Returns an effect object bound to the current context. |
| 259 * |
| 260 * @param {Object} effectDef |
| 261 * @param {Object=} effectsConfig The effect config object if the effect acc
epts config values. (Optional) |
| 262 */ |
| 263 _boundEffect: function(effectDef, effectsConfig) { |
| 264 effectsConfig = effectsConfig || {}; |
| 265 var startsAt = parseFloat(effectsConfig.startsAt || 0); |
| 266 var endsAt = parseFloat(effectsConfig.endsAt || 1); |
| 267 var deltaS = endsAt - startsAt; |
| 268 var noop = Function(); |
| 269 // fast path if possible |
| 270 var runFn = (startsAt === 0 && endsAt === 1) ? effectDef.run : |
| 271 function(progress, y) { |
| 272 effectDef.run.call(this, |
| 273 Math.max(0, (progress - startsAt) / deltaS), y); |
| 274 }; |
| 275 return { |
| 276 setUp: effectDef.setUp ? effectDef.setUp.bind(this, effectsConfig) : noo
p, |
| 277 run: effectDef.run ? runFn.bind(this) : noop, |
| 278 tearDown: effectDef.tearDown ? effectDef.tearDown.bind(this) : noop |
| 279 }; |
| 280 }, |
| 281 |
| 282 /** |
| 283 * Sets up the effects. |
| 284 */ |
| 285 _setUpEffect: function() { |
| 286 if (this.isAttached && this._effects) { |
| 287 this._effectsRunFn = []; |
| 288 this._effects.forEach(function(effectDef) { |
| 289 // install the effect only if no error was reported |
| 290 if (effectDef.setUp() !== false) { |
| 291 this._effectsRunFn.push(effectDef.run); |
| 292 } |
| 293 }, this); |
| 294 } |
| 295 }, |
| 296 |
| 297 /** |
| 298 * Tears down the effects. |
| 299 */ |
| 300 _tearDownEffects: function() { |
| 301 if (this._effects) { |
| 302 this._effects.forEach(function(effectDef) { |
| 303 effectDef.tearDown(); |
| 304 }); |
| 305 } |
| 306 this._effectsRunFn = []; |
| 307 this._effects = []; |
| 308 }, |
| 309 |
| 310 /** |
| 311 * Runs the effects. |
| 312 * |
| 313 * @param {number} p The progress |
| 314 * @param {number} y The top position of the current element relative to the
viewport. |
| 315 */ |
| 316 _runEffects: function(p, y) { |
| 317 if (this._effectsRunFn) { |
| 318 this._effectsRunFn.forEach(function(run) { |
| 319 run(p, y); |
| 320 }); |
| 321 } |
| 322 }, |
| 323 |
| 324 /** |
| 325 * Overrides the `_scrollHandler`. |
| 326 */ |
| 327 _scrollHandler: function() { |
| 328 if (!this.disabled) { |
| 329 this._updateScrollState(this._clampedScrollTop); |
| 330 } |
| 331 }, |
| 332 |
| 333 _getUndefinedMsg: function(effectName) { |
| 334 return 'Scroll effect `' + effectName + '` is undefined. ' + |
| 335 'Did you forget to import app-layout/app-scroll-effects/effects/' + ef
fectName + '.html ?'; |
| 336 } |
| 337 |
| 338 }]; |
| OLD | NEW |