core-scroll-header-panel.html revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1<!-- 2Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 3This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 4The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 6Code distributed by Google as part of the polymer project is also 7subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 8--> 9 10<!-- 11`core-scroll-header-panel` contains a header section and a content section. The 12header is initially on the top part of the view but it scrolls away with the 13rest of the scrollable content. Upon scrolling slightly up at any point, the 14header scrolls back into view. This saves screen space and allows users to 15access important controls by easily moving them back to the view. 16 17__Important:__ The `core-scroll-header-panel` will not display if its parent does not have a height. 18 19Using [layout attributes](http://www.polymer-project.org/docs/polymer/layout-attrs.html), you can easily make the `core-scroll-header-panel` fill the screen 20 21 <body fullbleed layout vertical> 22 <core-scroll-header-panel flex> 23 <core-toolbar> 24 <div>Hello World!</div> 25 </core-toolbar> 26 </core-scroll-header-panel> 27 </body> 28 29or, if you would prefer to do it in CSS, just give `html`, `body`, and `core-scroll-header-panel` a height of 100%: 30 31 html, body { 32 height: 100%; 33 margin: 0; 34 } 35 core-scroll-header-panel { 36 height: 100%; 37 } 38 39`core-scroll-header-panel` works well with `core-toolbar` but can use any element 40that represents a header by adding a `core-header` class to it. Use the attribute 41or class `content` to delineate the content section. 42 43 <core-scroll-header-panel> 44 <core-toolbar>Header</core-toolbar> 45 <div content>Content goes here...</div> 46 </core-scroll-header-panel> 47 48@group Polymer Core Elements 49@element core-scroll-header-panel 50@homepage github.io 51--> 52 53<link rel="import" href="/polymer/polymer.html"> 54 55<polymer-element name="core-scroll-header-panel"> 56<template> 57 58 <link rel="stylesheet" href="core-scroll-header-panel.css"> 59 60 <div id="mainContainer" on-scroll="{{scroll}}"> 61 62 <content id="mainContent" select="[content], .content"></content> 63 64 </div> 65 66 <div id="headerContainer"> 67 68 <div class="bg-container"> 69 <div id="condensedHeaderBg"></div> 70 <div id="headerBg"></div> 71 </div> 72 73 <content id="headerContent" select="core-toolbar, .core-header"></content> 74 75 </div> 76 77</template> 78<script> 79 80 Polymer('core-scroll-header-panel', { 81 82 /** 83 * Fired when the content has been scrolled. 84 * 85 * @event scroll 86 */ 87 88 /** 89 * Fired when the header is transformed. 90 * 91 * @event core-header-transform 92 */ 93 94 publish: { 95 /** 96 * If true, the header's height will condense to `_condensedHeaderHeight` 97 * as the user scrolls down from the top of the content area. 98 * 99 * @attribute condenses 100 * @type boolean 101 * @default false 102 */ 103 condenses: false, 104 105 /** 106 * If true, no cross-fade transition from one background to another. 107 * 108 * @attribute noDissolve 109 * @type boolean 110 * @default false 111 */ 112 noDissolve: false, 113 114 /** 115 * If true, the header doesn't slide back in when scrolling back up. 116 * 117 * @attribute noReveal 118 * @type boolean 119 * @default false 120 */ 121 noReveal: false, 122 123 /** 124 * If true, the header is fixed to the top and never moves away. 125 * 126 * @attribute fixed 127 * @type boolean 128 * @default false 129 */ 130 fixed: false, 131 132 /** 133 * If true, the condensed header is always shown and does not move away. 134 * 135 * @attribute keepCondensedHeader 136 * @type boolean 137 * @default false 138 */ 139 keepCondensedHeader: false, 140 141 /** 142 * The height of the header when it is at its full size. 143 * 144 * By default, the height will be measured when it is ready. If the height 145 * changes later the user needs to either set this value to reflect the 146 * new height or invoke `measureHeaderHeight()`. 147 * 148 * @attribute headerHeight 149 * @type number 150 */ 151 headerHeight: 0, 152 153 /** 154 * The height of the header when it is condensed. 155 * 156 * By default, `_condensedHeaderHeight` is 1/3 of `headerHeight` unless 157 * this is specified. 158 * 159 * @attribute condensedHeaderHeight 160 * @type number 161 */ 162 condensedHeaderHeight: 0 163 }, 164 165 prevScrollTop: 0, 166 167 headerMargin: 0, 168 169 y: 0, 170 171 observe: { 172 'headerMargin fixed': 'setup' 173 }, 174 175 domReady: function() { 176 this.async('measureHeaderHeight'); 177 }, 178 179 get header() { 180 return this.$.headerContent.getDistributedNodes()[0]; 181 }, 182 183 get scroller() { 184 return this.$.mainContainer; 185 }, 186 187 measureHeaderHeight: function() { 188 var header = this.header; 189 if (this.header) { 190 this.headerHeight = header.offsetHeight; 191 } 192 }, 193 194 headerHeightChanged: function() { 195 if (!this.condensedHeaderHeight) { 196 // assume _condensedHeaderHeight is 1/3 of the headerHeight 197 this._condensedHeaderHeight = this.headerHeight * 1 / 3; 198 } 199 this.condensedHeaderHeightChanged(); 200 }, 201 202 condensedHeaderHeightChanged: function() { 203 if (this.condensedHeaderHeight) { 204 this._condensedHeaderHeight = this.condensedHeaderHeight; 205 } 206 if (this.headerHeight) { 207 this.headerMargin = this.headerHeight - this._condensedHeaderHeight; 208 } 209 }, 210 211 condensesChanged: function() { 212 if (this.condenses) { 213 this.scroll(); 214 } else { 215 // reset transform/opacity set on the header 216 this.condenseHeader(null); 217 } 218 }, 219 220 setup: function() { 221 var s = this.scroller.style; 222 s.paddingTop = this.fixed ? '' : this.headerHeight + 'px'; 223 s.top = this.fixed ? this.headerHeight + 'px' : ''; 224 if (this.fixed) { 225 this.transformHeader(null); 226 } else { 227 this.scroll(); 228 } 229 }, 230 231 transformHeader: function(y) { 232 var s = this.$.headerContainer.style; 233 this.translateY(s, -y); 234 235 if (this.condenses) { 236 this.condenseHeader(y); 237 } 238 239 this.fire('core-header-transform', {y: y, height: this.headerHeight, 240 condensedHeight: this._condensedHeaderHeight}); 241 }, 242 243 condenseHeader: function(y) { 244 var reset = y == null; 245 // adjust top bar in core-header so the top bar stays at the top 246 if (this.header.$ && this.header.$.topBar) { 247 this.translateY(this.header.$.topBar.style, 248 reset ? null : Math.min(y, this.headerMargin)); 249 } 250 // transition header bg 251 var hbg = this.$.headerBg.style; 252 if (!this.noDissolve) { 253 hbg.opacity = reset ? '' : (this.headerMargin - y) / this.headerMargin; 254 } 255 // adjust header bg so it stays at the center 256 this.translateY(hbg, reset ? null : y / 2); 257 // transition condensed header bg 258 var chbg = this.$.condensedHeaderBg.style; 259 if (!this.noDissolve) { 260 chbg = this.$.condensedHeaderBg.style; 261 chbg.opacity = reset ? '' : y / this.headerMargin; 262 // adjust condensed header bg so it stays at the center 263 this.translateY(chbg, reset ? null : y / 2); 264 } 265 }, 266 267 translateY: function(s, y) { 268 s.transform = s.webkitTransform = y == null ? '' : 269 'translate3d(0, ' + y + 'px, 0)'; 270 }, 271 272 scroll: function(event) { 273 if (!this.header) { 274 return; 275 } 276 277 var sTop = this.scroller.scrollTop; 278 279 var y = Math.min(this.keepCondensedHeader ? 280 this.headerMargin : this.headerHeight, Math.max(0, 281 (this.noReveal ? sTop : this.y + sTop - this.prevScrollTop))); 282 283 if (this.condenses && this.prevScrollTop >= sTop && sTop > this.headerMargin) { 284 y = Math.max(y, this.headerMargin); 285 } 286 287 if (!event || !this.fixed && y !== this.y) { 288 requestAnimationFrame(this.transformHeader.bind(this, y)); 289 } 290 291 this.prevScrollTop = sTop; 292 this.y = y; 293 294 if (event) { 295 this.fire('scroll', {target: this.scroller}, this, false); 296 } 297 } 298 299 }); 300 301</script> 302</polymer-element> 303