| 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, isAttached)' | |
| 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, isAttached) { | |
| 231 this._tearDownEffects(); | |
| 232 | |
| 233 if (effects === '' || !isAttached) { | |
| 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 /** | |
| 334 * Override this method to return a reference to a node in the local DOM. | |
| 335 * The node is consumed by a scroll effect. | |
| 336 * | |
| 337 * @param {string} id The id for the node. | |
| 338 */ | |
| 339 _getDOMRef: function(id) { | |
| 340 this._warn(this._logf('_getDOMRef', '`'+ id +'` is undefined')); | |
| 341 }, | |
| 342 | |
| 343 _getUndefinedMsg: function(effectName) { | |
| 344 return 'Scroll effect `' + effectName + '` is undefined. ' + | |
| 345 'Did you forget to import app-layout/app-scroll-effects/effects/' + ef
fectName + '.html ?'; | |
| 346 } | |
| 347 | |
| 348 }]; | |
| OLD | NEW |