PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/bower_components/paper-scroll-header-panel/paper-scroll-header-panel.html

https://gitlab.com/jofinckh/htwg-thumbnail
HTML | 579 lines | 421 code | 79 blank | 79 comment | 0 complexity | d01ab2bbfed96102cee20b1e596324a2 MD5 | raw file
  1. <!--
  2. @license
  3. Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  4. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  5. The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  6. The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  7. Code distributed by Google as part of the polymer project is also
  8. subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  9. -->
  10. <link rel="import" href="../polymer/polymer.html">
  11. <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
  12. <!--
  13. Material design: [Scrolling techniques](https://www.google.com/design/spec/patterns/scrolling-techniques.html)
  14. `paper-scroll-header-panel` contains a header section and a content section. The
  15. header is initially on the top part of the view but it scrolls away with the
  16. rest of the scrollable content. Upon scrolling slightly up at any point, the
  17. header scrolls back into view. This saves screen space and allows users to
  18. access important controls by easily moving them back to the view.
  19. __Important:__ The `paper-scroll-header-panel` will not display if its parent does not have a height.
  20. Using [layout classes](https://www.polymer-project.org/1.0/docs/migration.html#layout-attributes) or custom properties, you can easily make the `paper-scroll-header-panel` fill the screen
  21. ```html
  22. <body class="fullbleed layout vertical">
  23. <paper-scroll-header-panel class="flex">
  24. <paper-toolbar>
  25. <div>Hello World!</div>
  26. </paper-toolbar>
  27. </paper-scroll-header-panel>
  28. </body>
  29. ```
  30. or, if you would prefer to do it in CSS, just give `html`, `body`, and `paper-scroll-header-panel` a height of 100%:
  31. ```css
  32. html, body {
  33. height: 100%;
  34. margin: 0;
  35. }
  36. paper-scroll-header-panel {
  37. height: 100%;
  38. }
  39. ```
  40. `paper-scroll-header-panel` works well with `paper-toolbar` but can use any element
  41. that represents a header by adding a `paper-header` class to it.
  42. ```html
  43. <paper-scroll-header-panel>
  44. <div class="paper-header">Header</div>
  45. <div>Content goes here...</div>
  46. </paper-scroll-header-panel>
  47. ```
  48. ### Styling
  49. =======
  50. The following custom properties and mixins are available for styling:
  51. Custom property | Description | Default
  52. ----------------|-------------|----------
  53. --paper-scroll-header-panel-full-header | To change background for toolbar when it is at its full size | {}
  54. --paper-scroll-header-panel-condensed-header | To change the background for toolbar when it is condensed | {}
  55. --paper-scroll-header-container | To override or add container styles | {}
  56. @group Paper Element
  57. @element paper-scroll-header-panel
  58. @demo demo/transform-header-1.html Transform header 1
  59. @demo demo/transform-header-2.html Transform header 2
  60. @demo demo/transform-header-3.html Transform header 3
  61. @demo demo/transform-header-4.html Transform header 4
  62. @demo demo/transform-header-5.html Transform header 5
  63. @demo demo/transform-header-6.html Transform header 6
  64. @demo demo/keep-header.html Keep header
  65. @demo demo/hide-header.html Hide header
  66. @demo demo/toggle-fixed-header.html Toggle fixed header
  67. @demo demo/drawer-panel.html Combine with paper-drawer-panel
  68. @hero hero.svg
  69. -->
  70. <dom-module id="paper-scroll-header-panel">
  71. <style>
  72. :host {
  73. display: block;
  74. position: relative;
  75. overflow: hidden;
  76. }
  77. #mainContainer {
  78. position: absolute;
  79. top: 0;
  80. right: 0;
  81. bottom: 0;
  82. left: 0;
  83. -moz-box-sizing: border-box;
  84. box-sizing: border-box;
  85. -webkit-overflow-scrolling: touch;
  86. overflow-x: hidden;
  87. overflow-y: auto;
  88. @apply(--paper-scroll-header-container);
  89. }
  90. #headerContainer {
  91. position: absolute;
  92. top: 0;
  93. right: 0;
  94. left: 0;
  95. }
  96. .bg-container {
  97. position: absolute;
  98. top: 0;
  99. left: 0;
  100. width: 100%;
  101. height: 100%;
  102. overflow: hidden;
  103. }
  104. #headerBg {
  105. @apply(--paper-scroll-header-panel-full-header);
  106. }
  107. #condensedHeaderBg {
  108. @apply(--paper-scroll-header-panel-condensed-header);
  109. }
  110. #headerBg, #condensedHeaderBg {
  111. position: absolute;
  112. top: 0;
  113. left: 0;
  114. width: 100%;
  115. height: 100%;
  116. background-repeat: no-repeat;
  117. background-size: cover;
  118. background-position: center center;
  119. }
  120. #condensedHeaderBg {
  121. opacity: 0;
  122. }
  123. </style>
  124. <template>
  125. <div id="mainContainer">
  126. <content id="mainContent" select=":not(paper-toolbar):not(.paper-header)"></content>
  127. </div>
  128. <div id="headerContainer">
  129. <div class="bg-container">
  130. <div id="condensedHeaderBg"></div>
  131. <div id="headerBg"></div>
  132. </div>
  133. <content id="headerContent" select="paper-toolbar, .paper-header"></content>
  134. </div>
  135. </template>
  136. </dom-module>
  137. <script>
  138. (function() {
  139. 'use strict';
  140. Polymer.PaperScrollHeaderPanel = Polymer({
  141. /**
  142. * Fired when the content has been scrolled.
  143. *
  144. * @event content-scroll
  145. */
  146. /**
  147. * Fired when the header is transformed.
  148. *
  149. * @event paper-header-transform
  150. */
  151. is: 'paper-scroll-header-panel',
  152. behaviors: [
  153. Polymer.IronResizableBehavior
  154. ],
  155. properties: {
  156. /**
  157. * If true, the header's height will condense to `condensedHeaderHeight`
  158. * as the user scrolls down from the top of the content area.
  159. */
  160. condenses: {
  161. type: Boolean,
  162. value: false
  163. },
  164. /**
  165. * If true, no cross-fade transition from one background to another.
  166. */
  167. noDissolve: {
  168. type: Boolean,
  169. value: false
  170. },
  171. /**
  172. * If true, the header doesn't slide back in when scrolling back up.
  173. */
  174. noReveal: {
  175. type: Boolean,
  176. value: false
  177. },
  178. /**
  179. * If true, the header is fixed to the top and never moves away.
  180. */
  181. fixed: {
  182. type: Boolean,
  183. value: false
  184. },
  185. /**
  186. * If true, the condensed header is always shown and does not move away.
  187. */
  188. keepCondensedHeader: {
  189. type: Boolean,
  190. value: false
  191. },
  192. /**
  193. * The height of the header when it is at its full size.
  194. *
  195. * By default, the height will be measured when it is ready. If the height
  196. * changes later the user needs to either set this value to reflect the
  197. * new height or invoke `measureHeaderHeight()`.
  198. */
  199. headerHeight: {
  200. type: Number,
  201. value: 0
  202. },
  203. /**
  204. * The height of the header when it is condensed.
  205. *
  206. * By default, `condensedHeaderHeight` is 1/3 of `headerHeight` unless
  207. * this is specified.
  208. */
  209. condensedHeaderHeight: {
  210. type: Number,
  211. value: 0
  212. },
  213. /**
  214. * By default, the top part of the header stays when the header is being
  215. * condensed. Set this to true if you want the top part of the header
  216. * to be scrolled away.
  217. */
  218. scrollAwayTopbar: {
  219. type: Boolean,
  220. value: false
  221. },
  222. /**
  223. * The state of the header. Depending on the configuration and the `scrollTop` value,
  224. * the header state could change to
  225. * Polymer.PaperScrollHeaderPanel.HEADER_STATE_EXPANDED
  226. * Polymer.PaperScrollHeaderPanel.HEADER_STATE_HIDDEN
  227. * Polymer.PaperScrollHeaderPanel.HEADER_STATE_CONDENSED
  228. * Polymer.PaperScrollHeaderPanel.HEADER_STATE_INTERPOLATED
  229. */
  230. headerState: {
  231. type: Number,
  232. readOnly: true,
  233. notify:true,
  234. value: 0
  235. },
  236. /** @type {number|null} */
  237. _defaultCondsensedHeaderHeight: {
  238. type: Number,
  239. value: 0
  240. }
  241. },
  242. observers: [
  243. '_setup(headerHeight, condensedHeaderHeight, fixed)',
  244. '_condensedHeaderHeightChanged(condensedHeaderHeight)',
  245. '_headerHeightChanged(headerHeight, condensedHeaderHeight)',
  246. '_condensesChanged(condenses)',
  247. ],
  248. listeners: {
  249. 'iron-resize': 'measureHeaderHeight'
  250. },
  251. ready: function() {
  252. this._scrollHandler = this._scroll.bind(this);
  253. this.scroller.addEventListener('scroll', this._scrollHandler);
  254. },
  255. attached: function() {
  256. this.async(this.measureHeaderHeight, 1);
  257. },
  258. /**
  259. * Returns the header element.
  260. *
  261. * @property header
  262. * @type Object
  263. */
  264. get header() {
  265. return Polymer.dom(this.$.headerContent).getDistributedNodes()[0];
  266. },
  267. /**
  268. * Returns the content element.
  269. *
  270. * @property content
  271. * @type Object
  272. */
  273. get content() {
  274. return Polymer.dom(this.$.mainContent).getDistributedNodes()[0];
  275. },
  276. /**
  277. * Returns the scrollable element.
  278. *
  279. * @property scroller
  280. * @type Object
  281. */
  282. get scroller() {
  283. return this.$.mainContainer;
  284. },
  285. get _headerMaxDelta() {
  286. return this.keepCondensedHeader ? this._headerMargin : this.headerHeight;
  287. },
  288. get _headerMargin() {
  289. return this.headerHeight - this.condensedHeaderHeight;
  290. },
  291. _y: 0,
  292. _prevScrollTop: 0,
  293. /**
  294. * Invoke this to tell `paper-scroll-header-panel` to re-measure the header's
  295. * height.
  296. *
  297. * @method measureHeaderHeight
  298. */
  299. measureHeaderHeight: function() {
  300. var header = this.header;
  301. if (header && header.offsetHeight) {
  302. this.headerHeight = header.offsetHeight;
  303. }
  304. },
  305. /**
  306. * Scroll to a specific y coordinate.
  307. *
  308. * @method scroll
  309. * @param {number} top The coordinate to scroll to, along the y-axis.
  310. * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
  311. */
  312. scroll: function(top, smooth) {
  313. // the scroll event will trigger _updateScrollState directly,
  314. // However, _updateScrollState relies on the previous `scrollTop` to update the states.
  315. // Calling _updateScrollState will ensure that the states are synced correctly.
  316. if (smooth) {
  317. // TODO(blasten): use CSS scroll-behavior once it ships in Chrome.
  318. var easingFn = function easeOutQuad(t, b, c, d) {
  319. t /= d;
  320. return -c * t*(t-2) + b;
  321. };
  322. var animationId = Math.random();
  323. var duration = 200;
  324. var startTime = Date.now();
  325. var currentScrollTop = this.scroller.scrollTop;
  326. var deltaScrollTop = top - currentScrollTop;
  327. this._currentAnimationId = animationId;
  328. (function updateFrame() {
  329. var now = Date.now();
  330. var elapsedTime = now - startTime;
  331. if (elapsedTime > duration) {
  332. this.scroller.scrollTop = top;
  333. this._updateScrollState(top);
  334. } else if (this._currentAnimationId === animationId) {
  335. this.scroller.scrollTop = easingFn(elapsedTime, currentScrollTop, deltaScrollTop, duration);
  336. requestAnimationFrame(updateFrame.bind(this));
  337. }
  338. }).call(this);
  339. } else {
  340. this.scroller.scrollTop = top;
  341. this._updateScrollState(top);
  342. }
  343. },
  344. /**
  345. * Condense the header.
  346. *
  347. * @method condense
  348. * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
  349. */
  350. condense: function(smooth) {
  351. if (this.condenses && !this.fixed && !this.noReveal) {
  352. switch (this.headerState) {
  353. case 1:
  354. this.scroll(this.scroller.scrollTop - (this._headerMaxDelta - this._headerMargin), smooth);
  355. break;
  356. case 0:
  357. case 3:
  358. this.scroll(this._headerMargin, smooth);
  359. break;
  360. }
  361. }
  362. },
  363. /**
  364. * Scroll to the top of the content.
  365. *
  366. * @method scrollToTop
  367. * @param {boolean} smooth true if the scroll position should be smoothly adjusted.
  368. */
  369. scrollToTop: function(smooth) {
  370. this.scroll(0, smooth);
  371. },
  372. _headerHeightChanged: function(headerHeight) {
  373. if (this._defaultCondsensedHeaderHeight !== null) {
  374. this._defaultCondsensedHeaderHeight = Math.round(headerHeight * 1/3);
  375. this.condensedHeaderHeight = this._defaultCondsensedHeaderHeight;
  376. }
  377. },
  378. _condensedHeaderHeightChanged: function(condensedHeaderHeight) {
  379. if (condensedHeaderHeight) {
  380. // a user custom value
  381. if (this._defaultCondsensedHeaderHeight != condensedHeaderHeight) {
  382. // disable the default value
  383. this._defaultCondsensedHeaderHeight = null;
  384. }
  385. }
  386. },
  387. _condensesChanged: function() {
  388. this._updateScrollState(this.scroller.scrollTop);
  389. this._condenseHeader(null);
  390. },
  391. _setup: function() {
  392. var s = this.scroller.style;
  393. s.paddingTop = this.fixed ? '' : this.headerHeight + 'px';
  394. s.top = this.fixed ? this.headerHeight + 'px' : '';
  395. if (this.fixed) {
  396. this._setHeaderState(0);
  397. this._transformHeader(null);
  398. } else {
  399. switch (this.headerState) {
  400. case 1:
  401. this._transformHeader(this._headerMaxDelta);
  402. break;
  403. case 2:
  404. this._transformHeader(this._headerMargin);
  405. break;
  406. }
  407. }
  408. },
  409. _transformHeader: function(y) {
  410. this._translateY(this.$.headerContainer, -y);
  411. if (this.condenses) {
  412. this._condenseHeader(y);
  413. }
  414. this.fire('paper-header-transform',
  415. { y: y,
  416. height: this.headerHeight,
  417. condensedHeight: this.condensedHeaderHeight
  418. }
  419. );
  420. },
  421. _condenseHeader: function(y) {
  422. var reset = (y === null);
  423. // adjust top bar in paper-header so the top bar stays at the top
  424. if (!this.scrollAwayTopbar && this.header && this.header.$ && this.header.$.topBar) {
  425. this._translateY(this.header.$.topBar,
  426. reset ? null : Math.min(y, this._headerMargin));
  427. }
  428. // transition header bg
  429. if (!this.noDissolve) {
  430. this.$.headerBg.style.opacity = reset ? '' :
  431. ( (this._headerMargin - y) / this._headerMargin);
  432. }
  433. // adjust header bg so it stays at the center
  434. this._translateY(this.$.headerBg, reset ? null : y / 2);
  435. // transition condensed header bg
  436. if (!this.noDissolve) {
  437. this.$.condensedHeaderBg.style.opacity = reset ? '' :
  438. (y / this._headerMargin);
  439. // adjust condensed header bg so it stays at the center
  440. this._translateY(this.$.condensedHeaderBg, reset ? null : y / 2);
  441. }
  442. },
  443. _translateY: function(node, y) {
  444. this.transform((y === null) ? '' : 'translate3d(0, ' + y + 'px, 0)', node);
  445. },
  446. /** @param {Event=} event */
  447. _scroll: function(event) {
  448. if (this.header) {
  449. this._updateScrollState(this.scroller.scrollTop);
  450. this.fire('content-scroll', {
  451. target: this.scroller
  452. },
  453. {
  454. cancelable: false
  455. });
  456. }
  457. },
  458. _updateScrollState: function(scrollTop) {
  459. var deltaScrollTop = scrollTop - this._prevScrollTop;
  460. var y = Math.max(0, (this.noReveal) ? scrollTop : this._y + deltaScrollTop);
  461. if (y > this._headerMaxDelta) {
  462. y = this._headerMaxDelta;
  463. if (this.keepCondensedHeader) {
  464. this._setHeaderState(2);
  465. } else {
  466. this._setHeaderState(1);
  467. }
  468. } else if (this.condenses && scrollTop >= this._headerMargin) {
  469. y = Math.max(y, this._headerMargin);
  470. this._setHeaderState(2);
  471. } else if (y === 0) {
  472. this._setHeaderState(0);
  473. } else {
  474. this._setHeaderState(3);
  475. }
  476. if (!this.fixed && y !== this._y) {
  477. this._transformHeader(y);
  478. }
  479. this._prevScrollTop = Math.max(scrollTop, 0);
  480. this._y = y;
  481. }
  482. });
  483. Polymer.PaperScrollHeaderPanel.HEADER_STATE_EXPANDED = 0;
  484. Polymer.PaperScrollHeaderPanel.HEADER_STATE_HIDDEN = 1;
  485. Polymer.PaperScrollHeaderPanel.HEADER_STATE_CONDENSED = 2;
  486. Polymer.PaperScrollHeaderPanel.HEADER_STATE_INTERPOLATED = 3;
  487. })();
  488. </script>