├── .editorconfig
├── .gitignore
├── README.md
├── config.xml
├── ionic.config.json
├── package.json
├── readme
├── shrink-0.5.gif
├── shrink-0.5.mov
├── shrink-1.gif
├── shrink-1.mov
├── static.gif
├── static.mov
├── translate.gif
└── translate.mov
├── resources
├── android
│ ├── icon
│ │ ├── drawable-hdpi-icon.png
│ │ ├── drawable-ldpi-icon.png
│ │ ├── drawable-mdpi-icon.png
│ │ ├── drawable-xhdpi-icon.png
│ │ ├── drawable-xxhdpi-icon.png
│ │ └── drawable-xxxhdpi-icon.png
│ └── splash
│ │ ├── drawable-land-hdpi-screen.png
│ │ ├── drawable-land-ldpi-screen.png
│ │ ├── drawable-land-mdpi-screen.png
│ │ ├── drawable-land-xhdpi-screen.png
│ │ ├── drawable-land-xxhdpi-screen.png
│ │ ├── drawable-land-xxxhdpi-screen.png
│ │ ├── drawable-port-hdpi-screen.png
│ │ ├── drawable-port-ldpi-screen.png
│ │ ├── drawable-port-mdpi-screen.png
│ │ ├── drawable-port-xhdpi-screen.png
│ │ ├── drawable-port-xxhdpi-screen.png
│ │ └── drawable-port-xxxhdpi-screen.png
├── icon.png
├── ios
│ ├── icon
│ │ ├── icon-40.png
│ │ ├── icon-40@2x.png
│ │ ├── icon-40@3x.png
│ │ ├── icon-50.png
│ │ ├── icon-50@2x.png
│ │ ├── icon-60.png
│ │ ├── icon-60@2x.png
│ │ ├── icon-60@3x.png
│ │ ├── icon-72.png
│ │ ├── icon-72@2x.png
│ │ ├── icon-76.png
│ │ ├── icon-76@2x.png
│ │ ├── icon-83.5@2x.png
│ │ ├── icon-small.png
│ │ ├── icon-small@2x.png
│ │ ├── icon-small@3x.png
│ │ ├── icon.png
│ │ └── icon@2x.png
│ └── splash
│ │ ├── Default-568h@2x~iphone.png
│ │ ├── Default-667h.png
│ │ ├── Default-736h.png
│ │ ├── Default-Landscape-736h.png
│ │ ├── Default-Landscape@2x~ipad.png
│ │ ├── Default-Landscape@~ipadpro.png
│ │ ├── Default-Landscape~ipad.png
│ │ ├── Default-Portrait@2x~ipad.png
│ │ ├── Default-Portrait@~ipadpro.png
│ │ ├── Default-Portrait~ipad.png
│ │ ├── Default@2x~iphone.png
│ │ └── Default~iphone.png
└── splash.png
├── src
├── app
│ ├── app.component.ts
│ ├── app.html
│ ├── app.module.ts
│ ├── app.scss
│ └── main.ts
├── assets
│ └── icon
│ │ └── favicon.ico
├── components
│ └── scroll-hide
│ │ ├── dev
│ │ ├── _scroll-hide.ts
│ │ ├── scroll-hide-v1.ts
│ │ ├── scroll-hide-v2.0.ts
│ │ ├── scroll-hide-v2.1.ts
│ │ ├── scroll-hide-v2.2.ts
│ │ ├── scroll-hide-v3.0.ts
│ │ └── scroll-hide-v3.1.ts
│ │ └── scroll-hide.ts
├── index.html
├── manifest.json
├── pages
│ ├── about
│ │ ├── about.html
│ │ ├── about.scss
│ │ └── about.ts
│ ├── contact
│ │ ├── contact.html
│ │ ├── contact.scss
│ │ └── contact.ts
│ ├── home
│ │ ├── home.html
│ │ ├── home.scss
│ │ └── home.ts
│ └── tabs
│ │ ├── tabs.html
│ │ └── tabs.ts
├── service-worker.js
└── theme
│ └── variables.scss
├── tsconfig.json
├── tslint.json
├── typings.json
└── typings
├── globals
└── jquery
│ ├── index.d.ts
│ └── typings.json
└── index.d.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | indent_style = space
8 | indent_size = 2
9 |
10 | # We recommend you to keep these unchanged
11 | end_of_line = lf
12 | charset = utf-8
13 | trim_trailing_whitespace = true
14 | insert_final_newline = true
15 |
16 | [*.md]
17 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 |
4 | *~
5 | *.sw[mnpcod]
6 | *.log
7 | *.tmp
8 | *.tmp.*
9 | log.txt
10 | *.sublime-project
11 | *.sublime-workspace
12 | .vscode/
13 | npm-debug.log*
14 |
15 | .idea/
16 | .sass-cache/
17 | .tmp/
18 | .versions/
19 | coverage/
20 | dist/
21 | node_modules/
22 | tmp/
23 | temp/
24 | hooks/
25 | platforms/
26 | plugins/
27 | plugins/android.json
28 | plugins/ios.json
29 | www/
30 | $RECYCLE.BIN/
31 |
32 | .DS_Store
33 | Thumbs.db
34 | UserInterfaceState.xcuserstate
35 |
36 | src/components/scroll-hide/dev
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ionic Scroll Hide
2 |
3 | This plugin was inspired by [Ionic Scroll Sista](https://github.com/djett41/ionic-scroll-sista). Tested in Ionic 2 and 3.
4 |
5 | ## Setup
6 |
7 | 1. Install jQuery library
8 | ```
9 | npm install --save jquery
10 | ```
11 | 2. Copy `scroll-hide` folder into your Ionic project (src/components).
12 | 3. When using Ionic 2, remember to uncomment the line 547 and comment the line 545 (inside the `onContentScroll()` of `ScrollHide`).
13 | ```typescript
14 | export class ScrollHide implements OnInit, OnDestroy {
15 | ...
16 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
17 |
18 | // For Ionic 3
19 | //let maxScrollTop = this.content._scrollContent.nativeElement.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0);
20 | // For Ionic 2
21 | let maxScrollTop = this.content._scrollEle.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0); // For ionic2
22 | ...
23 | }
24 | ...
25 | }
26 | ```
27 | 4. Add `ScrollHide` class to the declarations NgModule metadata.
28 | #### app.module.ts
29 | ```typescript
30 | import { ScrollHide } from './../components/scroll-hide/scroll-hide';
31 |
32 | @NgModule({
33 | declarations: [
34 | MyApp,
35 | ...
36 | ScrollHide,
37 | ],
38 | ...
39 | })
40 | export class AppModule { }
41 | ```
42 |
43 | ## Usage
44 |
45 | Scroll Hide is an attribute directive, and requires an input of `viewCtrl`. So, rememeber to inject ViewController into the page constrcutor. To enable the plugin, add `[scroll-hide]="viewCtrl"` into `ion-content`.
46 |
47 |
48 | There are three transistion types can be used for each `ion-toolbar`, they are `Static`, `Translate`, `Shrink`.
49 |
50 | #### Static
51 | The toolbar would not be translated and shrinked during scrolling. It is the default type.
52 |
53 | 
54 |
55 | #### Translate
56 | The toolbar would be translated during scrolling. To enable, add an attribute of `scroll-hide-translate` into ion-toolbar.
57 |
58 | 
59 |
60 | #### Shrink
61 | The toolbar would be shrinked during scrolling. To enable, add an attribute of `scroll-hide-shrink="shrinkVal"` into ion-toolbar, where `shrinkVal` is the maximum percentage of shrinking, defualt to 1.
62 |
63 | |shrinkVal = 1 | shrinkVal = 0.5 |
64 | ------------------------------------|---------------------------------------|
65 | | |  |
66 |
67 |
68 | ## Example
69 |
70 | #### home.ts
71 | ```typescript
72 | import { Component } from '@angular/core';
73 | import { NavController, ViewController } from 'ionic-angular';
74 |
75 | @Component({
76 | selector: 'page-home',
77 | templateUrl: 'home.html'
78 | })
79 | export class HomePage {
80 |
81 | constructor(public navCtrl: NavController,
82 | public viewCtrl: ViewController) {}
83 | }
84 | ```
85 | #### home.html
86 | ```html
87 |
88 |
89 | Home
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Welcome to Ionic!
99 |
100 | ```
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | scroll-hide-test
4 | An awesome Ionic/Cordova app.
5 | Ionic Framework Team
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scroll-hide",
3 | "app_id": "",
4 | "v2": true,
5 | "typescript": true
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scroll-hide-test",
3 | "version": "0.0.0",
4 | "author": "Ionic Framework",
5 | "homepage": "http://ionicframework.com/",
6 | "private": true,
7 | "scripts": {
8 | "clean": "ionic-app-scripts clean",
9 | "build": "ionic-app-scripts build",
10 | "lint": "ionic-app-scripts lint",
11 | "ionic:build": "ionic-app-scripts build",
12 | "ionic:serve": "ionic-app-scripts serve"
13 | },
14 | "dependencies": {
15 | "@angular/common": "4.1.2",
16 | "@angular/compiler": "4.1.2",
17 | "@angular/compiler-cli": "4.1.2",
18 | "@angular/core": "4.1.2",
19 | "@angular/forms": "4.1.2",
20 | "@angular/http": "4.1.2",
21 | "@angular/platform-browser": "4.1.2",
22 | "@angular/platform-browser-dynamic": "4.1.2",
23 | "@ionic-native/core": "3.10.2",
24 | "@ionic-native/splash-screen": "3.10.2",
25 | "@ionic-native/status-bar": "3.10.2",
26 | "@ionic/storage": "2.0.1",
27 | "ionic-angular": "3.3.0",
28 | "ionicons": "3.0.0",
29 | "jquery": "^3.2.1",
30 | "rxjs": "5.1.1",
31 | "sw-toolbox": "3.6.0",
32 | "zone.js": "0.8.11"
33 | },
34 | "devDependencies": {
35 | "@ionic/app-scripts": "1.3.7",
36 | "typescript": "2.3.3"
37 | },
38 | "cordovaPlugins": [
39 | "cordova-plugin-statusbar",
40 | "cordova-plugin-device",
41 | "cordova-plugin-whitelist",
42 | "cordova-plugin-splashscreen",
43 | "cordova-plugin-console",
44 | "ionic-plugin-keyboard"
45 | ],
46 | "cordovaPlatforms": [
47 | "ios",
48 | {
49 | "platform": "ios",
50 | "version": "",
51 | "locator": "ios"
52 | }
53 | ],
54 | "description": "scroll-hide-test: An Ionic project"
55 | }
56 |
--------------------------------------------------------------------------------
/readme/shrink-0.5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/shrink-0.5.gif
--------------------------------------------------------------------------------
/readme/shrink-0.5.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/shrink-0.5.mov
--------------------------------------------------------------------------------
/readme/shrink-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/shrink-1.gif
--------------------------------------------------------------------------------
/readme/shrink-1.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/shrink-1.mov
--------------------------------------------------------------------------------
/readme/static.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/static.gif
--------------------------------------------------------------------------------
/readme/static.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/static.mov
--------------------------------------------------------------------------------
/readme/translate.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/translate.gif
--------------------------------------------------------------------------------
/readme/translate.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/readme/translate.mov
--------------------------------------------------------------------------------
/resources/android/icon/drawable-hdpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-hdpi-icon.png
--------------------------------------------------------------------------------
/resources/android/icon/drawable-ldpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-ldpi-icon.png
--------------------------------------------------------------------------------
/resources/android/icon/drawable-mdpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-mdpi-icon.png
--------------------------------------------------------------------------------
/resources/android/icon/drawable-xhdpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-xhdpi-icon.png
--------------------------------------------------------------------------------
/resources/android/icon/drawable-xxhdpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-xxhdpi-icon.png
--------------------------------------------------------------------------------
/resources/android/icon/drawable-xxxhdpi-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/icon/drawable-xxxhdpi-icon.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-hdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-hdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-ldpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-ldpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-mdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-mdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-xhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-xhdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-xxhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-xxhdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-land-xxxhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-land-xxxhdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-hdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-hdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-ldpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-ldpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-mdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-mdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-xhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-xhdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-xxhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-xxhdpi-screen.png
--------------------------------------------------------------------------------
/resources/android/splash/drawable-port-xxxhdpi-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/android/splash/drawable-port-xxxhdpi-screen.png
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/icon.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-40.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-40@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-40@3x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-50.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-50@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-60.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-60@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-60@3x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-72.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-72@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-76.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-76@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-83.5@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-small.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-small@2x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon-small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon-small@3x.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon.png
--------------------------------------------------------------------------------
/resources/ios/icon/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/icon/icon@2x.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-568h@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-568h@2x~iphone.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-667h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-667h.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-736h.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Landscape-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Landscape-736h.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Landscape@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Landscape@2x~ipad.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Landscape@~ipadpro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Landscape@~ipadpro.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Landscape~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Landscape~ipad.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Portrait@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Portrait@2x~ipad.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Portrait@~ipadpro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Portrait@~ipadpro.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default-Portrait~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default-Portrait~ipad.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default@2x~iphone.png
--------------------------------------------------------------------------------
/resources/ios/splash/Default~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/ios/splash/Default~iphone.png
--------------------------------------------------------------------------------
/resources/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/resources/splash.png
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Platform } from 'ionic-angular';
3 | import { StatusBar } from '@ionic-native/status-bar';
4 | import { SplashScreen } from '@ionic-native/splash-screen';
5 |
6 | import { TabsPage } from '../pages/tabs/tabs';
7 |
8 | @Component({
9 | templateUrl: 'app.html'
10 | })
11 | export class MyApp {
12 | rootPage:any = TabsPage;
13 |
14 | constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
15 | platform.ready().then(() => {
16 | // Okay, so the platform is ready and our plugins are available.
17 | // Here you can do any higher level native things you might need.
18 | statusBar.styleDefault();
19 | splashScreen.hide();
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, ErrorHandler } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
4 | import { MyApp } from './app.component';
5 |
6 | import { AboutPage } from '../pages/about/about';
7 | import { ContactPage } from '../pages/contact/contact';
8 | import { HomePage } from '../pages/home/home';
9 | import { TabsPage } from '../pages/tabs/tabs';
10 |
11 | import { StatusBar } from '@ionic-native/status-bar';
12 | import { SplashScreen } from '@ionic-native/splash-screen';
13 |
14 |
15 | // Directive
16 | import { ScrollHide } from './../components/scroll-hide/scroll-hide';
17 |
18 |
19 |
20 |
21 | @NgModule({
22 | declarations: [
23 | MyApp,
24 | AboutPage,
25 | ContactPage,
26 | HomePage,
27 | TabsPage,
28 |
29 | // Directive
30 | ScrollHide,
31 |
32 | ],
33 | imports: [
34 | BrowserModule,
35 | IonicModule.forRoot(MyApp)
36 | ],
37 | bootstrap: [IonicApp],
38 | entryComponents: [
39 | MyApp,
40 | AboutPage,
41 | ContactPage,
42 | HomePage,
43 | TabsPage
44 | ],
45 | providers: [
46 | StatusBar,
47 | SplashScreen,
48 | { provide: ErrorHandler, useClass: IonicErrorHandler }
49 | ]
50 | })
51 | export class AppModule { }
52 |
--------------------------------------------------------------------------------
/src/app/app.scss:
--------------------------------------------------------------------------------
1 | // http://ionicframework.com/docs/v2/theming/
2 |
3 |
4 | // App Global Sass
5 | // --------------------------------------------------
6 | // Put style rules here that you want to apply globally. These
7 | // styles are for the entire app and not just one component.
8 | // Additionally, this file can be also used as an entry point
9 | // to import other Sass files to be included in the output CSS.
10 | //
11 | // Shared Sass variables, which can be used to adjust Ionic's
12 | // default Sass variables, belong in "theme/variables.scss".
13 | //
14 | // To declare rules for a specific mode, create a child rule
15 | // for the .md, .ios, or .wp mode classes. The mode class is
16 | // automatically applied to the
element in the app.
17 |
--------------------------------------------------------------------------------
/src/app/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app.module';
4 |
5 | platformBrowserDynamic().bootstrapModule(AppModule);
6 |
--------------------------------------------------------------------------------
/src/assets/icon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaslee2775/ionic-scroll-hide/ecaf85b85621f3d7de00082140ebc35e111e8793/src/assets/icon/favicon.ico
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/_scroll-hide.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 |
14 |
15 |
16 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
17 |
18 |
19 | function constrain(val: number, min: number, max: number) {
20 | return Math.min(max, Math.max(val, min));
21 | }
22 |
23 | function forEach(children: HTMLCollection, callback: (ele: HTMLElement) => void) {
24 | for(var i = 0; i < children.length; i++) {
25 | callback( children[i]);
26 | }
27 | }
28 |
29 |
30 |
31 | interface Item {
32 | ele: HTMLElement;
33 | startY: number;
34 | endY: number;
35 | unshrinkable: boolean;
36 | }
37 |
38 |
39 | @Directive({
40 | selector: '[scroll-hide]' // Attribute selector
41 | })
42 | export class ScrollHide implements OnInit, OnDestroy {
43 |
44 | @Input() navCtrl: NavController;
45 |
46 | initiated: boolean = false;
47 |
48 | contentTop: number;
49 | endY: number;
50 |
51 | //tabbarOnBottom: boolean = true;
52 | //tabbarEle: HTMLElement;
53 | headerItems: Item[] = [];
54 | footerItems: Item[] = [];
55 |
56 |
57 | constructor(private content: Content,
58 | private renderer: Renderer,
59 | private zone: NgZone) {}
60 |
61 |
62 | ngOnInit() {
63 | this.content.fullscreen = true;
64 |
65 | //console.log("mode:", this.content._mode, "tabs:", this.content._tabs);
66 | console.log("ngOnInit() > navCtrl:", this.navCtrl)
67 |
68 |
69 | /*
70 | this.navCtrl.viewDidEnter.subscribe((event) => {
71 | //console.log("viewDidEnter > _tabsPlacement:", this.content._tabsPlacement);
72 | console.log("ScrollHide > viewDidEnter()", event);
73 | });
74 | this.navCtrl.viewWillLeave.subscribe((event) => {
75 | console.log("ScrollHide > viewWillLeave()", event);
76 | });
77 | */
78 |
79 |
80 |
81 | let win: any = window;
82 | win.temp = win.temp || {};
83 | win.temp.content = this.content;
84 | win.temp.zone = this.zone;
85 | }
86 |
87 | ngOnDestroy() {
88 | console.log("ngOnDestroy()");
89 | }
90 |
91 | private init() {
92 | let contentEle: HTMLElement = this.content.getNativeElement();
93 | let headerEle = contentEle.parentElement.querySelector("ion-header");
94 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
95 | if(headerEle) {
96 | this.headerItems = [];
97 | for(var i = 0; i < headerEle.children.length; i++) {
98 | this.headerItems[i] = {
99 | ele: headerEle.children[i],
100 | startY: 0,
101 | endY: 0,
102 | unshrinkable: false
103 | };
104 | }
105 | }
106 | if(footerEle) {
107 | this.footerItems = [];
108 | for(var i = 0; i < footerEle.children.length; i++) {
109 | this.footerItems[i] = {
110 | ele: footerEle.children[i],
111 | startY: 0,
112 | endY: 0,
113 | unshrinkable: false
114 | };
115 | }
116 | }
117 |
118 |
119 |
120 |
121 | let hasTabbar = (this.content._tabs !== null);
122 | if(hasTabbar) {
123 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
124 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
125 |
126 | if(this.content._tabsPlacement === "bottom") {
127 | this.footerItems.push({
128 | ele: tabbarEle,
129 | startY: 0,
130 | endY: 0,
131 | unshrinkable: false
132 | });
133 | } else {
134 | this.headerItems.push({
135 | ele: tabbarEle,
136 | startY: 0,
137 | endY: 0,
138 | unshrinkable: false
139 | });
140 | }
141 | }
142 |
143 |
144 | this.headerItems = this.headerItems.reverse();
145 | this.footerItems = this.footerItems.reverse();
146 |
147 |
148 |
149 | var contentTop = 0;
150 |
151 | console.group("headerItems")
152 |
153 | var shrinkableHeightSum = 0;
154 | this.headerItems.forEach((item, index) => {
155 | contentTop += item.ele.offsetHeight;
156 |
157 | //this.renderer.setElementStyle(item.ele, "z-index", this.headerItems.length - index + "");
158 |
159 | if(item.ele.classList.contains("tabbar")) {
160 | this.renderer.setElementStyle(item.ele, "overflow", "hidden");
161 | forEach(item.ele.children, (tabButton) => {
162 | this.renderer.setElementStyle(tabButton, "align-self", "flex-end");
163 | });
164 | } else {
165 | this.renderer.setElementStyle(item.ele, "z-index", (index + 1) + "");
166 | }
167 |
168 | if(item.ele.tagName === "ION-NAVBAR") {
169 | this.renderer.setElementStyle(item.ele, "position", "relative")
170 | }
171 |
172 |
173 | if(!item.ele.classList.contains("unshrinkable")) {
174 | item.startY = shrinkableHeightSum;
175 | item.endY = shrinkableHeightSum + item.ele.offsetHeight;
176 | item.unshrinkable = false;
177 |
178 | shrinkableHeightSum += item.ele.offsetHeight;
179 |
180 | } else {
181 | item.startY = shrinkableHeightSum;
182 | item.endY = shrinkableHeightSum;
183 | item.unshrinkable = true;
184 | }
185 |
186 | console.log(item, item.ele.offsetHeight);
187 | });
188 | this.contentTop = contentTop;
189 | this.defaultEnd = this.contentTop * 2;
190 | this.endY = shrinkableHeightSum;
191 |
192 | console.groupEnd();
193 |
194 |
195 | console.group("footerItems")
196 | this.footerItems.forEach(item => {
197 | console.log(item.ele, item.ele.offsetHeight);
198 | });
199 | console.groupEnd();
200 |
201 | console.log("contentTop:", contentTop, ", endY:", this.endY);
202 |
203 |
204 | /*
205 | ion-header {
206 | pointer-events: none;
207 | }
208 |
209 | ion-header > * {
210 | pointer-events: all;
211 | }*/
212 |
213 | if(headerEle) {
214 | this.renderer.setElementStyle(headerEle, "pointer-events", "none");
215 | this.headerItems.forEach((item) => {
216 | this.renderer.setElementStyle(item.ele, "pointer-events", "all");
217 | });
218 | }
219 | if(footerEle) {
220 | this.renderer.setElementStyle(footerEle, "pointer-events", "none");
221 | this.footerItems.forEach((item) => {
222 | this.renderer.setElementStyle(item.ele, "pointer-events", "all");
223 | });
224 | }
225 | }
226 |
227 |
228 | defaultDelay: number = 400 * 2;
229 | defaultDuration: number = 400;
230 |
231 | defaultEnd: number = 0;
232 | y: number = 0;
233 | yPrev: number = 0;
234 | scrollTopPrev: number = 0;
235 |
236 | @HostListener("ionScroll", ["$event"])
237 | onContentScoll(event: ScrollEvent) {
238 | //console.log("ionScroll", event); //{ contentHeight: event.contentHeight, scrollHeight: event.scrollHeight });
239 | //this.checkTabsPlacment();
240 |
241 | if(!this.initiated) {
242 | this.initiated = true;
243 | this.init();
244 | this.content.resize();
245 | }
246 |
247 |
248 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
249 |
250 |
251 |
252 | var duration = 0;
253 | let scrollTop = event.scrollTop;
254 | let scrollTopDiff = scrollTop - this.scrollTopPrev;
255 |
256 | let y = (scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
257 |
258 |
259 |
260 | //if we are at the bottom, animate the header/tabs back in
261 | //if (scrollView.getScrollMax().top - scrollTop <= contentTop) {
262 | // if (scrollTop >= maxScrollTop) {
263 | // y = 0;
264 | // duration = this.defaultDuration;
265 | // }
266 |
267 | this.scrollTopPrev = scrollTop;
268 |
269 | //if previous and current y are the same, no need to continue
270 | if (this.yPrev === y) {
271 | return;
272 | }
273 | this.yPrev = y;
274 |
275 |
276 |
277 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
278 |
279 |
280 | this.modifyDom(y * 1.0, duration, event.domWrite);
281 | }
282 |
283 |
284 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
285 | dowWrite(() => {
286 |
287 | // Header Items
288 | this.headerItems.forEach((item) => {
289 |
290 | /*if(y < item.endY) {
291 | this.translateElementY(item.ele, -y, duration);
292 |
293 | let dy = (y - item.startY);
294 | let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight * 2), 0, 1);
295 |
296 | forEach(item.ele.children, (navbarChild) => {
297 | if(!navbarChild.classList.contains("toolbar-background")) {
298 | this.renderer.setElementStyle(navbarChild, "opacity", fadeAmt + "");
299 | }
300 | });
301 |
302 | } else {
303 | this.translateElementY(item.ele, -item.endY, duration);
304 | // if(item.unshrinkable) {
305 | // this.translateElementY(item.ele, -item.endY, duration);
306 | // } else {
307 | // this.translateElementY(item.ele, -item.endY - 100, duration);
308 | // }
309 | }*/
310 |
311 |
312 | /*
313 | if(y <= this.endY) {
314 | if(item.startY <= y) {
315 | this.translateElementY(item.ele, -(y - item.startY), duration);
316 |
317 | if(!item.unshrinkable) {
318 | let dy = (y - item.startY);
319 | let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
320 |
321 | forEach(item.ele.children, (navbarChild) => {
322 | if(!navbarChild.classList.contains("toolbar-background")) {
323 | this.renderer.setElementStyle(navbarChild, "opacity", fadeAmt + "");
324 | }
325 | });
326 | }
327 |
328 | } else {
329 | this.translateElementY(item.ele, 0, duration);
330 | }
331 | } else {
332 | this.translateElementY(item.ele, -(this.endY - item.startY), duration);
333 | }*/
334 |
335 |
336 |
337 | let dy = constrain((y - item.startY), 0, (this.endY - item.startY));
338 |
339 | if(item.ele.classList.contains("tabbar")) {
340 | var val = constrain((dy / this.content._tabbarHeight), 0, 1);
341 |
342 | console.log("val:", val);
343 |
344 | let height = this.content._tabbarHeight * (1 - val);
345 | if(height >= 0) {
346 | this.renderer.setElementStyle(item.ele, "height", height + "px");
347 | this.renderer.setElementStyle(item.ele, "display", "");
348 | } else {
349 | this.renderer.setElementStyle(item.ele, "display", "none");
350 | }
351 |
352 | } else {
353 | var val = constrain((dy / item.ele.offsetHeight), 0, 1);
354 |
355 | this.translateElementY(item.ele, -dy, duration);
356 | }
357 | if(!item.unshrinkable) {
358 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
359 | let fadeAmt = 1 - val;
360 | forEach(item.ele.children, (navbarChild) => {
361 | if(!navbarChild.classList.contains("toolbar-background")) {
362 | this.renderer.setElementStyle(navbarChild, "opacity", fadeAmt + "");
363 | }
364 | });
365 | //item.ele.querySelectorAll("")
366 | }
367 |
368 |
369 |
370 |
371 |
372 | //this.translateElementY(item.ele, -y, duration);
373 |
374 | // if(item.ele.tagName === "ION-NAVBAR") {
375 | // var fadeAmt = 1 - (y / item.ele.offsetHeight);
376 |
377 | // forEach(item.ele.children, (navbarChild) => {
378 | // if(!navbarChild.classList.contains("toolbar-background")) {
379 | // this.renderer.setElementStyle(navbarChild, "opacity", fadeAmt + "");
380 | // //if (scaleHeaderElements) {
381 | // // this.renderer.setElementStyle(navbarChild, "transform", `scale(${fadeAmt}, ${fadeAmt})`);
382 | // //}
383 | // }
384 | // });
385 | // }
386 | });
387 |
388 |
389 |
390 | // Footer Items
391 | this.footerItems.forEach((item) => {
392 | this.translateElementY(item.ele, y, duration);
393 | });
394 | });
395 | }
396 |
397 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
398 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
399 |
400 | if (duration && !ele.style.transitionDuration) {
401 | ele.style.transitionDuration = duration + "ms";
402 | setTimeout(() => {
403 | ele.style.transitionDuration = "";
404 | }, this.defaultDelay);
405 | }
406 | }
407 |
408 | private translateElementHeight(ele: HTMLElement, height: number) {
409 | this.renderer.setElementStyle(ele, "height", `${height}px`);
410 | }
411 | }
412 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v1.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 |
14 |
15 |
16 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
17 |
18 |
19 | function constrain(val: number, min: number, max: number) {
20 | return Math.min(max, Math.max(val, min));
21 | }
22 |
23 | function forEach(children: HTMLCollection, callback: (ele: HTMLElement) => void) {
24 | for(var i = 0; i < children.length; i++) {
25 | callback( children[i]);
26 | }
27 | }
28 |
29 |
30 | /*
31 | interface Item {
32 | ele: HTMLElement;
33 | startY: number;
34 | endY: number;
35 | unshrinkable: boolean;
36 | }
37 | */
38 |
39 | enum ItemType {
40 | Navbar, Toolbar, Tabbar
41 | }
42 | class Item {
43 | public type: ItemType;
44 | public shrinkable: boolean;
45 | public startY = 0;
46 | public endY = 0;
47 |
48 | constructor(public ele: HTMLElement) {
49 |
50 | if(ele.classList.contains("tabbar")) {
51 | this.type = ItemType.Tabbar;
52 | } else if(ele.tagName === "ION-NAVBAR") {
53 | this.type = ItemType.Navbar;
54 | } else {
55 | this.type = ItemType.Toolbar;
56 | }
57 |
58 | this.shrinkable = !ele.classList.contains("unshrinkable");
59 | }
60 |
61 | setY(startY: number, endY: number) {
62 | this.startY = startY;
63 | this.endY = endY;
64 | }
65 | }
66 |
67 |
68 | @Directive({
69 | selector: '[scroll-hide]' // Attribute selector
70 | })
71 | export class ScrollHide implements OnInit, OnDestroy {
72 |
73 | @Input() navCtrl: NavController;
74 | @Input() viewCtrl: ViewController;
75 |
76 | initiated: boolean = false;
77 |
78 | contentTop: number;
79 | endY: number;
80 |
81 | //tabbarOnBottom: boolean = true;
82 | //tabbarEle: HTMLElement;
83 | headerItems: Item[] = [];
84 | footerItems: Item[] = [];
85 |
86 | headerEle: HTMLElement;
87 | footerEle: HTMLElement;
88 |
89 | constructor(private content: Content,
90 | private renderer: Renderer,
91 | private zone: NgZone) {}
92 |
93 |
94 | ngOnInit() {
95 | this.content.fullscreen = true;
96 |
97 | //console.log("mode:", this.content._mode, "tabs:", this.content._tabs);
98 | console.log("ngOnInit() > navCtrl:", this.navCtrl);
99 | console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
100 |
101 |
102 | this.viewCtrl.didEnter.subscribe(() => {
103 | //console.log("ScrollHide > viewCtrl.didEnter(), _tabsPlacement:", this.content._tabsPlacement);
104 | console.log("init()");
105 | this.init();
106 | this.content.resize();
107 | });
108 |
109 | this.viewCtrl.willLeave.subscribe(() => {
110 | console.log("resetStyle()");
111 | this.resetStyle();
112 | });
113 |
114 |
115 |
116 | let win: any = window;
117 | win.temp = win.temp || {};
118 | win.temp.content = this.content;
119 | win.temp.zone = this.zone;
120 | }
121 |
122 | ngOnDestroy() {
123 | console.log("ngOnDestroy()");
124 | }
125 |
126 | private init() {
127 | this.y = 0;
128 | this.yPrev = 0;
129 | this.scrollTopPrev = this.content.scrollTop;
130 |
131 |
132 |
133 | let contentEle = this.content.getNativeElement();
134 | let headerEle = contentEle.parentElement.querySelector("ion-header");
135 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
136 |
137 | if(headerEle) {
138 | this.headerItems = $(headerEle)
139 | .children()
140 | .toArray().map(ele => new Item(ele));
141 | }
142 | if(footerEle) {
143 | this.footerItems = $(footerEle)
144 | .children()
145 | .toArray().map(ele => new Item(ele));
146 | }
147 |
148 |
149 |
150 |
151 | let hasTabbar = (this.content._tabs !== null);
152 | if(hasTabbar) {
153 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
154 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
155 |
156 | if(this.content._tabsPlacement === "bottom") {
157 | this.footerItems.push(new Item(tabbarEle));
158 | } else {
159 | this.headerItems.push(new Item(tabbarEle));
160 | }
161 | }
162 |
163 |
164 | this.headerItems = this.headerItems.reverse();
165 | this.footerItems = this.footerItems.reverse();
166 |
167 |
168 |
169 |
170 | var contentTop = 0;
171 |
172 | console.group("headerItems")
173 |
174 | var shrinkableHeightSum = 0;
175 | this.headerItems.forEach((item, index) => {
176 | contentTop += item.ele.offsetHeight;
177 |
178 | // Navbar would not be raised without setting position to relative
179 | if(item.type === ItemType.Navbar) {
180 | $(item.ele).css("position", "relative")
181 | }
182 |
183 |
184 | // Prevent overlapping of bottom element
185 | if(item.type === ItemType.Tabbar) {
186 | $(item.ele).css("overflow", "hidden");
187 | $(item.ele).children().css("align-self", "flex-end");
188 | } else {
189 | $(item.ele).css("z-index", index + 1);
190 | }
191 |
192 |
193 | if(item.shrinkable) {
194 | item.setY(
195 | shrinkableHeightSum,
196 | shrinkableHeightSum + item.ele.offsetHeight
197 | );
198 | shrinkableHeightSum += item.ele.offsetHeight;
199 |
200 | } else {
201 | item.setY(
202 | shrinkableHeightSum,
203 | shrinkableHeightSum
204 | );
205 | }
206 |
207 | console.log(item, item.ele.offsetHeight);
208 | });
209 | this.contentTop = contentTop;
210 | this.defaultEnd = this.contentTop * 2;
211 | this.endY = shrinkableHeightSum;
212 |
213 | console.groupEnd();
214 |
215 |
216 | console.group("footerItems")
217 | this.footerItems.forEach(item => {
218 | console.log(item.ele, item.ele.offsetHeight);
219 | });
220 | console.groupEnd();
221 |
222 | console.log("contentTop:", contentTop, ", endY:", this.endY);
223 |
224 |
225 | /*
226 | ion-header {
227 | pointer-events: none;
228 | }
229 |
230 | ion-header > * {
231 | pointer-events: all;
232 | }*/
233 |
234 | if(headerEle) {
235 | $(headerEle).css("pointer-events", "none");
236 | $(headerEle).children().css("pointer-events", "all");
237 | }
238 | if(footerEle) {
239 | $(footerEle).css("pointer-events", "none");
240 | $(footerEle).children().css("pointer-events", "all");
241 | }
242 |
243 | this.headerEle = headerEle;
244 | this.footerEle = footerEle;
245 | }
246 |
247 |
248 | private resetStyle() {
249 | this.headerItems.forEach((item, index) => {
250 |
251 | if(item.type === ItemType.Navbar) {
252 | $(item.ele).css("position", "");
253 | }
254 |
255 | if(item.type === ItemType.Tabbar) {
256 | $(item.ele).css("overflow", "");
257 | $(item.ele).children().css("align-self", "center");
258 | } else {
259 | $(item.ele).css("z-index", "");
260 | }
261 |
262 | });
263 |
264 | if(this.headerEle) {
265 | $(this.headerEle).css("pointer-events", "none");
266 | $(this.headerEle).children().css("pointer-events", "all");
267 | }
268 | if(this.footerEle) {
269 | $(this.footerEle).css("pointer-events", "none");
270 | $(this.footerEle).children().css("pointer-events", "all");
271 | }
272 |
273 |
274 | // Header Items
275 | this.headerItems.forEach((item) => {
276 |
277 |
278 | $(item.ele).css("height", "");
279 | $(item.ele).css("display", "");
280 | $(item.ele).css("webkitTransform", "");
281 |
282 | if(item.shrinkable) {
283 | $(item.ele).children().not(".toolbar-background")
284 | .each((index, navbarChild) => {
285 | $(navbarChild).css("opacity", "");
286 | });
287 | }
288 | });
289 |
290 |
291 | // Footer Items
292 | this.footerItems.forEach((item) => {
293 | $(item.ele).css("webkitTransform", "");
294 | });
295 | }
296 |
297 |
298 |
299 | defaultDelay: number = 400 * 2;
300 | defaultDuration: number = 400;
301 |
302 | defaultEnd: number = 0;
303 | y: number = 0;
304 | yPrev: number = 0;
305 | scrollTopPrev: number = 0;
306 |
307 | @HostListener("ionScroll", ["$event"])
308 | onContentScoll(event: ScrollEvent) {
309 | //console.log("ionScroll", event); //{ contentHeight: event.contentHeight, scrollHeight: event.scrollHeight });
310 | //this.checkTabsPlacment();
311 |
312 | /*if(!this.initiated) {
313 | this.initiated = true;
314 | this.init();
315 | this.content.resize();
316 | }*/
317 |
318 |
319 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
320 |
321 |
322 |
323 | var duration = 0;
324 | let scrollTop = event.scrollTop;
325 | let scrollTopDiff = scrollTop - this.scrollTopPrev;
326 |
327 | let y = (scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
328 |
329 |
330 |
331 | //if we are at the bottom, animate the header/tabs back in
332 | //if (scrollView.getScrollMax().top - scrollTop <= contentTop) {
333 | // if (scrollTop >= maxScrollTop) {
334 | // y = 0;
335 | // duration = this.defaultDuration;
336 | // }
337 |
338 | this.scrollTopPrev = scrollTop;
339 |
340 | //if previous and current y are the same, no need to continue
341 | if (this.yPrev === y) {
342 | return;
343 | }
344 | this.yPrev = y;
345 |
346 |
347 |
348 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
349 |
350 |
351 | this.modifyDom(y * 1.0, duration, event.domWrite);
352 | }
353 |
354 |
355 |
356 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
357 | dowWrite(() => {
358 |
359 | // Header Items
360 | this.headerItems.forEach((item) => {
361 |
362 | let dy = constrain((y - item.startY), 0, (this.endY - item.startY));
363 |
364 | if(item.type === ItemType.Tabbar) {
365 | var val = constrain((dy / this.content._tabbarHeight), 0, 1);
366 |
367 | console.log("val:", val);
368 |
369 | let height = this.content._tabbarHeight * (1 - val);
370 | if(height >= 0) {
371 | $(item.ele).css("height", height + "px");
372 | $(item.ele).css("display", "");
373 | } else {
374 | $(item.ele).css("display", "none");
375 | }
376 |
377 | } else {
378 | var val = constrain((dy / item.ele.offsetHeight), 0, 1);
379 |
380 | this.translateElementY(item.ele, -dy, duration);
381 | }
382 | if(item.shrinkable) {
383 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
384 | let fadeAmt = 1 - val;
385 |
386 | $(item.ele).children().not(".toolbar-background")
387 | .each((index, navbarChild) => {
388 | $(navbarChild).css("opacity", fadeAmt);
389 | });
390 | }
391 | });
392 |
393 |
394 |
395 | // Footer Items
396 | this.footerItems.forEach((item) => {
397 | this.translateElementY(item.ele, y, duration);
398 | });
399 | });
400 | }
401 |
402 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
403 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
404 |
405 | /*if (duration && !ele.style.transitionDuration) {
406 | ele.style.transitionDuration = duration + "ms";
407 | setTimeout(() => {
408 | ele.style.transitionDuration = "";
409 | }, this.defaultDelay);
410 | }*/
411 | }
412 | }
413 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v2.0.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 | /*
14 |
15 |
16 | ion-header {
17 | pointer-events: none;
18 | }
19 | ion-header > * {
20 | pointer-events: all;
21 | }
22 |
23 |
24 |
25 |
26 | ion-header > ion-navbar {
27 | position: relative;
28 | }
29 |
30 |
31 |
32 | ion-tabs > .tabbar {
33 | overflow: hidden;
34 | }
35 | ion-tabs > .tabbar > *{
36 | align-self: flex-end;
37 | }
38 |
39 | */
40 |
41 |
42 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
43 |
44 |
45 | function constrain(val: number, min: number, max: number) {
46 | return Math.min(max, Math.max(val, min));
47 | }
48 |
49 | function forEach(children: HTMLCollection, callback: (ele: HTMLElement) => void) {
50 | for(var i = 0; i < children.length; i++) {
51 | callback( children[i]);
52 | }
53 | }
54 |
55 |
56 | /*
57 | interface Item {
58 | ele: HTMLElement;
59 | startY: number;
60 | endY: number;
61 | unshrinkable: boolean;
62 | }
63 | */
64 |
65 |
66 | enum ItemType {
67 | Navbar, Toolbar, Tabbar
68 | }
69 | class Item {
70 | public type: ItemType;
71 | public shrinkable: boolean;
72 | public startY = 0;
73 | public endY = Infinity;
74 | public height: number;
75 |
76 | constructor(public ele: HTMLElement) {
77 |
78 | if(ele.classList.contains("tabbar")) {
79 | this.type = ItemType.Tabbar;
80 | } else if(ele.tagName === "ION-NAVBAR") {
81 | this.type = ItemType.Navbar;
82 | } else {
83 | this.type = ItemType.Toolbar;
84 | }
85 |
86 | this.shrinkable = !ele.classList.contains("unshrinkable");
87 | this.height = ele.offsetHeight;
88 | }
89 |
90 | setY(startY: number, endY: number) {
91 | this.startY = startY;
92 | this.endY = endY;
93 | }
94 |
95 | setStartY(startY: number) {
96 | this.startY = startY;
97 | }
98 | setEndY(endY: number) {
99 | this.endY = endY;
100 | }
101 |
102 | }
103 |
104 |
105 | @Directive({
106 | selector: '[scroll-hide]' // Attribute selector
107 | })
108 | export class ScrollHide implements OnInit, OnDestroy {
109 |
110 | @Input() navCtrl: NavController;
111 | @Input() viewCtrl: ViewController;
112 |
113 |
114 | headerItems: Item[] = [];
115 | footerItems: Item[] = [];
116 |
117 | headerEle: HTMLElement;
118 | footerEle: HTMLElement;
119 |
120 | //---------------------------------
121 |
122 | defaultDelay: number = 400 * 2;
123 | defaultDuration: number = 400;
124 |
125 |
126 | headerHeight: number;
127 | footerHeight: number;
128 | endY: number;
129 |
130 | defaultEnd: number = 0;
131 | y: number = 0;
132 | yPrev: number = 0;
133 | scrollTopPrev: number = 0;
134 |
135 | //---------------------------------
136 |
137 |
138 | constructor(private content: Content,
139 | private renderer: Renderer,
140 | private zone: NgZone) {}
141 |
142 |
143 | ngOnInit() {
144 | this.content.fullscreen = true;
145 |
146 |
147 | //console.log("ngOnInit() > navCtrl:", this.navCtrl);
148 | //console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
149 |
150 |
151 | this.viewCtrl.didEnter.subscribe(() => {
152 | //console.log("ScrollHide > viewCtrl.didEnter(), _tabsPlacement:", this.content._tabsPlacement);
153 | console.log("init()");
154 | this.init();
155 | });
156 | this.viewCtrl.willLeave.subscribe(() => {
157 | console.log("resetStyle()");
158 | this.resetStyle();
159 | });
160 |
161 |
162 | this.content.ionScroll.subscribe(event => {
163 | this.onContentScroll(event, false);
164 | });
165 | this.content.ionScrollEnd.subscribe(event => {
166 | console.log("ionScrollEnd!");
167 | this.onContentScroll(event, true);
168 | });
169 |
170 |
171 |
172 | let win: any = window;
173 | win.temp = win.temp || {};
174 | win.temp.content = this.content;
175 | win.temp.zone = this.zone;
176 | }
177 |
178 | ngOnDestroy() {
179 | console.log("ngOnDestroy()");
180 | }
181 |
182 | private init() {
183 | this.y = 0;
184 | this.yPrev = 0;
185 | this.scrollTopPrev = this.content.scrollTop;
186 |
187 |
188 |
189 | this.content.resize();
190 |
191 |
192 | let contentEle = this.content.getNativeElement();
193 | let headerEle = contentEle.parentElement.querySelector("ion-header");
194 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
195 |
196 | if(headerEle) {
197 | this.headerItems = $(headerEle)
198 | .children()
199 | .toArray().map(ele => new Item(ele));
200 | } else {
201 | this.headerItems = [];
202 | }
203 |
204 | if(footerEle) {
205 | this.footerItems = $(footerEle)
206 | .children()
207 | .toArray().map(ele => new Item(ele));
208 | } else {
209 | this.footerItems = [];
210 | }
211 |
212 |
213 |
214 |
215 | let hasTabbar = (this.content._tabs !== null);
216 | if(hasTabbar) {
217 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
218 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
219 |
220 | if(this.content._tabsPlacement === "bottom") {
221 | this.footerItems.push(new Item(tabbarEle));
222 | } else {
223 | this.headerItems.push(new Item(tabbarEle));
224 | }
225 | }
226 |
227 |
228 | this.headerItems = this.headerItems.reverse();
229 | this.footerItems = this.footerItems.reverse();
230 |
231 |
232 |
233 |
234 | var headerHeight = 0,
235 | footerHeight = 0;
236 |
237 | console.group("headerItems")
238 |
239 | var shrinkableHeightSum = 0;
240 | this.headerItems.forEach((item, index) => {
241 | headerHeight += item.ele.offsetHeight;
242 |
243 | // Navbar would not be raised without setting position to relative
244 | if(item.type === ItemType.Navbar) {
245 | $(item.ele).css("position", "relative")
246 | }
247 |
248 |
249 | // Prevent overlapping of bottom element
250 | if(item.type === ItemType.Tabbar) {
251 | $(item.ele).css("overflow", "hidden");
252 | $(item.ele).children().css("align-self", "flex-end");
253 | } else {
254 | $(item.ele).css("z-index", index + 1);
255 | }
256 |
257 |
258 | if(item.shrinkable) {
259 | item.setStartY(shrinkableHeightSum);
260 | item.setEndY(Infinity); //item.setEndY(shrinkableHeightSum + item.ele.offsetHeight);
261 | shrinkableHeightSum += item.ele.offsetHeight;
262 | } else {
263 | item.setStartY(shrinkableHeightSum);
264 | item.setEndY(Infinity); //item.setEndY(shrinkableHeightSum);
265 | }
266 |
267 | console.log(item, item.ele.offsetHeight);
268 | });
269 | this.headerHeight = headerHeight;
270 | this.defaultEnd = this.headerHeight * 2;
271 | this.endY = shrinkableHeightSum;
272 |
273 | console.groupEnd();
274 |
275 |
276 | console.group("footerItems")
277 | this.footerItems.forEach(item => {
278 | footerHeight += item.ele.offsetHeight;
279 | console.log(item.ele, item.ele.offsetHeight);
280 | });
281 | this.footerHeight = footerHeight;
282 | console.groupEnd();
283 |
284 | console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
285 |
286 |
287 | /*
288 | ion-header {
289 | pointer-events: none;
290 | }
291 |
292 | ion-header > * {
293 | pointer-events: all;
294 | }*/
295 |
296 | if(headerEle) {
297 | $(headerEle).css("pointer-events", "none");
298 | $(headerEle).children().css("pointer-events", "all");
299 | }
300 | if(footerEle) {
301 | $(footerEle).css("pointer-events", "none");
302 | $(footerEle).children().css("pointer-events", "all");
303 | }
304 |
305 | this.headerEle = headerEle;
306 | this.footerEle = footerEle;
307 | }
308 |
309 |
310 | private resetStyle() {
311 | this.headerItems.forEach((item, index) => {
312 |
313 | if(item.type === ItemType.Navbar) {
314 | $(item.ele).css("position", "");
315 | }
316 |
317 | if(item.type === ItemType.Tabbar) {
318 | $(item.ele).css("overflow", "");
319 | $(item.ele).children().css("align-self", "center");
320 | } else {
321 | $(item.ele).css("z-index", "");
322 | }
323 |
324 | });
325 |
326 | if(this.headerEle) {
327 | $(this.headerEle).css("pointer-events", "none");
328 | $(this.headerEle).children().css("pointer-events", "all");
329 | }
330 | if(this.footerEle) {
331 | $(this.footerEle).css("pointer-events", "none");
332 | $(this.footerEle).children().css("pointer-events", "all");
333 | }
334 |
335 |
336 | // Header Items
337 | this.headerItems.forEach((item) => {
338 |
339 | $(item.ele).css("height", "");
340 | $(item.ele).css("display", "");
341 | $(item.ele).css("webkitTransform", "");
342 |
343 | if(item.shrinkable) {
344 | $(item.ele).children().not(".toolbar-background")
345 | .each((index, navbarChild) => {
346 | $(navbarChild).css("opacity", "");
347 | });
348 | }
349 | });
350 |
351 |
352 | // Footer Items
353 | this.footerItems.forEach((item) => {
354 | $(item.ele).css("webkitTransform", "");
355 | });
356 | }
357 |
358 |
359 |
360 |
361 |
362 |
363 | hasScrollToBottomBefore = false;
364 |
365 |
366 | //@HostListener("ionScroll", ["$event", "false"])
367 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
368 | //console.log("ionScroll", event); //{ contentHeight: event.contentHeight, scrollHeight: event.scrollHeight });
369 | //this.checkTabsPlacment();
370 |
371 | /*if(!this.initiated) {
372 | this.initiated = true;
373 | this.init();
374 | this.content.resize();
375 | }*/
376 |
377 |
378 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
379 |
380 |
381 |
382 | var duration = 0;
383 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
384 |
385 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
386 |
387 |
388 |
389 | //if we are at the bottom, animate the header/tabs back in
390 | if (event.scrollTop >= maxScrollTop - this.footerHeight) {
391 |
392 | if(this.hasScrollToBottomBefore) {
393 | y = 0;
394 | duration = this.defaultDuration;
395 | }
396 |
397 | if(isMoveEnd) {
398 | this.hasScrollToBottomBefore = true;
399 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
400 | }
401 |
402 | } else {
403 | this.hasScrollToBottomBefore = false;
404 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
405 | }
406 |
407 |
408 | console.log({
409 | y: y,
410 | endY: this.endY,
411 |
412 | scrollTop: event.scrollTop,
413 | maxScrollTop: maxScrollTop,
414 | footerHeight: this.footerHeight,
415 | }, isMoveEnd);
416 |
417 |
418 |
419 |
420 |
421 | //if previous and current y are the same, no need to continue
422 | if (this.yPrev !== y) {
423 | //this.modifyDom(y, duration, event.domWrite);
424 |
425 | let yDiff = y - this.yPrev;
426 | if(-30 < yDiff && yDiff < 30) {
427 | this.modifyDom(y, duration, event.domWrite);
428 | } else {
429 | this.modifyDom(y, 200, event.domWrite);
430 | }
431 | }
432 |
433 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
434 |
435 |
436 | this.yPrev = y;
437 | this.scrollTopPrev = event.scrollTop;
438 | }
439 |
440 |
441 |
442 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
443 | dowWrite(() => {
444 |
445 | // Header Items
446 | this.headerItems.forEach((item) => {
447 |
448 | let dy = constrain((y - item.startY), 0, (this.endY - item.startY));
449 |
450 | if(item.type === ItemType.Tabbar) {
451 | var val = constrain((dy / this.content._tabbarHeight), 0, 1);
452 |
453 | console.log("val:", val);
454 |
455 | let height = this.content._tabbarHeight * (1 - val);
456 | if(height >= 0) {
457 | $(item.ele).css("height", height + "px");
458 | $(item.ele).css("display", "");
459 | } else {
460 | $(item.ele).css("display", "none");
461 | }
462 |
463 | } else {
464 | var val = constrain((dy / item.ele.offsetHeight), 0, 1);
465 | //var val = constrain((dy / item.height), 0, 1);
466 |
467 | this.translateElementY(item.ele, -dy, duration);
468 | /*if(dy <= item.endY) {
469 | $(item.ele).css({
470 | height: item.height - dy,
471 | minHeight: item.height - dy,
472 | })
473 | } else {
474 | this.translateElementY(item.ele, -dy, duration);
475 | }*/
476 | }
477 | if(item.shrinkable) {
478 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
479 | let fadeAmt = 1 - val;
480 |
481 | $(item.ele).children().not(".toolbar-background")
482 | .each((index, navbarChild) => {
483 | $(navbarChild).css("opacity", fadeAmt);
484 | });
485 | }
486 | });
487 |
488 |
489 |
490 | // Footer Items
491 | this.footerItems.forEach((item) => {
492 | this.translateElementY(item.ele, y, duration);
493 | });
494 |
495 |
496 | // Content
497 | /*if(y <= this.endY) {
498 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - y) + "px");
499 | } else {
500 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - this.endY) + "px");
501 | }
502 |
503 | if(y <= this.footerHeight) {
504 | this.content.setScrollElementStyle("margin-bottom", (this.footerHeight - y) + "px");
505 | }*/
506 | });
507 | }
508 |
509 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
510 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
511 |
512 | if (duration && !ele.style.transitionDuration) {
513 | ele.style.transitionDuration = duration + "ms";
514 | setTimeout(() => {
515 | ele.style.transitionDuration = "";
516 | }, this.defaultDelay);
517 | }
518 | }
519 | }
520 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v2.1.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 | /*
14 |
15 |
16 | ion-header {
17 | pointer-events: none;
18 | }
19 | ion-header > * {
20 | pointer-events: all;
21 | }
22 |
23 |
24 |
25 |
26 | ion-header > ion-navbar {
27 | position: relative;
28 | }
29 |
30 |
31 |
32 | ion-tabs > .tabbar {
33 | overflow: hidden;
34 | }
35 | ion-tabs > .tabbar > *{
36 | align-self: flex-end;
37 | }
38 |
39 | */
40 |
41 |
42 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
43 |
44 |
45 | function constrain(val: number, min: number, max: number) {
46 | return Math.min(max, Math.max(val, min));
47 | }
48 |
49 | function forEach(children: HTMLCollection, callback: (ele: HTMLElement) => void) {
50 | for(var i = 0; i < children.length; i++) {
51 | callback( children[i]);
52 | }
53 | }
54 |
55 |
56 | /*
57 | interface Item {
58 | ele: HTMLElement;
59 | startY: number;
60 | endY: number;
61 | unshrinkable: boolean;
62 | }
63 | */
64 |
65 |
66 | enum ItemType {
67 | Navbar, Toolbar, Tabbar
68 | }
69 | class Item {
70 | public type: ItemType;
71 | public height: number;
72 | public shrinkable: boolean;
73 | public scrollShrinkVal: number;
74 |
75 | public startY = 0;
76 | public endY = 0;
77 |
78 | constructor(public ele: HTMLElement) {
79 |
80 | if(ele.classList.contains("tabbar")) {
81 | this.type = ItemType.Tabbar;
82 | } else if(ele.tagName === "ION-NAVBAR") {
83 | this.type = ItemType.Navbar;
84 | } else {
85 | this.type = ItemType.Toolbar;
86 | }
87 |
88 | this.height = ele.offsetHeight;
89 | this.shrinkable = !ele.classList.contains("unshrinkable");
90 |
91 | var scrollShrinkVal = parseFloat($(ele).attr("scroll-hide-val"));
92 | if(this.shrinkable) {
93 | if(isNaN(scrollShrinkVal)) scrollShrinkVal = 1;
94 | } else {
95 | scrollShrinkVal = 0;
96 | }
97 | this.scrollShrinkVal = scrollShrinkVal;
98 | }
99 |
100 | setY(startY: number, endY: number) {
101 | this.startY = startY;
102 | this.endY = endY;
103 | }
104 |
105 | setStartY(startY: number) {
106 | this.startY = startY;
107 | }
108 | setEndY(endY: number) {
109 | this.endY = endY;
110 | }
111 |
112 | }
113 |
114 |
115 | @Directive({
116 | selector: '[scroll-hide]' // Attribute selector
117 | })
118 | export class ScrollHide implements OnInit, OnDestroy {
119 |
120 | @Input() navCtrl: NavController;
121 | @Input() viewCtrl: ViewController;
122 |
123 |
124 | headerItems: Item[] = [];
125 | footerItems: Item[] = [];
126 |
127 | headerEle: HTMLElement;
128 | footerEle: HTMLElement;
129 |
130 | //---------------------------------
131 |
132 | defaultDelay: number = 400 * 2;
133 | defaultDuration: number = 400;
134 |
135 |
136 | headerHeight: number;
137 | footerHeight: number;
138 | endY: number;
139 |
140 | defaultEnd: number = 0;
141 | y: number = 0;
142 | yPrev: number = 0;
143 | scrollTopPrev: number = 0;
144 |
145 | //---------------------------------
146 |
147 |
148 | constructor(private content: Content,
149 | private renderer: Renderer,
150 | private zone: NgZone) {}
151 |
152 |
153 | ngOnInit() {
154 | this.content.fullscreen = true;
155 |
156 |
157 | //console.log("ngOnInit() > navCtrl:", this.navCtrl);
158 | //console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
159 |
160 |
161 | this.viewCtrl.didEnter.subscribe(() => {
162 | //console.log("ScrollHide > viewCtrl.didEnter(), _tabsPlacement:", this.content._tabsPlacement);
163 | console.log("init()");
164 | this.init();
165 | });
166 | this.viewCtrl.willLeave.subscribe(() => {
167 | console.log("resetStyle()");
168 | this.resetStyle();
169 | });
170 |
171 |
172 | this.content.ionScroll.subscribe(event => {
173 | this.onContentScroll(event, false);
174 | });
175 | this.content.ionScrollEnd.subscribe(event => {
176 | console.log("ionScrollEnd!");
177 | this.onContentScroll(event, true);
178 | });
179 |
180 |
181 |
182 | let win: any = window;
183 | win.temp = win.temp || {};
184 | win.temp.content = this.content;
185 | win.temp.zone = this.zone;
186 | }
187 |
188 | ngOnDestroy() {
189 | console.log("ngOnDestroy()");
190 | }
191 |
192 | private init() {
193 | this.y = 0;
194 | this.yPrev = 0;
195 | this.scrollTopPrev = this.content.scrollTop;
196 |
197 |
198 |
199 | this.content.resize();
200 |
201 |
202 | let contentEle = this.content.getNativeElement();
203 | let headerEle = contentEle.parentElement.querySelector("ion-header");
204 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
205 |
206 | if(headerEle) {
207 | this.headerItems = $(headerEle)
208 | .children()
209 | .toArray().map(ele => new Item(ele));
210 | } else {
211 | this.headerItems = [];
212 | }
213 |
214 | if(footerEle) {
215 | this.footerItems = $(footerEle)
216 | .children()
217 | .toArray().map(ele => new Item(ele));
218 | } else {
219 | this.footerItems = [];
220 | }
221 |
222 |
223 |
224 |
225 | let hasTabbar = (this.content._tabs !== null);
226 | if(hasTabbar) {
227 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
228 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
229 |
230 | if(this.content._tabsPlacement === "bottom") {
231 | this.footerItems.push(new Item(tabbarEle));
232 | } else {
233 | this.headerItems.push(new Item(tabbarEle));
234 | }
235 | }
236 |
237 |
238 | this.headerItems = this.headerItems.reverse();
239 | this.footerItems = this.footerItems.reverse();
240 |
241 |
242 |
243 |
244 | var headerHeight = 0,
245 | footerHeight = 0;
246 |
247 | console.group("headerItems")
248 |
249 | var shrinkableHeightSum = 0;
250 | this.headerItems.forEach((item, index) => {
251 | headerHeight += item.ele.offsetHeight;
252 |
253 | // Navbar would not be raised without setting position to relative
254 | if(item.type === ItemType.Navbar) {
255 | $(item.ele).css("position", "relative")
256 | }
257 |
258 |
259 | // Prevent overlapping of bottom element
260 | if(item.type === ItemType.Tabbar) {
261 | $(item.ele).css("overflow", "hidden");
262 | $(item.ele).children().css("align-self", "flex-end");
263 | } else {
264 | $(item.ele).css("z-index", index + 1);
265 | }
266 |
267 |
268 | // For shrinking
269 | $(item.ele).css("padding-top", "0");
270 | $(item.ele).css("padding-bottom", "0");
271 |
272 |
273 |
274 | if(item.shrinkable) {
275 | item.setStartY(shrinkableHeightSum);
276 | //item.setEndY(shrinkableHeightSum + (item.height * (1 - item.scrollShrinkVal)));
277 | //shrinkableHeightSum += item.height * (1 - item.scrollShrinkVal);
278 | item.setEndY(shrinkableHeightSum + (item.height * (item.scrollShrinkVal)));
279 | shrinkableHeightSum += item.height * item.scrollShrinkVal;
280 | //item.setEndY(shrinkableHeightSum + shrinkHeight);
281 | //shrinkableHeightSum += shrinkHeight;
282 | } else {
283 | item.setStartY(shrinkableHeightSum);
284 | item.setEndY(shrinkableHeightSum);
285 | //shrinkableHeightSum += item.height;
286 | }
287 |
288 | console.log(item, item.ele.offsetHeight);
289 | });
290 | this.headerHeight = headerHeight;
291 | this.defaultEnd = this.headerHeight * 2;
292 | this.endY = shrinkableHeightSum;
293 |
294 | console.groupEnd();
295 |
296 |
297 | console.group("footerItems")
298 | this.footerItems.forEach(item => {
299 | footerHeight += item.ele.offsetHeight;
300 | console.log(item.ele, item.ele.offsetHeight);
301 | });
302 | this.footerHeight = footerHeight;
303 | console.groupEnd();
304 |
305 | console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
306 |
307 |
308 | /*
309 | ion-header {
310 | pointer-events: none;
311 | }
312 |
313 | ion-header > * {
314 | pointer-events: all;
315 | }*/
316 |
317 | if(headerEle) {
318 | $(headerEle).css("pointer-events", "none");
319 | $(headerEle).children().css("pointer-events", "all");
320 | }
321 | if(footerEle) {
322 | $(footerEle).css("pointer-events", "none");
323 | $(footerEle).children().css("pointer-events", "all");
324 | }
325 |
326 | this.headerEle = headerEle;
327 | this.footerEle = footerEle;
328 | }
329 |
330 |
331 | private resetStyle() {
332 | this.headerItems.forEach((item, index) => {
333 |
334 | if(item.type === ItemType.Navbar) {
335 | $(item.ele).css("position", "");
336 | }
337 |
338 | if(item.type === ItemType.Tabbar) {
339 | $(item.ele).css("overflow", "");
340 | $(item.ele).children().css("align-self", "center");
341 | } else {
342 | $(item.ele).css("z-index", "");
343 | }
344 |
345 | });
346 |
347 | if(this.headerEle) {
348 | $(this.headerEle).css("pointer-events", "none");
349 | $(this.headerEle).children().css("pointer-events", "all");
350 | }
351 | if(this.footerEle) {
352 | $(this.footerEle).css("pointer-events", "none");
353 | $(this.footerEle).children().css("pointer-events", "all");
354 | }
355 |
356 |
357 | // Header Items
358 | this.headerItems.forEach((item) => {
359 |
360 | $(item.ele).css("height", "");
361 | $(item.ele).css("display", "");
362 | $(item.ele).css("webkitTransform", "");
363 |
364 | if(item.shrinkable) {
365 | $(item.ele).children().not(".toolbar-background")
366 | .each((index, navbarChild) => {
367 | $(navbarChild).css("opacity", "");
368 | });
369 | }
370 | });
371 |
372 |
373 | // Footer Items
374 | this.footerItems.forEach((item) => {
375 | $(item.ele).css("webkitTransform", "");
376 | });
377 | }
378 |
379 |
380 |
381 |
382 |
383 |
384 | hasScrollToBottomBefore = false;
385 |
386 |
387 | //@HostListener("ionScroll", ["$event", "false"])
388 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
389 | //console.log("ionScroll", event); //{ contentHeight: event.contentHeight, scrollHeight: event.scrollHeight });
390 | //this.checkTabsPlacment();
391 |
392 | /*if(!this.initiated) {
393 | this.initiated = true;
394 | this.init();
395 | this.content.resize();
396 | }*/
397 |
398 |
399 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
400 |
401 |
402 |
403 | var duration = 0;
404 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
405 |
406 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
407 |
408 |
409 |
410 | //if we are at the bottom, animate the header/tabs back in
411 | if (event.scrollTop >= maxScrollTop - this.footerHeight) {
412 |
413 | if(this.hasScrollToBottomBefore) {
414 | y = 0;
415 | duration = this.defaultDuration;
416 | }
417 |
418 | if(isMoveEnd) {
419 | this.hasScrollToBottomBefore = true;
420 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
421 | }
422 |
423 | } else {
424 | this.hasScrollToBottomBefore = false;
425 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
426 | }
427 |
428 |
429 | /*console.log({
430 | y: y,
431 | endY: this.endY,
432 |
433 | scrollTop: event.scrollTop,
434 | maxScrollTop: maxScrollTop,
435 | footerHeight: this.footerHeight,
436 | }, isMoveEnd);*/
437 |
438 |
439 |
440 |
441 |
442 | //if previous and current y are the same, no need to continue
443 | if (this.yPrev !== y) {
444 | //this.modifyDom(y, duration, event.domWrite);
445 |
446 | let yDiff = y - this.yPrev;
447 | if(-30 < yDiff && yDiff < 30) {
448 | this.modifyDom(y, duration, event.domWrite);
449 | } else {
450 | this.modifyDom(y, 200, event.domWrite);
451 | }
452 | }
453 |
454 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
455 |
456 |
457 | this.yPrev = y;
458 | this.scrollTopPrev = event.scrollTop;
459 | }
460 |
461 |
462 |
463 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
464 | dowWrite(() => {
465 | y = constrain(y, 0, this.endY);
466 |
467 |
468 | console.group();
469 |
470 |
471 | // Header Items
472 | this.headerItems.forEach((item) => {
473 |
474 | //let dy = constrain((y - item.startY), 0, Infinity);
475 | //let val = constrain((dy / item.height), 0, 1);
476 |
477 | if(item.type === ItemType.Tabbar) {
478 | let dy = constrain((y - item.startY), 0, Infinity);
479 | let val = constrain((dy / item.height), 0, 1);
480 |
481 | //var val = constrain((dy / this.content._tabbarHeight), 0, 1);
482 |
483 | console.log("val:", val);
484 |
485 | let height = this.content._tabbarHeight * (1 - val);
486 | if(height >= 0) {
487 | $(item.ele).css("height", height + "px");
488 | $(item.ele).css("display", "");
489 | } else {
490 | $(item.ele).css("display", "none");
491 | }
492 |
493 | } else {
494 | let dy_shrink = constrain((y - item.startY), 0, item.endY - item.startY);
495 | let dy_translate = constrain((y - item.endY), 0, Infinity);
496 |
497 | //var val = constrain((dy / item.height), 0, 1);
498 |
499 | //this.translateElementY(item.ele, -dy, duration);
500 | //if(item.scrollShrinkVal === 0.3)
501 | console.log(y, "shrink:", dy_shrink, "tranalate:", dy_translate);
502 |
503 | if(y <= item.endY) {
504 | let val = constrain((dy_shrink / item.height), 0, 1);
505 | $(item.ele).css({
506 | height: item.height - dy_shrink,
507 | minHeight: item.height - dy_shrink,
508 | marginBottom: dy_shrink,
509 | })
510 | this.translateElementY(item.ele, 0, duration);
511 | } else {
512 | //this.translateElementY(item.ele, -dy, duration);
513 | let val = constrain((dy_translate / item.height), 0, 1);
514 |
515 | $(item.ele).css({
516 | height: item.height - (item.endY - item.startY),
517 | minHeight: item.height - (item.endY - item.startY),
518 | marginBottom: (item.endY - item.startY),
519 | })
520 | this.translateElementY(item.ele, -dy_translate, duration);
521 | }
522 | }
523 | /*if(item.shrinkable) {
524 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
525 | let fadeAmt = 1 - val;
526 |
527 | $(item.ele).children().not(".toolbar-background")
528 | .each((index, navbarChild) => {
529 | $(navbarChild).css("opacity", fadeAmt);
530 | });
531 | }*/
532 | });
533 | console.groupEnd();
534 |
535 |
536 | // Footer Items
537 | this.footerItems.forEach((item) => {
538 | this.translateElementY(item.ele, y, duration);
539 | });
540 |
541 |
542 | // Content
543 | /*if(y <= this.endY) {
544 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - y) + "px");
545 | } else {
546 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - this.endY) + "px");
547 | }
548 |
549 | if(y <= this.footerHeight) {
550 | this.content.setScrollElementStyle("margin-bottom", (this.footerHeight - y) + "px");
551 | }*/
552 | });
553 | }
554 |
555 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
556 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
557 |
558 | if (duration && !ele.style.transitionDuration) {
559 | ele.style.transitionDuration = duration + "ms";
560 | setTimeout(() => {
561 | ele.style.transitionDuration = "";
562 | }, this.defaultDelay);
563 | }
564 | }
565 | }
566 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v2.2.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 | /*
14 |
15 |
16 | ion-header {
17 | pointer-events: none;
18 | }
19 | ion-header > * {
20 | pointer-events: all;
21 | }
22 |
23 |
24 |
25 |
26 | ion-header > ion-navbar {
27 | position: relative;
28 | }
29 |
30 |
31 |
32 | ion-tabs > .tabbar {
33 | overflow: hidden;
34 | }
35 | ion-tabs > .tabbar > *{
36 | align-self: flex-end;
37 | }
38 |
39 | */
40 |
41 |
42 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
43 |
44 |
45 | function constrain(val: number, min: number, max: number) {
46 | return Math.min(max, Math.max(val, min));
47 | }
48 |
49 |
50 |
51 |
52 |
53 | enum ItemType {
54 | Navbar, Toolbar, Tabbar
55 | }
56 | enum ItemTransition {
57 | Static, Translate, Shrink
58 | }
59 |
60 | /*
61 | * startY EndY
62 | * shrinkStart TransitionStart MoveStart
63 | * | ------------- | ---------------- | ---------->
64 | * Shrink Translate
65 | */
66 | class Item {
67 | public type: ItemType;
68 | public height: number;
69 |
70 | //public static: boolean;
71 | public transition: ItemTransition;
72 | public scrollShrinkVal: number = 0;
73 |
74 | public shrinkStart = 0;
75 | public translateStart = 0;
76 | public moveStart = 0;
77 |
78 | constructor(public ele: HTMLElement) {
79 |
80 | if(ele.classList.contains("tabbar")) {
81 | this.type = ItemType.Tabbar;
82 | } else if(ele.tagName === "ION-NAVBAR") {
83 | this.type = ItemType.Navbar;
84 | } else {
85 | this.type = ItemType.Toolbar;
86 | }
87 |
88 | this.height = ele.offsetHeight;
89 |
90 |
91 | let shrinkable = ele.hasAttribute("scroll-hide-shrink");
92 | let translatable = ele.hasAttribute("scroll-hide-translate");
93 |
94 | if(shrinkable) {
95 | this.transition = ItemTransition.Shrink;
96 | var scrollShrinkVal = parseFloat(ele.getAttribute("scroll-hide-shrink"));
97 | if(isNaN(scrollShrinkVal)) scrollShrinkVal = 1;
98 | this.scrollShrinkVal = scrollShrinkVal;
99 |
100 | } else if(translatable) {
101 | this.transition = ItemTransition.Translate;
102 |
103 | } else {
104 | this.transition = ItemTransition.Static;
105 |
106 | }
107 |
108 |
109 | /*if(this.type === ItemType.Tabbar) {
110 | this.transition = ItemTransition.Shrink;
111 | this.scrollShrinkVal = 0.5;
112 |
113 | //this.transition = ItemTransition.Translate;
114 |
115 | //this.transition = ItemTransition.Static;
116 | }*/
117 | }
118 |
119 | setY(shrinkStart: number, translateStart: number) {
120 | this.shrinkStart = shrinkStart;
121 | this.translateStart = translateStart;
122 | }
123 |
124 | setShrinkStart(shrinkStart: number) {
125 | this.shrinkStart = shrinkStart;
126 | }
127 | setTranslateStart(translateStart: number) {
128 | this.translateStart = translateStart;
129 | }
130 | setMoveStart(moveStart: number) {
131 | this.moveStart = moveStart;
132 | }
133 |
134 | }
135 |
136 |
137 | @Directive({
138 | selector: '[scroll-hide]' // Attribute selector
139 | })
140 | export class ScrollHide implements OnInit, OnDestroy {
141 |
142 | @Input() navCtrl: NavController;
143 | @Input() viewCtrl: ViewController;
144 |
145 |
146 | headerItems: Item[] = [];
147 | footerItems: Item[] = [];
148 |
149 | headerEle: HTMLElement;
150 | footerEle: HTMLElement;
151 |
152 | //---------------------------------
153 |
154 | defaultDelay: number = 400 * 2;
155 | defaultDuration: number = 400;
156 |
157 |
158 | headerHeight: number;
159 | footerHeight: number;
160 | endY: number;
161 |
162 | defaultEnd: number = 0;
163 | y: number = 0;
164 | yPrev: number = 0;
165 | scrollTopPrev: number = 0;
166 |
167 | //---------------------------------
168 |
169 |
170 | constructor(private content: Content,
171 | private renderer: Renderer,
172 | private zone: NgZone) {}
173 |
174 |
175 | ngOnInit() {
176 | this.content.fullscreen = true;
177 |
178 |
179 | //console.log("ngOnInit() > navCtrl:", this.navCtrl);
180 | //console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
181 |
182 |
183 | this.viewCtrl.didEnter.subscribe(() => {
184 | //console.log("ScrollHide > viewCtrl.didEnter(), _tabsPlacement:", this.content._tabsPlacement);
185 | console.log("init()");
186 |
187 | this.init();
188 |
189 | });
190 | this.viewCtrl.willLeave.subscribe(() => {
191 | console.log("resetStyle()");
192 | this.resetStyle();
193 | });
194 |
195 |
196 | this.content.ionScroll.subscribe(event => {
197 | this.onContentScroll(event, false);
198 | });
199 | this.content.ionScrollEnd.subscribe(event => {
200 | console.log("ionScrollEnd!");
201 | this.onContentScroll(event, true);
202 | });
203 |
204 |
205 |
206 | let win: any = window;
207 | win.temp = win.temp || {};
208 | win.temp.content = this.content;
209 | win.temp.zone = this.zone;
210 | }
211 |
212 | ngOnDestroy() {
213 | console.log("ngOnDestroy()");
214 | }
215 |
216 | private init() {
217 | this.y = 0;
218 | this.yPrev = 0;
219 | this.scrollTopPrev = this.content.scrollTop;
220 |
221 |
222 |
223 | this.content.resize();
224 |
225 |
226 | let contentEle = this.content.getNativeElement();
227 | let headerEle = contentEle.parentElement.querySelector("ion-header");
228 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
229 |
230 | if(headerEle) {
231 | this.headerItems = $(headerEle)
232 | .children()
233 | .toArray().map(ele => new Item(ele));
234 | } else {
235 | this.headerItems = [];
236 | }
237 |
238 | if(footerEle) {
239 | this.footerItems = $(footerEle)
240 | .children()
241 | .toArray().map(ele => new Item(ele));
242 | } else {
243 | this.footerItems = [];
244 | }
245 |
246 |
247 |
248 |
249 | let hasTabbar = (this.content._tabs !== null);
250 | if(hasTabbar) {
251 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
252 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
253 |
254 | if(this.content._tabsPlacement === "bottom") {
255 | this.footerItems.push(new Item(tabbarEle));
256 | } else {
257 | this.headerItems.push(new Item(tabbarEle));
258 | }
259 | }
260 |
261 |
262 | this.headerItems = this.headerItems.reverse();
263 | this.footerItems = this.footerItems.reverse();
264 |
265 |
266 |
267 |
268 | var headerHeight = 0,
269 | footerHeight = 0;
270 |
271 | console.group("headerItems")
272 |
273 | var shrinkableHeightSum = 0;
274 | this.headerItems.forEach((item, index) => {
275 | headerHeight += item.ele.offsetHeight;
276 |
277 | // Navbar would not be raised without setting position to relative
278 | if(item.type === ItemType.Navbar) {
279 | $(item.ele).css("position", "relative")
280 | }
281 |
282 |
283 | // Prevent overlapping of bottom element
284 | if(item.type === ItemType.Tabbar) {
285 | $(item.ele).css("overflow", "hidden");
286 | $(item.ele).children().css("align-self", "flex-end");
287 | } else {
288 | $(item.ele).css("z-index", index + 1);
289 | }
290 |
291 |
292 | // For shrinking
293 | //$(item.ele).css("padding-top", "0");
294 | //$(item.ele).css("padding-bottom", "0");
295 |
296 |
297 | switch(item.transition) {
298 | case ItemTransition.Shrink:
299 | let shrinkHeight = (item.height * item.scrollShrinkVal);
300 | item.setShrinkStart(shrinkableHeightSum);
301 | item.setTranslateStart(shrinkableHeightSum + shrinkHeight);
302 | item.setMoveStart(shrinkableHeightSum + shrinkHeight);
303 | shrinkableHeightSum += shrinkHeight;
304 | break;
305 |
306 | case ItemTransition.Translate:
307 | item.setShrinkStart(shrinkableHeightSum);
308 | item.setTranslateStart(shrinkableHeightSum);
309 | item.setMoveStart(shrinkableHeightSum + item.height);
310 | shrinkableHeightSum += item.height;
311 | break;
312 |
313 | case ItemTransition.Static:
314 | item.setShrinkStart(shrinkableHeightSum);
315 | item.setTranslateStart(shrinkableHeightSum);
316 | item.setMoveStart(shrinkableHeightSum);
317 | break;
318 | }
319 |
320 | console.log(item, item.ele.offsetHeight);
321 | });
322 | this.headerHeight = headerHeight;
323 | this.defaultEnd = this.headerHeight * 2;
324 | this.endY = shrinkableHeightSum;
325 |
326 | console.groupEnd();
327 |
328 |
329 | console.group("footerItems")
330 | this.footerItems.forEach(item => {
331 | footerHeight += item.ele.offsetHeight;
332 | console.log(item.ele, item.ele.offsetHeight);
333 | });
334 | this.footerHeight = footerHeight;
335 | console.groupEnd();
336 |
337 | console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
338 |
339 |
340 | /*
341 | ion-header {
342 | pointer-events: none;
343 | }
344 |
345 | ion-header > * {
346 | pointer-events: all;
347 | }*/
348 |
349 | if(headerEle) {
350 | $(headerEle).css("pointer-events", "none");
351 | $(headerEle).children().css("pointer-events", "all");
352 | }
353 | if(footerEle) {
354 | $(footerEle).css("pointer-events", "none");
355 | $(footerEle).children().css("pointer-events", "all");
356 | }
357 |
358 | this.headerEle = headerEle;
359 | this.footerEle = footerEle;
360 | }
361 |
362 |
363 | private resetStyle() {
364 | this.headerItems.forEach((item, index) => {
365 |
366 | if(item.type === ItemType.Navbar) {
367 | $(item.ele).css("position", "");
368 | }
369 |
370 | if(item.type === ItemType.Tabbar) {
371 | $(item.ele).css("overflow", "");
372 | $(item.ele).children().css("align-self", "center");
373 | } else {
374 | $(item.ele).css("z-index", "");
375 | }
376 |
377 | });
378 |
379 | if(this.headerEle) {
380 | $(this.headerEle).css("pointer-events", "none");
381 | $(this.headerEle).children().css("pointer-events", "all");
382 | }
383 | if(this.footerEle) {
384 | $(this.footerEle).css("pointer-events", "none");
385 | $(this.footerEle).children().css("pointer-events", "all");
386 | }
387 |
388 |
389 | // Header Items
390 | this.headerItems.forEach((item) => {
391 |
392 | $(item.ele).css("height", "");
393 | $(item.ele).css("display", "");
394 | $(item.ele).css("webkitTransform", "");
395 |
396 | if(item.transition === ItemTransition.Translate) {
397 | $(item.ele).children().not(".toolbar-background")
398 | .each((index, navbarChild) => {
399 | $(navbarChild).css("opacity", "");
400 | });
401 | }
402 | });
403 |
404 |
405 | // Footer Items
406 | this.footerItems.forEach((item) => {
407 | $(item.ele).css("webkitTransform", "");
408 | });
409 | }
410 |
411 |
412 |
413 |
414 |
415 |
416 | hasScrollToBottomBefore = false;
417 |
418 |
419 | //@HostListener("ionScroll", ["$event", "false"])
420 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
421 | //console.log("ionScroll", event); //{ contentHeight: event.contentHeight, scrollHeight: event.scrollHeight });
422 | //this.checkTabsPlacment();
423 |
424 | /*if(!this.initiated) {
425 | this.initiated = true;
426 | this.init();
427 | this.content.resize();
428 | }*/
429 |
430 |
431 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
432 |
433 |
434 |
435 | var duration = 0;
436 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
437 |
438 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
439 |
440 |
441 |
442 | //if we are at the bottom, animate the header/tabs back in
443 | if (event.scrollTop >= maxScrollTop - this.footerHeight) {
444 |
445 | if(this.hasScrollToBottomBefore) {
446 | y = 0;
447 | duration = this.defaultDuration;
448 | }
449 |
450 | if(isMoveEnd) {
451 | this.hasScrollToBottomBefore = true;
452 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
453 | }
454 |
455 | } else {
456 | this.hasScrollToBottomBefore = false;
457 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
458 | }
459 |
460 |
461 | console.log({
462 | y: y,
463 | endY: this.endY,
464 |
465 | scrollTop: event.scrollTop,
466 | maxScrollTop: maxScrollTop,
467 | footerHeight: this.footerHeight,
468 | });
469 |
470 |
471 |
472 |
473 | //if previous and current y are the same, no need to continue
474 | if (this.yPrev !== y) {
475 | //this.modifyDom(y, duration, event.domWrite);
476 |
477 | let yDiff = y - this.yPrev;
478 | if(-30 < yDiff && yDiff < 30) {
479 | this.modifyDom(y, duration, event.domWrite);
480 | } else {
481 | this.modifyDom(y, 200, event.domWrite);
482 | }
483 | }
484 |
485 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
486 |
487 |
488 | this.yPrev = y;
489 | this.scrollTopPrev = event.scrollTop;
490 | }
491 |
492 |
493 |
494 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
495 | dowWrite(() => {
496 | y = constrain(y, 0, this.endY);
497 |
498 |
499 | console.group();
500 |
501 |
502 | // Header Items
503 | this.headerItems.forEach((item) => {
504 |
505 | //let dy = constrain((y - item.shrinkStart), 0, Infinity);
506 | //let val = constrain((dy / item.height), 0, 1);
507 |
508 | if(item.type === ItemType.Tabbar) {
509 | let dy_shrink = constrain((y - item.shrinkStart), 0, (item.translateStart - item.shrinkStart));
510 | let dy_translate = constrain((y - item.translateStart), 0, (item.moveStart - item.translateStart));
511 | let dy_move = constrain((y - item.moveStart), 0, Infinity);
512 |
513 | /*let height = this.content._tabbarHeight * (1 - val);
514 | if(height >= 0) {
515 | $(item.ele).css("height", height + "px");
516 | $(item.ele).css("display", "");
517 | } else {
518 | $(item.ele).css("display", "none");
519 | }*/
520 | if(y <= item.translateStart) {
521 | // Shrinking
522 |
523 | let val = constrain((dy_shrink / item.height), 0, 1);
524 | $(item.ele).css({
525 | height: item.height - dy_shrink,
526 | //minHeight: item.height - dy_shrink,
527 | //marginBottom: dy_shrink,
528 | })
529 | this.translateElementY(item.ele, 0, duration);
530 |
531 | } else if(y <= item.moveStart){
532 | // Translating
533 |
534 | let val = constrain((dy_translate / item.height), 0, 1);
535 | $(item.ele).css({
536 | height: item.height - dy_translate,
537 | //minHeight: item.height - (item.translateStart - item.shrinkStart),
538 | //marginBottom: (item.translateStart - item.shrinkStart),
539 | })
540 | this.translateElementY(item.ele, 0, duration);
541 |
542 | } else {
543 | // Moving
544 |
545 | $(item.ele).css({
546 | height: item.height - Math.max((item.translateStart - item.shrinkStart), (item.moveStart - item.translateStart)),
547 | //minHeight: item.height - (item.translateStart - item.shrinkStart),
548 | //marginBottom: (item.translateStart - item.shrinkStart),
549 | })
550 | this.translateElementY(item.ele, -dy_move, duration);
551 | }
552 |
553 | } else {
554 |
555 | let dy_shrink = constrain((y - item.shrinkStart), 0, (item.translateStart - item.shrinkStart));
556 | let dy_translate = constrain((y - item.translateStart), 0, (item.moveStart - item.translateStart));
557 | let dy_move = constrain((y - item.moveStart), 0, Infinity);
558 |
559 | //var val = constrain((dy / item.height), 0, 1);
560 |
561 | //this.translateElementY(item.ele, -dy, duration);
562 | //if(item.scrollShrinkVal === 0.3)
563 | console.log(y, "shrink:", dy_shrink, "tranalate:", dy_translate);
564 |
565 | /*if(y <= item.translateStart) {
566 | // Shrinking
567 |
568 | let val = constrain((dy_shrink / item.height), 0, 1);
569 | $(item.ele).css({
570 | height: item.height - dy_shrink,
571 | minHeight: item.height - dy_shrink,
572 | marginBottom: dy_shrink,
573 | })
574 | this.translateElementY(item.ele, 0, duration);
575 |
576 | } else {
577 | // Translating
578 |
579 | //this.translateElementY(item.ele, -dy, duration);
580 | let val = constrain((dy_translate / item.height), 0, 1);
581 |
582 | $(item.ele).css({
583 | height: item.height - (item.translateStart - item.shrinkStart),
584 | minHeight: item.height - (item.translateStart - item.shrinkStart),
585 | marginBottom: (item.translateStart - item.shrinkStart),
586 | })
587 | this.translateElementY(item.ele, -dy_translate, duration);
588 | }*/
589 |
590 | if(y <= item.translateStart) {
591 | // Shrinking
592 |
593 | let val = constrain((dy_shrink / item.height), 0, 1);
594 | $(item.ele).css({
595 | height: item.height - dy_shrink,
596 | minHeight: item.height - dy_shrink,
597 | marginBottom: dy_shrink,
598 | })
599 | this.translateElementY(item.ele, 0, duration);
600 |
601 | } else if(y <= item.moveStart) {
602 | // Translating
603 |
604 | //this.translateElementY(item.ele, -dy, duration);
605 | let val = constrain((dy_translate / item.height), 0, 1);
606 |
607 | $(item.ele).css({
608 | height: item.height - (item.translateStart - item.shrinkStart),
609 | minHeight: item.height - (item.translateStart - item.shrinkStart),
610 | marginBottom: (item.translateStart - item.shrinkStart),
611 | })
612 | this.translateElementY(item.ele, -dy_translate, duration);
613 |
614 | } else {
615 | // Moving
616 |
617 | let val = constrain((dy_move / item.height), 0, 1);
618 |
619 | let maxPrevVal = Math.max((item.translateStart - item.shrinkStart), (item.moveStart - item.translateStart));
620 | $(item.ele).css({
621 | height: item.height - maxPrevVal,
622 | minHeight: item.height - maxPrevVal,
623 | marginBottom: maxPrevVal,
624 | })
625 | this.translateElementY(item.ele, -dy_move, duration);
626 | }
627 | }
628 | /*if(item.shrinkable) {
629 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
630 | let fadeAmt = 1 - val;
631 |
632 | $(item.ele).children().not(".toolbar-background")
633 | .each((index, navbarChild) => {
634 | $(navbarChild).css("opacity", fadeAmt);
635 | });
636 | }*/
637 | });
638 | console.groupEnd();
639 |
640 |
641 | // Footer Items
642 | this.footerItems.forEach((item) => {
643 | this.translateElementY(item.ele, y, duration);
644 | });
645 |
646 |
647 | // Content
648 | /*if(y <= this.endY) {
649 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - y) + "px");
650 | } else {
651 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - this.endY) + "px");
652 | }
653 |
654 | if(y <= this.footerHeight) {
655 | this.content.setScrollElementStyle("margin-bottom", (this.footerHeight - y) + "px");
656 | }*/
657 | });
658 | }
659 |
660 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
661 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
662 |
663 | if (duration && !ele.style.transitionDuration) {
664 | ele.style.transitionDuration = duration + "ms";
665 | setTimeout(() => {
666 | ele.style.transitionDuration = "";
667 | }, this.defaultDelay);
668 | }
669 | }
670 | }
671 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v3.0.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 |
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 | /*
14 |
15 |
16 | ion-header {
17 | pointer-events: none;
18 | }
19 | ion-header > * {
20 | pointer-events: all;
21 | }
22 |
23 |
24 |
25 |
26 | ion-header > ion-navbar {
27 | position: relative;
28 | }
29 |
30 |
31 |
32 | ion-tabs > .tabbar {
33 | overflow: hidden;
34 | }
35 | ion-tabs > .tabbar > *{
36 | align-self: flex-end;
37 | }
38 |
39 | */
40 |
41 |
42 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
43 |
44 |
45 | function constrain(val: number, min: number, max: number) {
46 | return Math.min(max, Math.max(val, min));
47 | }
48 |
49 |
50 |
51 |
52 |
53 | enum ItemType { Navbar, Toolbar, Tabbar }
54 |
55 | enum TransitionType { Static, Translate, Shrink }
56 | interface Transition {
57 | type: TransitionType;
58 | start: number;
59 | end: number;
60 | interval: number;
61 | }
62 |
63 | /*
64 | * startY EndY
65 | * shrinkStart TransitionStart MoveStart
66 | * | ------------- | ---------------- | ---------->
67 | * Shrink Translate
68 | *
69 | *
70 | * transitionStart transitionEnd
71 | * | ------------- | ---------->
72 | * Shrink
73 | *
74 | * transitionStart transitionEnd
75 | * | ------------- | ---------->
76 | * Translate
77 | *
78 | */
79 | class Item {
80 | public type: ItemType;
81 | public height: number;
82 | //public paddingTop: number;
83 | //public paddingBottom: number;
84 |
85 | //public static: boolean;
86 | public transition: Transition = { type: 0, start: 0, end: 0, interval: 0 };
87 | public scrollShrinkVal: number = 0;
88 |
89 | //public transitionStart = 0;
90 | //public transitionEnd = 0;
91 | //public transitionInterval = 0;
92 |
93 | constructor(public ele: HTMLElement) {
94 |
95 | if(ele.classList.contains("tabbar")) {
96 | this.type = ItemType.Tabbar;
97 | } else if(ele.tagName === "ION-NAVBAR") {
98 | this.type = ItemType.Navbar;
99 | } else {
100 | this.type = ItemType.Toolbar;
101 | }
102 |
103 | this.height = ele.offsetHeight;
104 |
105 |
106 | let shrinkable = ele.hasAttribute("scroll-hide-shrink");
107 | let translatable = ele.hasAttribute("scroll-hide-translate");
108 |
109 | if(shrinkable) {
110 | this.transition.type = TransitionType.Shrink;
111 | var scrollShrinkVal = parseFloat(ele.getAttribute("scroll-hide-shrink"));
112 | if(isNaN(scrollShrinkVal)) scrollShrinkVal = 1;
113 | this.scrollShrinkVal = scrollShrinkVal;
114 |
115 | } else if(translatable) {
116 | this.transition.type = TransitionType.Translate;
117 |
118 | } else {
119 | this.transition.type = TransitionType.Static;
120 |
121 | }
122 |
123 |
124 | if(this.type === ItemType.Tabbar) {
125 | this.transition.type = TransitionType.Shrink;
126 | this.scrollShrinkVal = 0.5;
127 |
128 | //this.transition = ItemTransition.Translate;
129 |
130 | //this.transition = ItemTransition.Static;
131 | }
132 | }
133 |
134 |
135 |
136 | /*setTransitionStart(transitionStart: number) {
137 | this.transitionStart = transitionStart;
138 | }
139 | setTransitionEnd(transitionEnd: number) {
140 | this.transitionEnd = transitionEnd;
141 | }
142 | setTransitionInterval(transitionInterval: number) {
143 | this.transitionInterval = transitionInterval;
144 | }*/
145 |
146 | }
147 |
148 |
149 | @Directive({
150 | selector: '[scroll-hide]' // Attribute selector
151 | })
152 | export class ScrollHide implements OnInit, OnDestroy {
153 |
154 | @Input() navCtrl: NavController;
155 | @Input() viewCtrl: ViewController;
156 |
157 |
158 | headerItems: Item[] = [];
159 | footerItems: Item[] = [];
160 |
161 | headerEle: HTMLElement;
162 | footerEle: HTMLElement;
163 |
164 | //---------------------------------
165 |
166 | defaultDelay: number = 400 * 2;
167 | defaultDuration: number = 400;
168 |
169 |
170 | headerHeight: number;
171 | footerHeight: number;
172 | endY: number;
173 |
174 | defaultEnd: number = 0;
175 | y: number = 0;
176 | yPrev: number = 0;
177 | scrollTopPrev: number = 0;
178 |
179 | //---------------------------------
180 |
181 |
182 | constructor(private content: Content,
183 | private renderer: Renderer,
184 | private zone: NgZone) {}
185 |
186 |
187 | ngOnInit() {
188 | this.content.fullscreen = true;
189 |
190 |
191 | //console.log("ngOnInit() > navCtrl:", this.navCtrl);
192 | //console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
193 |
194 |
195 | this.viewCtrl.didEnter.subscribe(() => {
196 | //console.log("ScrollHide > viewCtrl.didEnter(), _tabsPlacement:", this.content._tabsPlacement);
197 | console.log("init()");
198 |
199 | this.init();
200 |
201 | });
202 | this.viewCtrl.willLeave.subscribe(() => {
203 | console.log("resetStyle()");
204 | this.resetStyle();
205 | });
206 |
207 |
208 | this.content.ionScroll.subscribe(event => {
209 | this.onContentScroll(event, false);
210 | });
211 | this.content.ionScrollEnd.subscribe(event => {
212 | console.log("ionScrollEnd!");
213 | this.onContentScroll(event, true);
214 | });
215 |
216 |
217 |
218 | let win: any = window;
219 | win.temp = win.temp || {};
220 | win.temp.content = this.content;
221 | win.temp.zone = this.zone;
222 | }
223 |
224 | ngOnDestroy() {
225 | console.log("ngOnDestroy()");
226 | }
227 |
228 |
229 |
230 | private init() {
231 | this.y = 0;
232 | this.yPrev = 0;
233 | this.scrollTopPrev = this.content.scrollTop;
234 |
235 |
236 |
237 | this.content.resize();
238 |
239 |
240 | let contentEle = this.content.getNativeElement();
241 | let headerEle = contentEle.parentElement.querySelector("ion-header");
242 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
243 |
244 | if(headerEle) {
245 | this.headerItems = $(headerEle)
246 | .children()
247 | .toArray().map(ele => new Item(ele));
248 | } else {
249 | this.headerItems = [];
250 | }
251 |
252 | if(footerEle) {
253 | this.footerItems = $(footerEle)
254 | .children()
255 | .toArray().map(ele => new Item(ele));
256 | } else {
257 | this.footerItems = [];
258 | }
259 |
260 |
261 |
262 |
263 | let hasTabbar = (this.content._tabs !== null);
264 | if(hasTabbar) {
265 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
266 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
267 |
268 | if(this.content._tabsPlacement === "bottom") {
269 | this.footerItems.push(new Item(tabbarEle));
270 | } else {
271 | this.headerItems.push(new Item(tabbarEle));
272 | }
273 | }
274 |
275 |
276 | this.headerItems = this.headerItems.reverse();
277 | this.footerItems = this.footerItems.reverse();
278 |
279 |
280 |
281 |
282 | var headerHeight = 0,
283 | footerHeight = 0;
284 |
285 | console.group("headerItems")
286 |
287 | var shrinkableHeightSum = 0;
288 | this.headerItems.forEach((item, index) => {
289 | headerHeight += item.ele.offsetHeight;
290 |
291 | // Navbar would not be raised without setting position to relative
292 | if(item.type === ItemType.Navbar) {
293 | $(item.ele).css("position", "relative")
294 | }
295 |
296 |
297 | // Prevent overlapping of bottom element
298 | if(item.type === ItemType.Tabbar) {
299 | $(item.ele).css("overflow", "hidden");
300 | $(item.ele).children().css("align-self", "flex-end");
301 | } else {
302 | $(item.ele).css("z-index", index + 1);
303 | }
304 |
305 |
306 | // For shrinking
307 | //$(item.ele).css("padding-top", "0");
308 | //$(item.ele).css("padding-bottom", "0");
309 |
310 |
311 | $(item.ele).css("min-height", "0");
312 | $(item.ele).css("height", item.height + "px");
313 |
314 |
315 | switch(item.transition.type) {
316 |
317 | case TransitionType.Shrink:
318 |
319 | let shrinkHeight = (item.height * item.scrollShrinkVal);
320 | item.transition.start = (shrinkableHeightSum);
321 | item.transition.end = (shrinkableHeightSum + shrinkHeight);
322 | item.transition.interval = (shrinkHeight);
323 | shrinkableHeightSum += (shrinkHeight);
324 | break;
325 |
326 | case TransitionType.Translate:
327 |
328 | item.transition.start = (shrinkableHeightSum);
329 | item.transition.end = (shrinkableHeightSum + item.height);
330 | item.transition.interval = (item.height);
331 | shrinkableHeightSum += (item.height);
332 | break;
333 |
334 | case TransitionType.Static:
335 |
336 | item.transition.start = (shrinkableHeightSum);
337 | item.transition.end = (shrinkableHeightSum);
338 | item.transition.interval = (0);
339 | break;
340 | }
341 |
342 | console.log(item, item.ele.offsetHeight);
343 | });
344 | this.headerHeight = headerHeight;
345 | this.defaultEnd = this.headerHeight * 2;
346 | this.endY = shrinkableHeightSum;
347 |
348 | console.groupEnd();
349 |
350 |
351 | console.group("footerItems")
352 | this.footerItems.forEach(item => {
353 | footerHeight += item.ele.offsetHeight;
354 | console.log(item.ele, item.ele.offsetHeight);
355 | });
356 | this.footerHeight = footerHeight;
357 | console.groupEnd();
358 |
359 | console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
360 |
361 |
362 | /*
363 | ion-header {
364 | pointer-events: none;
365 | }
366 |
367 | ion-header > * {
368 | pointer-events: all;
369 | }*/
370 |
371 | if(headerEle) {
372 | $(headerEle).css("pointer-events", "none");
373 | $(headerEle).children().css("pointer-events", "all");
374 | }
375 | if(footerEle) {
376 | $(footerEle).css("pointer-events", "none");
377 | $(footerEle).children().css("pointer-events", "all");
378 | }
379 |
380 | this.headerEle = headerEle;
381 | this.footerEle = footerEle;
382 |
383 |
384 |
385 | }
386 |
387 |
388 | private resetStyle() {
389 | this.headerItems.forEach((item, index) => {
390 |
391 | if(item.type === ItemType.Navbar) {
392 | $(item.ele).css("position", "");
393 | }
394 |
395 | if(item.type === ItemType.Tabbar) {
396 | $(item.ele).css("overflow", "");
397 | $(item.ele).children().css("align-self", "center");
398 | } else {
399 | $(item.ele).css("z-index", "");
400 | }
401 |
402 | });
403 |
404 | if(this.headerEle) {
405 | $(this.headerEle).css("pointer-events", "none");
406 | $(this.headerEle).children().css("pointer-events", "all");
407 | }
408 | if(this.footerEle) {
409 | $(this.footerEle).css("pointer-events", "none");
410 | $(this.footerEle).children().css("pointer-events", "all");
411 | }
412 |
413 |
414 | // Header Items
415 | this.headerItems.forEach((item) => {
416 |
417 | $(item.ele).css("height", "");
418 | $(item.ele).css("display", "");
419 | $(item.ele).css("webkitTransform", "");
420 |
421 | if(item.transition.type === TransitionType.Translate) {
422 | $(item.ele).children().not(".toolbar-background")
423 | .each((index, navbarChild) => {
424 | $(navbarChild).css("opacity", "");
425 | });
426 | }
427 | });
428 |
429 |
430 | // Footer Items
431 | this.footerItems.forEach((item) => {
432 | $(item.ele).css("webkitTransform", "");
433 | });
434 | }
435 |
436 |
437 |
438 |
439 |
440 |
441 | hasScrollToBottomBefore = false;
442 |
443 |
444 | //@HostListener("ionScroll", ["$event", "false"])
445 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
446 |
447 | let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
448 |
449 |
450 |
451 | var duration = 0;
452 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
453 |
454 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
455 |
456 |
457 |
458 | //if we are at the bottom, animate the header/tabs back in
459 | if (event.scrollTop >= maxScrollTop - this.footerHeight) {
460 |
461 | if(this.hasScrollToBottomBefore) {
462 | y = 0;
463 | duration = this.defaultDuration;
464 | }
465 |
466 | if(isMoveEnd) {
467 | this.hasScrollToBottomBefore = true;
468 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
469 | }
470 |
471 | } else {
472 | this.hasScrollToBottomBefore = false;
473 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
474 | }
475 |
476 |
477 | console.log({
478 | y: y,
479 | endY: this.endY,
480 |
481 | scrollTop: event.scrollTop,
482 | maxScrollTop: maxScrollTop,
483 | footerHeight: this.footerHeight,
484 | });
485 |
486 |
487 |
488 |
489 | //if previous and current y are the same, no need to continue
490 | if (this.yPrev !== y) {
491 | //this.modifyDom(y, duration, event.domWrite);
492 |
493 | let yDiff = y - this.yPrev;
494 | if(-30 < yDiff && yDiff < 30) {
495 | this.modifyDom(y, duration, event.domWrite);
496 | } else {
497 | this.modifyDom(y, 50, event.domWrite);
498 | }
499 | }
500 |
501 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
502 |
503 |
504 | this.yPrev = y;
505 | this.scrollTopPrev = event.scrollTop;
506 | }
507 |
508 |
509 |
510 |
511 |
512 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
513 | dowWrite(() => {
514 | y = constrain(y, 0, this.endY);
515 |
516 |
517 | console.group();
518 |
519 |
520 | // Header Items
521 | this.headerItems.forEach((item) => {
522 |
523 | let dy_transit = constrain((y - item.transition.start), 0, item.transition.interval);
524 | let dy_move = constrain((y - item.transition.end), 0, Infinity);
525 |
526 | console.log(y, "dy_transit:", dy_transit, "dy_move:", dy_move, TransitionType[item.transition.type]);
527 |
528 | if(item.type === ItemType.Tabbar) {
529 | //let dy_transit = constrain((y - item.transition.start), 0, item.transition.interval);
530 | //let dy_move = constrain((y - item.transition.end), 0, Infinity);
531 |
532 |
533 | if(y <= item.transition.end) {
534 | // Transiting
535 |
536 | if(item.transition.type === TransitionType.Shrink) {
537 | // Shrinking
538 |
539 | let val = constrain((dy_transit / item.height), 0, 1);
540 | $(item.ele).css({
541 | height: item.height - dy_transit,
542 | //minHeight: item.height - dy_shrink,
543 | //marginBottom: dy_shrink,
544 | })
545 | this.translateElementY(item.ele, 0, duration);
546 |
547 | } else {
548 | // Translating
549 |
550 | let val = constrain((dy_transit / item.height), 0, 1);
551 | $(item.ele).css({
552 | height: item.height - dy_transit,
553 | //minHeight: item.height - (item.translateStart - item.shrinkStart),
554 | //marginBottom: (item.translateStart - item.shrinkStart),
555 | })
556 | this.translateElementY(item.ele, 0, duration);
557 | }
558 |
559 | } else {
560 | // Moving
561 |
562 | $(item.ele).css({
563 | height: item.height - item.transition.interval,
564 | //minHeight: item.height - (item.translateStart - item.shrinkStart),
565 | //marginBottom: (item.translateStart - item.shrinkStart),
566 | })
567 | this.translateElementY(item.ele, -dy_move, duration);
568 | }
569 |
570 | } else {
571 |
572 | if(y <= item.transition.end) {
573 | // Transiting
574 |
575 | if(item.transition.type === TransitionType.Shrink) {
576 | // Shrinking
577 |
578 | let val = constrain((dy_transit / item.height), 0, 1);
579 | $(item.ele).css({
580 | height: item.height - dy_transit,
581 | //minHeight: item.height - dy_transit,
582 | marginBottom: dy_transit,
583 | paddingTop: "",
584 | paddingBottom: "",
585 | })
586 | this.translateElementY(item.ele, 0, duration);
587 |
588 |
589 | } else {
590 | // Translating
591 |
592 | let val = constrain((dy_transit / item.height), 0, 1);
593 |
594 | $(item.ele).css({
595 | height: item.height,
596 | //minHeight: item.height,
597 | marginBottom: 0,
598 | paddingTop: "",
599 | paddingBottom: "",
600 | })
601 | this.translateElementY(item.ele, -dy_transit, duration);
602 | }
603 |
604 | } else {
605 | // Moving
606 |
607 | let val = constrain((dy_move / item.height), 0, 1);
608 |
609 | $(item.ele).css({
610 | height: item.height - item.transition.interval,
611 | //minHeight: item.height - item.transition.interval,
612 | marginBottom: item.transition.interval,
613 | paddingTop: 0,
614 | paddingBottom: 0,
615 | })
616 | this.translateElementY(item.ele, -dy_move, duration);
617 | }
618 |
619 | }
620 | /*if(item.shrinkable) {
621 | //let fadeAmt = constrain(1 - (dy / item.ele.offsetHeight), 0, 1);
622 | let fadeAmt = 1 - val;
623 |
624 | $(item.ele).children().not(".toolbar-background")
625 | .each((index, navbarChild) => {
626 | $(navbarChild).css("opacity", fadeAmt);
627 | });
628 | }*/
629 | });
630 | console.groupEnd();
631 |
632 |
633 | // Footer Items
634 | this.footerItems.forEach((item) => {
635 | this.translateElementY(item.ele, y, duration);
636 | });
637 |
638 |
639 | // Content
640 | /*if(y <= this.endY) {
641 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - y) + "px");
642 | } else {
643 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - this.endY) + "px");
644 | }
645 |
646 | if(y <= this.footerHeight) {
647 | this.content.setScrollElementStyle("margin-bottom", (this.footerHeight - y) + "px");
648 | }*/
649 | });
650 | }
651 |
652 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
653 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
654 |
655 | if (duration && !ele.style.transitionDuration) {
656 | ele.style.transitionDuration = duration + "ms";
657 | setTimeout(() => {
658 | ele.style.transitionDuration = "";
659 | }, this.defaultDelay);
660 | }
661 | }
662 | }
663 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/dev/scroll-hide-v3.1.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 | import { Subscription } from 'rxjs';
9 |
10 |
11 | import * as $ from 'jquery'
12 |
13 |
14 | /*
15 |
16 |
17 | ion-header {
18 | pointer-events: none;
19 | }
20 | ion-header > * {
21 | pointer-events: all;
22 | }
23 |
24 |
25 |
26 |
27 | ion-header > ion-navbar {
28 | position: relative;
29 | }
30 |
31 |
32 |
33 | ion-tabs > .tabbar {
34 | overflow: hidden;
35 | }
36 | ion-tabs > .tabbar > *{
37 | align-self: flex-end;
38 | }
39 |
40 | */
41 |
42 |
43 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
44 |
45 |
46 | function constrain(val: number, min: number, max: number) {
47 | return Math.min(max, Math.max(val, min));
48 | }
49 |
50 |
51 |
52 |
53 |
54 | enum ItemType { Navbar, Toolbar, Tabbar }
55 |
56 | enum TransitionType { Static, Translate, Shrink }
57 | interface Transition {
58 | type: TransitionType;
59 | start: number;
60 | end: number;
61 | interval: number;
62 | }
63 |
64 | /*
65 | * startY EndY
66 | * shrinkStart TransitionStart MoveStart
67 | * | ------------- | ---------------- | ---------->
68 | * Shrink Translate
69 | *
70 | *
71 | * transitionStart transitionEnd
72 | * | ------------- | ---------->
73 | * Shrink
74 | *
75 | * transitionStart transitionEnd
76 | * | ------------- | ---------->
77 | * Translate
78 | *
79 | */
80 | class Item {
81 | public type: ItemType;
82 | public height: number;
83 |
84 | public transition: Transition = { type: 0, start: 0, end: 0, interval: 0 };
85 | public scrollShrinkVal: number = 0;
86 |
87 |
88 | constructor(public ele: HTMLElement) {
89 |
90 | if(ele.classList.contains("tabbar")) {
91 | this.type = ItemType.Tabbar;
92 | } else if(ele.tagName === "ION-NAVBAR") {
93 | this.type = ItemType.Navbar;
94 | } else {
95 | this.type = ItemType.Toolbar;
96 | }
97 |
98 | this.height = ele.offsetHeight;
99 |
100 |
101 | let shrinkable = ele.hasAttribute("scroll-hide-shrink");
102 | let translatable = ele.hasAttribute("scroll-hide-translate");
103 |
104 | if(shrinkable) {
105 | this.transition.type = TransitionType.Shrink;
106 | var scrollShrinkVal = parseFloat(ele.getAttribute("scroll-hide-shrink"));
107 | if(isNaN(scrollShrinkVal)) scrollShrinkVal = 1;
108 | this.scrollShrinkVal = scrollShrinkVal;
109 |
110 | } else if(translatable) {
111 | this.transition.type = TransitionType.Translate;
112 |
113 | } else {
114 | this.transition.type = TransitionType.Static;
115 |
116 | }
117 |
118 |
119 | if(this.type === ItemType.Tabbar) {
120 | this.transition.type = TransitionType.Shrink;
121 | this.scrollShrinkVal = 0.5;
122 |
123 | //this.transition.type = TransitionType.Translate;
124 |
125 | //this.transition.type = TransitionType.Static;
126 | }
127 | }
128 |
129 | }
130 |
131 |
132 | @Directive({
133 | selector: '[scroll-hide]' // Attribute selector
134 | })
135 | export class ScrollHide implements OnInit, OnDestroy {
136 |
137 | @Input() navCtrl: NavController;
138 | @Input() viewCtrl: ViewController;
139 |
140 |
141 | headerItems: Item[] = [];
142 | footerItems: Item[] = [];
143 |
144 | headerEle: HTMLElement;
145 | footerEle: HTMLElement;
146 |
147 | //---------------------------------
148 |
149 | defaultDelay: number = 400 * 2;
150 | defaultDuration: number = 400;
151 |
152 |
153 | headerHeight: number;
154 | footerHeight: number;
155 | endY: number;
156 |
157 | defaultEnd: number = 0;
158 | y: number = 0;
159 | yPrev: number = 0;
160 | scrollTopPrev: number = 0;
161 |
162 |
163 | initiated: boolean = false;
164 | viewEntered: boolean = false;
165 |
166 | //---------------------------------
167 |
168 | subscriptions: Subscription[] = [];
169 |
170 | //---------------------------------
171 |
172 |
173 | constructor(private content: Content,
174 | private renderer: Renderer,
175 | private zone: NgZone) {}
176 |
177 |
178 |
179 | ngOnInit() {
180 | this.content.fullscreen = true;
181 |
182 |
183 | //console.log("ngOnInit() > navCtrl:", this.navCtrl);
184 | //console.log("ngOnInit() > viewCtrl:", this.viewCtrl);
185 |
186 |
187 |
188 | this.subscriptions.push(
189 | this.viewCtrl.willEnter.subscribe(() => {
190 | if(this.initiated) {
191 |
192 |
193 | //this.appplyStoredTabbarStyle();
194 | }
195 | })
196 | );
197 | this.subscriptions.push(
198 | this.viewCtrl.didEnter.subscribe(() => {
199 | //this.viewEntered = true;
200 | /*if(!this.initiated) {
201 | this.initiated = true;
202 | console.log("init()");
203 | this.init();
204 | } else {
205 |
206 | }*/
207 |
208 | this.init();
209 | })
210 | );
211 |
212 | /*(this.content._tabs).ionChange.subscribe(() => {
213 | console.log("ionChange > viewEntered:", this.viewEntered);
214 | if(this.viewEntered && this.cachedTabbarStayle) {
215 | //this.appplyStoredTabbarStyle();
216 | if(this.content._tabsPlacement === "top") {
217 | let top = parseInt(this.cachedTabbarStayle.top);
218 | console.log("this.content._tabsPlacement === 'top' top:", top);
219 | this.content._tabs.setTabbarPosition(top, -1);
220 | }
221 | }
222 | });*/
223 |
224 |
225 | this.subscriptions.push(
226 | this.viewCtrl.willLeave.subscribe(() => {
227 | //console.log("resetStyle()");
228 | this.resetStyle();
229 |
230 | //this.viewEntered = false;
231 |
232 | //this.storeTabbarStyle();
233 | //this.resetTabbarStyle();
234 | })
235 | );
236 |
237 |
238 |
239 |
240 | // Prevent multipe trigger of scrollEnd event
241 |
242 | //var startTimestamp: number = 0;
243 | var prevEndTimestep: number = 0;
244 | this.subscriptions.push(
245 | this.content.ionScrollStart.subscribe(event => {
246 | //console.log("ionScrollStart!", event);
247 | //startTimestamp = event.timeStamp;
248 | //console.log("ionScrollStart!", event.timeStamp);
249 | })
250 | );
251 | this.subscriptions.push(
252 | this.content.ionScroll.subscribe(event => {
253 | if(event.timeStamp > prevEndTimestep + 200) { // prevent multiple triggered of scroll event
254 | this.onContentScroll(event, false);
255 | //console.log("ionScroll!", event.timeStamp);
256 | } else {
257 | //console.log("fake --> ionScroll!", event.timeStamp);
258 | }
259 | //this.onContentScroll(event, false);
260 | })
261 | );
262 | this.subscriptions.push(
263 | this.content.ionScrollEnd.subscribe(event => {
264 | if(event.timeStamp > prevEndTimestep + 200) {
265 | this.onContentScroll(event, true);
266 | //console.log("ionScrollEnd!", event.timeStamp);
267 | } else {
268 | //console.log("fake --> ionScrollEnd!", event.timeStamp);
269 | }
270 | prevEndTimestep = event.timeStamp;
271 | })
272 | );
273 |
274 |
275 |
276 | let win: any = window;
277 | win.temp = win.temp || {};
278 | win.temp.content = this.content;
279 | win.temp.zone = this.zone;
280 | win.temp.viewCtrl = this.viewCtrl;
281 | }
282 |
283 | ngOnDestroy() {
284 | console.log("ngOnDestroy()");
285 |
286 | this.resetStyle();
287 |
288 |
289 | this.navCtrl = null;
290 | this.viewCtrl = null;
291 |
292 | this.headerItems = null;
293 | this.footerItems = null;
294 |
295 | this.headerEle = null;
296 | this.footerEle = null;
297 |
298 | this.subscriptions.forEach(sub => sub.unsubscribe());
299 | this.subscriptions = null;
300 | }
301 |
302 |
303 |
304 | private init() {
305 | this.y = 0;
306 | this.yPrev = 0;
307 | this.scrollTopPrev = this.content.scrollTop;
308 |
309 |
310 |
311 | this.content.resize();
312 |
313 |
314 | let contentEle = this.content.getNativeElement();
315 | let headerEle = contentEle.parentElement.querySelector("ion-header");
316 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
317 |
318 | if(headerEle) {
319 | this.headerItems = $(headerEle)
320 | .children()
321 | .toArray().map(ele => new Item(ele));
322 | } else {
323 | this.headerItems = [];
324 | }
325 |
326 | if(footerEle) {
327 | this.footerItems = $(footerEle)
328 | .children()
329 | .toArray().map(ele => new Item(ele));
330 | } else {
331 | this.footerItems = [];
332 | }
333 |
334 |
335 |
336 |
337 | let hasTabbar = (this.content._tabs !== null);
338 | if(hasTabbar) {
339 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
340 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
341 |
342 | if(this.content._tabsPlacement === "bottom") {
343 | this.footerItems.push(new Item(tabbarEle));
344 | } else {
345 | this.headerItems.push(new Item(tabbarEle));
346 | }
347 | }
348 |
349 |
350 | this.headerItems = this.headerItems.reverse();
351 | this.footerItems = this.footerItems.reverse();
352 |
353 |
354 |
355 |
356 | var headerHeight = 0,
357 | footerHeight = 0;
358 |
359 | console.group("headerItems")
360 |
361 | var shrinkableHeightSum = 0;
362 | this.headerItems.forEach((item, index) => {
363 | headerHeight += item.ele.offsetHeight;
364 |
365 | // Navbar would not be raised without setting position to relative
366 | if(item.type === ItemType.Navbar) {
367 | $(item.ele).css("position", "relative")
368 | }
369 |
370 |
371 | // Prevent overlapping of bottom element
372 | if(item.type === ItemType.Tabbar) {
373 | $(item.ele).css("overflow", "hidden");
374 | $(item.ele).children().css("align-self", "flex-end");
375 | } else {
376 | $(item.ele).css("z-index", index + 1);
377 | }
378 |
379 |
380 | // For shrinking
381 | //$(item.ele).css("padding-top", "0");
382 | //$(item.ele).css("padding-bottom", "0");
383 |
384 |
385 | $(item.ele).css("min-height", "0");
386 | $(item.ele).css("height", item.height + "px");
387 |
388 |
389 | switch(item.transition.type) {
390 |
391 | case TransitionType.Shrink:
392 |
393 | let shrinkHeight = (item.height * item.scrollShrinkVal);
394 | item.transition.start = (shrinkableHeightSum);
395 | item.transition.end = (shrinkableHeightSum + shrinkHeight);
396 | item.transition.interval = (shrinkHeight);
397 | shrinkableHeightSum += (shrinkHeight);
398 | break;
399 |
400 | case TransitionType.Translate:
401 |
402 | item.transition.start = (shrinkableHeightSum);
403 | item.transition.end = (shrinkableHeightSum + item.height);
404 | item.transition.interval = (item.height);
405 | shrinkableHeightSum += (item.height);
406 | break;
407 |
408 | case TransitionType.Static:
409 |
410 | item.transition.start = (shrinkableHeightSum);
411 | item.transition.end = (shrinkableHeightSum);
412 | item.transition.interval = (0);
413 | break;
414 | }
415 |
416 | console.log(item, item.ele.offsetHeight);
417 | });
418 | this.headerHeight = headerHeight;
419 | this.defaultEnd = this.headerHeight * 2;
420 | this.endY = shrinkableHeightSum;
421 |
422 | console.groupEnd();
423 |
424 |
425 | console.group("footerItems")
426 | this.footerItems.forEach(item => {
427 | footerHeight += item.ele.offsetHeight;
428 | console.log(item.ele, item.ele.offsetHeight);
429 | });
430 | this.footerHeight = footerHeight;
431 | console.groupEnd();
432 |
433 | console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
434 |
435 |
436 | /*
437 | ion-header {
438 | pointer-events: none;
439 | }
440 |
441 | ion-header > * {
442 | pointer-events: all;
443 | }*/
444 |
445 | if(headerEle) {
446 | $(headerEle).css("pointer-events", "none");
447 | $(headerEle).children().css("pointer-events", "all");
448 | }
449 | if(footerEle) {
450 | $(footerEle).css("pointer-events", "none");
451 | $(footerEle).children().css("pointer-events", "all");
452 | }
453 |
454 | this.headerEle = headerEle;
455 | this.footerEle = footerEle;
456 |
457 |
458 |
459 | }
460 |
461 |
462 | private resetStyle() {
463 | this.headerItems.forEach((item, index) => {
464 |
465 | if(item.type === ItemType.Navbar) {
466 | $(item.ele).css("position", "");
467 | }
468 |
469 | if(item.type === ItemType.Tabbar) {
470 | $(item.ele).css("overflow", "");
471 | $(item.ele).children().css("align-self", "center");
472 | } else {
473 | $(item.ele).css("z-index", "");
474 | }
475 |
476 | });
477 |
478 | if(this.headerEle) {
479 | $(this.headerEle).css("pointer-events", "none");
480 | $(this.headerEle).children().css("pointer-events", "all");
481 | }
482 | if(this.footerEle) {
483 | $(this.footerEle).css("pointer-events", "none");
484 | $(this.footerEle).children().css("pointer-events", "all");
485 | }
486 |
487 |
488 | // Header Items
489 | this.headerItems.forEach((item) => {
490 |
491 | $(item.ele).css("height", "");
492 | $(item.ele).css("min-height", "");
493 | $(item.ele).css("webkitTransform", "");
494 |
495 |
496 | if(item.type !== ItemType.Tabbar) {
497 |
498 | $(item.ele).css("marginBottom", "");
499 |
500 | if(item.transition.type === TransitionType.Shrink) {
501 | // Shrinking
502 | $(item.ele).children().not(".toolbar-background, .toolbar-content")
503 | .each((index, navbarChild) => {
504 | $(navbarChild).css("transform", "");
505 | });
506 | $(item.ele).children(".toolbar-content").children()
507 | .each((index, navbarChild) => {
508 | $(navbarChild).css("transform", "");
509 | });
510 |
511 | } else {
512 | // Translating
513 | $(item.ele).children().not(".toolbar-background")
514 | .each((index, navbarChild) => {
515 | $(navbarChild).css("opacity", "");
516 | });
517 | }
518 |
519 | }
520 | });
521 |
522 |
523 | // Footer Items
524 | this.footerItems.forEach((item) => {
525 | $(item.ele).css("webkitTransform", "");
526 | });
527 |
528 |
529 | // Content
530 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
531 | }
532 |
533 |
534 | cachedTabbarStayle: {
535 | top: string;
536 | height?: string;
537 | webkitTransform: string;
538 | };
539 | private storeTabbarStyle() {
540 | console.log("storeTabbarStyle()");
541 |
542 | this.headerItems.forEach((item) => {
543 | if(item.type === ItemType.Tabbar) {
544 | this.cachedTabbarStayle = {
545 | top: $(item.ele).css("top"),
546 | height: $(item.ele).css("height"),
547 | webkitTransform: $(item.ele).css("webkitTransform")
548 | };
549 | }
550 | });
551 | this.footerItems.forEach((item) => {
552 | if(item.type === ItemType.Tabbar) {
553 | this.cachedTabbarStayle = {
554 | top: $(item.ele).css("top"),
555 | webkitTransform: $(item.ele).css("webkitTransform")
556 | };
557 | }
558 | });
559 | console.log("this.cachedTabbarStayle:", this.cachedTabbarStayle);
560 | }
561 | private resetTabbarStyle() {
562 | console.log("resetTabbarStyle()");
563 |
564 | this.headerItems.forEach((item) => {
565 | if(item.type === ItemType.Tabbar) {
566 | $(item.ele).css("overflow", "");
567 | $(item.ele).children().css("align-self", "center");
568 |
569 | $(item.ele).css("height", "");
570 | $(item.ele).css("min-height", "");
571 | $(item.ele).css("webkitTransform", "");
572 | }
573 | });
574 | this.footerItems.forEach((item) => {
575 | if(item.type === ItemType.Tabbar) {
576 | $(item.ele).css("webkitTransform", "");
577 | }
578 | });
579 | }
580 | private appplyStoredTabbarStyle() {
581 | console.log("appplyStoredTabbarStyle()");
582 |
583 | this.headerItems.forEach((item) => {
584 | if(item.type === ItemType.Tabbar) {
585 | $(item.ele).css("overflow", "hidden");
586 | $(item.ele).children().css("align-self", "flex-end");
587 | $(item.ele).css("min-height", "0");
588 |
589 | $(item.ele).css("top", this.cachedTabbarStayle.top);
590 | $(item.ele).css("height", this.cachedTabbarStayle.height);
591 | $(item.ele).css("webkitTransform", this.cachedTabbarStayle.webkitTransform);
592 | }
593 | });
594 | this.footerItems.forEach((item) => {
595 | if(item.type === ItemType.Tabbar) {
596 | $(item.ele).css("top", this.cachedTabbarStayle.top);
597 | $(item.ele).css("webkitTransform", this.cachedTabbarStayle.webkitTransform);
598 | }
599 | });
600 | }
601 |
602 |
603 |
604 | hasStopMoving = false;
605 | hasClearStopMoving = false;
606 |
607 | extendBottom = false;
608 | hasReachBottomBefore = false;
609 | hasBeyondMaxScrollTop = false;
610 |
611 | //@HostListener("ionScroll", ["$event", "false"])
612 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
613 |
614 | //let maxScrollTop = event.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom);
615 |
616 | // For Ionic 3
617 | let maxScrollTop = this.content._scrollContent.nativeElement.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0);
618 | // For Ionic 2
619 | //let maxScrollTop = this.content._scrollEle.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0); // For ionic2
620 |
621 |
622 |
623 | var duration = 0;
624 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
625 |
626 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
627 |
628 |
629 | //console.log(event.scrollTop, "maxScrollTop:", maxScrollTop, "this.hasScrollToBottomBefore:", this.hasScrollToBottomBefore);
630 | //console.log(event.scrollTop + ", maxScrollTop: " + maxScrollTop + ", hasExtend: " + this.extendBottom + ", hasBottom: " + this.hasScrollToBottomBefore);
631 | console.log(event.scrollTop + ", maxScrollTop: " + maxScrollTop + ", isMoveEnd: " + isMoveEnd);
632 |
633 |
634 |
635 | //----------------------------------------------------------------------------------------
636 |
637 | //if we are at the bottom, animate the header/tabs back in
638 | /*if (event.scrollTop < maxScrollTop - 40) {
639 |
640 | this.hasReachBottomBefore = false;
641 | //this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
642 | this.extendBottom = false;
643 | console.log("--> reset padding-bottom");
644 |
645 | } else if (event.scrollTop >= maxScrollTop) {
646 |
647 | if(this.hasReachBottomBefore) {
648 | y = 0;
649 | duration = this.defaultDuration;
650 | console.log("--> show");
651 | }
652 | if(isMoveEnd) {
653 | this.hasReachBottomBefore = true;
654 | //this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
655 | this.extendBottom = true;
656 | console.log("--> extend padding-bottom");
657 | }
658 | }*/
659 |
660 |
661 |
662 | // ------------------------------
663 |
664 |
665 |
666 |
667 | if (event.scrollTop >= maxScrollTop) {
668 | if(this.hasReachBottomBefore) {
669 | console.log("--> 1 show");
670 | y = 0;
671 | duration = this.defaultDuration;
672 | }
673 | if(isMoveEnd) {
674 | console.log("--> 1 extend padding-bottom");
675 | this.hasReachBottomBefore = true;
676 | this.extendBottom = true;
677 | }
678 | }
679 |
680 | /*if(event.scrollTop <= maxScrollTop) {
681 | if(this.hasBeyondMaxScrollTop) {
682 | console.log("--> 2 extend padding-bottom!");
683 | this.extendBottom = true;
684 | }
685 | this.hasBeyondMaxScrollTop = false;
686 |
687 | } else { // > maxScrollTop
688 | console.log("--> 2 scroll to bottom!");
689 | this.hasBeyondMaxScrollTop = true;
690 |
691 | if(this.extendBottom) {
692 | console.log("--> 2 show!");
693 | y = 0;
694 | duration = this.defaultDuration;
695 | }
696 | }*/
697 |
698 | // Reset bottom
699 | if (event.scrollTop < maxScrollTop - 40) {
700 | this.hasReachBottomBefore = false;
701 | this.extendBottom = false;
702 | console.log("--> reset padding-bottom");
703 | }
704 |
705 | // Set bottom height
706 | if(this.extendBottom) {
707 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
708 | } else {
709 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
710 | }
711 |
712 |
713 |
714 | // ------------------------------
715 |
716 |
717 | /*
718 | // ????
719 | if(event.scrollTop >= maxScrollTop) {
720 | if(event.scrollTop < this.scrollTopPrev) {
721 | console.log("--> scroll to bottom!");
722 | this.hasScrollToBottomBefore = true;
723 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
724 |
725 | } else if(this.hasScrollToBottomBefore) {
726 | console.log("--> show!");
727 | y = 0;
728 | duration = this.defaultDuration;
729 | }
730 | } else if(event.scrollTop < maxScrollTop - 40){
731 | console.log("--> show!");
732 | this.hasScrollToBottomBefore = false;
733 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
734 | }*/
735 |
736 |
737 |
738 | // ------------------------------
739 |
740 |
741 | /*
742 | // ????
743 | if(event.scrollTop >= maxScrollTop) {
744 | if(!this.hasExtendBottom) {
745 | if(event.scrollTop < this.scrollTopPrev) {
746 | console.log("--> scroll to bottom!");
747 | this.hasScrollToBottomBefore = true;
748 | }
749 | } else {
750 | console.log("--> show!");
751 | y = 0;
752 | duration = this.defaultDuration;
753 | }
754 | } else if(event.scrollTop < maxScrollTop) {
755 | if(this.hasScrollToBottomBefore) {
756 | this.hasScrollToBottomBefore = false;
757 | console.log("--> extend padding-bottom!");
758 | this.hasExtendBottom = true;
759 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
760 | } else {
761 | console.log("reset padding-bottom");
762 | this.hasExtendBottom = false;
763 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
764 | }
765 | }*/
766 |
767 |
768 |
769 | // ------------------------------
770 |
771 |
772 |
773 | // Work!
774 | /*if(event.scrollTop <= maxScrollTop) {
775 | if(this.hasBeyondMaxScrollTop) {
776 | console.log("--> extend padding-bottom!");
777 | this.extendBottom = true;
778 | }
779 | this.hasBeyondMaxScrollTop = false;
780 |
781 |
782 | } else { // > maxScrollTop
783 | this.hasBeyondMaxScrollTop = true;
784 | console.log("--> scroll to bottom!");
785 |
786 | if(this.extendBottom) {
787 | console.log("--> show!");
788 | y = 0;
789 | duration = this.defaultDuration;
790 | }
791 | }
792 |
793 | if(event.scrollTop < maxScrollTop - 40) {
794 | this.extendBottom = false;
795 | }
796 |
797 | if(this.extendBottom) {
798 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
799 | } else {
800 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
801 | }*/
802 |
803 |
804 | // ------------------------------
805 |
806 | /*
807 | // Seem to be work
808 | if(event.scrollTop >= maxScrollTop) {
809 | if(scrollTopDiff <= 0) {
810 | console.log("--> Beyond MaxScrollTop! extend!");
811 | this.hasBeyondMaxScrollTop = true;
812 | this.extendBottom = true;
813 | } else { // > 0
814 | if(this.hasBeyondMaxScrollTop) {
815 | console.log("--> show!");
816 | y = 0;
817 | duration = this.defaultDuration;
818 | }
819 | }
820 | } else {
821 | if(scrollTopDiff <= 0) {
822 | console.log("--> reset beyondMaxScrollTop!");
823 | this.hasBeyondMaxScrollTop = false;
824 | }
825 | }
826 |
827 | // Reset the bottom
828 | if(event.scrollTop < maxScrollTop - 40) {
829 | this.extendBottom = false;
830 | }
831 |
832 | // Set bottom height
833 | if(this.extendBottom) {
834 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
835 | } else {
836 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
837 | }*/
838 |
839 | // ------------------------------
840 |
841 | /*if(this.hasStopMoving) {
842 | if(!this.hasClearStopMoving) {
843 | console.log("---> clear stop moving!");
844 | this.hasClearStopMoving = true;
845 | this.content.setScrollElementStyle("overflow-y", "");
846 | }
847 |
848 | if(scrollTopDiff < 0) { // scroll up
849 | this.hasStopMoving = false;
850 |
851 | } else if(scrollTopDiff > 0) { // scroll down
852 | this.hasStopMoving = false;
853 | console.log("--> show!");
854 | y = 0;
855 | duration = this.defaultDuration;
856 | }
857 | }
858 |
859 | if(event.scrollTop >= maxScrollTop) {
860 | if(!this.hasStopMoving) {
861 | console.log("---> stop moving!");
862 | this.hasStopMoving = true;
863 | this.hasClearStopMoving = false;
864 |
865 | this.extendBottom = true;
866 | this.content.setScrollElementStyle("overflow-y", "hidden");
867 | //this.content.scrollTo(0, maxScrollTop);
868 | }
869 | }
870 |
871 |
872 | // Reset the bottom
873 | if(event.scrollTop < maxScrollTop - 40) {
874 | this.extendBottom = false;
875 | }
876 |
877 | // Set bottom height
878 | if(this.extendBottom) {
879 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
880 | } else {
881 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
882 | }*/
883 |
884 |
885 |
886 |
887 |
888 | //----------------------------------------------------------------------------------------
889 |
890 | /*console.log({
891 | y: y,
892 | endY: this.endY,
893 |
894 | scrollTop: event.scrollTop,
895 | maxScrollTop: maxScrollTop,
896 | footerHeight: this.footerHeight,
897 | });*/
898 |
899 |
900 |
901 |
902 | //if previous and current y are the same, no need to continue
903 | if (this.yPrev !== y) {
904 | //this.modifyDom(y, duration, event.domWrite);
905 |
906 | let yDiff = y - this.yPrev;
907 | if(-30 < yDiff && yDiff < 30) {
908 | this.modifyDom(y, duration, event.domWrite);
909 | } else {
910 | this.modifyDom(y, 50, event.domWrite);
911 | }
912 | }
913 |
914 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
915 |
916 |
917 | this.yPrev = y;
918 | this.scrollTopPrev = event.scrollTop;
919 | }
920 |
921 |
922 |
923 |
924 |
925 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
926 | dowWrite(() => {
927 | y = constrain(y, 0, this.endY);
928 |
929 |
930 | //console.group();
931 |
932 |
933 | // Header Items
934 | this.headerItems.forEach((item) => {
935 |
936 | let dy_transit = constrain((y - item.transition.start), 0, item.transition.interval);
937 | let dy_move = constrain((y - item.transition.end), 0, Infinity);
938 |
939 | //console.log(y, "dy_transit:", dy_transit, "dy_move:", dy_move, TransitionType[item.transition.type]);
940 |
941 | if(item.type === ItemType.Tabbar) {
942 |
943 | let val = constrain((dy_transit / item.height), 0, 1);
944 | $(item.ele).css({
945 | height: item.height - dy_transit,
946 | })
947 | this.translateElementY(item.ele, -dy_move, duration);
948 |
949 | } else {
950 |
951 | if(item.transition.type === TransitionType.Shrink) {
952 | // Shrinking
953 |
954 | let val = constrain((dy_transit / item.height), 0, 1);
955 | $(item.ele).css({
956 | height: item.height - dy_transit,
957 | marginBottom: 0, // marginBottom: dy_transit,
958 | })
959 | this.translateElementY(item.ele, 0, duration);
960 |
961 |
962 | /*$(item.ele).children().not(".toolbar-background")
963 | .each((index, navbarChild) => {
964 | $(navbarChild).css("transform", `scale(${1 - val})`);
965 | });*/
966 |
967 | $(item.ele).children().not(".toolbar-background, .toolbar-content")
968 | .each((index, navbarChild) => {
969 | $(navbarChild).css("transform", `scale(${1 - val})`);
970 | });
971 | $(item.ele).children(".toolbar-content").children()
972 | .each((index, navbarChild) => {
973 | $(navbarChild).css("transform", `scale(${1 - val})`);
974 | });
975 |
976 | } else {
977 | // Translating
978 |
979 | let val = constrain((dy_transit / item.height), 0, 1);
980 |
981 | $(item.ele).css({
982 | height: item.height,
983 | marginBottom: -dy_transit, // marginBottom: 0,
984 | })
985 | this.translateElementY(item.ele, -dy_transit, duration);
986 |
987 |
988 | $(item.ele).children().not(".toolbar-background")
989 | .each((index, navbarChild) => {
990 | $(navbarChild).css("opacity", 1 - val);
991 | });
992 | }
993 |
994 |
995 | }
996 | });
997 | //console.groupEnd();
998 |
999 |
1000 | // Footer Items
1001 | this.footerItems.forEach((item) => {
1002 | this.translateElementY(item.ele, y, duration);
1003 | });
1004 |
1005 |
1006 | // Content
1007 | /*if(y <= this.endY) {
1008 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - y) + "px");
1009 | } else {
1010 | this.content.setScrollElementStyle("margin-top", (this.headerHeight - this.endY) + "px");
1011 | }
1012 |
1013 | if(y <= this.footerHeight) {
1014 | this.content.setScrollElementStyle("margin-bottom", (this.footerHeight - y) + "px");
1015 | }*/
1016 | });
1017 | }
1018 |
1019 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
1020 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
1021 |
1022 | if (duration && !ele.style.transitionDuration) {
1023 | ele.style.transitionDuration = duration + "ms";
1024 | setTimeout(() => {
1025 | ele.style.transitionDuration = "";
1026 | }, this.defaultDelay);
1027 | }
1028 | }
1029 | }
1030 |
--------------------------------------------------------------------------------
/src/components/scroll-hide/scroll-hide.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef, NgZone, HostListener, Input, Renderer,
4 | OnInit, OnDestroy,
5 | } from '@angular/core';
6 |
7 | import { Content, ScrollEvent, NavController, ViewController } from 'ionic-angular'
8 | import { Subscription } from 'rxjs';
9 |
10 | import * as $ from 'jquery'
11 |
12 |
13 | /*
14 |
15 |
16 | ion-header {
17 | pointer-events: none;
18 | }
19 | ion-header > * {
20 | pointer-events: all;
21 | }
22 |
23 |
24 |
25 |
26 | ion-header > ion-navbar {
27 | position: relative;
28 | }
29 |
30 |
31 |
32 | ion-tabs > .tabbar {
33 | overflow: hidden;
34 | }
35 | ion-tabs > .tabbar > *{
36 | align-self: flex-end;
37 | }
38 |
39 | */
40 |
41 |
42 | type DomWrite = (fn: (timeStamp?: number) => void, ctx?: any) => void;
43 |
44 |
45 | function constrain(val: number, min: number, max: number) {
46 | return Math.min(max, Math.max(val, min));
47 | }
48 |
49 |
50 |
51 |
52 |
53 | enum ItemType { Navbar, Toolbar, Tabbar }
54 |
55 | enum TransitionType { Static, Translate, Shrink }
56 | interface Transition {
57 | type: TransitionType;
58 | start: number;
59 | end: number;
60 | interval: number;
61 | }
62 |
63 | /*
64 | * startY EndY
65 | * shrinkStart TransitionStart MoveStart
66 | * | ------------- | ---------------- | ---------->
67 | * Shrink Translate
68 | *
69 | *
70 | * transitionStart transitionEnd
71 | * | ------------- | ---------->
72 | * Shrink
73 | *
74 | * transitionStart transitionEnd
75 | * | ------------- | ---------->
76 | * Translate
77 | *
78 | */
79 | class Item {
80 | public type: ItemType;
81 | public height: number;
82 |
83 | public transition: Transition = { type: 0, start: 0, end: 0, interval: 0 };
84 | public scrollShrinkVal: number = 0;
85 |
86 |
87 | constructor(public ele: HTMLElement) {
88 |
89 | if(ele.classList.contains("tabbar")) {
90 | this.type = ItemType.Tabbar;
91 | } else if(ele.tagName === "ION-NAVBAR") {
92 | this.type = ItemType.Navbar;
93 | } else {
94 | this.type = ItemType.Toolbar;
95 | }
96 |
97 | this.height = ele.offsetHeight;
98 | Subscription
99 |
100 |
101 | let shrinkable = ele.hasAttribute("scroll-hide-shrink");
102 | let translatable = ele.hasAttribute("scroll-hide-translate");
103 |
104 | if(shrinkable) {
105 | this.transition.type = TransitionType.Shrink;
106 | var scrollShrinkVal = parseFloat(ele.getAttribute("scroll-hide-shrink"));
107 | if(isNaN(scrollShrinkVal)) scrollShrinkVal = 1;
108 | this.scrollShrinkVal = scrollShrinkVal;
109 |
110 | } else if(translatable) {
111 | this.transition.type = TransitionType.Translate;
112 |
113 | } else {
114 | this.transition.type = TransitionType.Static;
115 |
116 | }
117 |
118 |
119 | if(this.type === ItemType.Tabbar) {
120 | this.transition.type = TransitionType.Shrink;
121 | this.scrollShrinkVal = 0.5;
122 |
123 | //this.transition.type = TransitionType.Translate;
124 |
125 | //this.transition.type = TransitionType.Static;
126 | }
127 | }
128 |
129 | }
130 |
131 |
132 | @Directive({
133 | selector: '[scroll-hide]' // Attribute selector
134 | })
135 | export class ScrollHide implements OnInit, OnDestroy {
136 |
137 | @Input("scroll-hide") viewCtrl: ViewController;
138 |
139 |
140 | headerItems: Item[] = [];
141 | footerItems: Item[] = [];
142 |
143 | headerEle: HTMLElement;
144 | footerEle: HTMLElement;
145 |
146 | //---------------------------------
147 |
148 | defaultDelay: number = 400 * 2;
149 | defaultDuration: number = 400;
150 |
151 |
152 | headerHeight: number;
153 | footerHeight: number;
154 | endY: number;
155 |
156 | defaultEnd: number = 0;
157 | y: number = 0;
158 | yPrev: number = 0;
159 | scrollTopPrev: number = 0;
160 |
161 |
162 | initiated: boolean = false;
163 | viewEntered: boolean = false;
164 |
165 | //---------------------------------
166 |
167 | subscriptions: Subscription[] = [];
168 |
169 | //---------------------------------
170 |
171 | constructor(private content: Content,
172 | private renderer: Renderer,
173 | private zone: NgZone) {}
174 |
175 |
176 |
177 | ngOnInit() {
178 | this.content.fullscreen = true;
179 |
180 |
181 | // ViewCtrl LifeCycle
182 | this.subscriptions.push(
183 | this.viewCtrl.didEnter.subscribe(() => {
184 | this.init();
185 | })
186 | );
187 | this.subscriptions.push(
188 | this.viewCtrl.willLeave.subscribe(() => {
189 | this.resetStyle();
190 | })
191 | );
192 |
193 |
194 |
195 | // Content Scroll Event
196 | var prevEndTimestep: number = 0;
197 | this.subscriptions.push(
198 | this.content.ionScroll.subscribe(event => {
199 | if(event.timeStamp > prevEndTimestep + 200) { // prevent multiple triggered of scroll event
200 | this.onContentScroll(event, false);
201 | }
202 | })
203 | );
204 | this.subscriptions.push(
205 | this.content.ionScrollEnd.subscribe(event => {
206 | if(event.timeStamp > prevEndTimestep + 200) {
207 | this.onContentScroll(event, true);
208 | }
209 | prevEndTimestep = event.timeStamp;
210 | })
211 | );
212 | }
213 |
214 | ngOnDestroy() {
215 | console.log("ngOnDestroy()");
216 |
217 | this.resetStyle();
218 |
219 |
220 | this.viewCtrl = null;
221 |
222 | this.headerItems = null;
223 | this.footerItems = null;
224 |
225 | this.headerEle = null;
226 | this.footerEle = null;
227 |
228 | this.subscriptions.forEach(sub => sub.unsubscribe());
229 | this.subscriptions = null;
230 | }
231 |
232 |
233 |
234 | private init() {
235 | this.y = 0;
236 | this.yPrev = 0;
237 | this.scrollTopPrev = this.content.scrollTop;
238 |
239 |
240 |
241 | this.content.resize();
242 |
243 |
244 | let contentEle = this.content.getNativeElement();
245 | let headerEle = contentEle.parentElement.querySelector("ion-header");
246 | let footerEle = contentEle.parentElement.querySelector("ion-footer");
247 |
248 | if(headerEle) {
249 | this.headerItems = $(headerEle)
250 | .children()
251 | .toArray().map(ele => new Item(ele));
252 | } else {
253 | this.headerItems = [];
254 | }
255 |
256 | if(footerEle) {
257 | this.footerItems = $(footerEle)
258 | .children()
259 | .toArray().map(ele => new Item(ele));
260 | } else {
261 | this.footerItems = [];
262 | }
263 |
264 |
265 |
266 |
267 | let hasTabbar = (this.content._tabs !== null);
268 | if(hasTabbar) {
269 | //this.tabbarEle = this.content._tabs._tabbar.nativeElement;
270 | let tabbarEle = ( this.content._tabs)._tabbar.nativeElement;
271 |
272 | if(this.content._tabsPlacement === "bottom") {
273 | this.footerItems.push(new Item(tabbarEle));
274 | } else {
275 | this.headerItems.push(new Item(tabbarEle));
276 | }
277 | }
278 |
279 |
280 | this.headerItems = this.headerItems.reverse();
281 | this.footerItems = this.footerItems.reverse();
282 |
283 |
284 |
285 |
286 | var headerHeight = 0,
287 | footerHeight = 0;
288 |
289 | //console.group("headerItems")
290 |
291 | var shrinkableHeightSum = 0;
292 | this.headerItems.forEach((item, index) => {
293 | headerHeight += item.ele.offsetHeight;
294 |
295 | // Navbar would not be raised without setting position to relative
296 | if(item.type === ItemType.Navbar) {
297 | $(item.ele).css("position", "relative")
298 | }
299 |
300 |
301 | // Prevent overlapping of bottom element
302 | if(item.type === ItemType.Tabbar) {
303 | $(item.ele).css("overflow", "hidden");
304 | $(item.ele).children().css("align-self", "flex-end");
305 | } else {
306 | $(item.ele).css("z-index", index + 1);
307 | }
308 |
309 |
310 | // For shrinking
311 | //$(item.ele).css("padding-top", "0");
312 | //$(item.ele).css("padding-bottom", "0");
313 |
314 |
315 | $(item.ele).css("min-height", "0");
316 | $(item.ele).css("height", item.height + "px");
317 |
318 |
319 | switch(item.transition.type) {
320 |
321 | case TransitionType.Shrink:
322 |
323 | let shrinkHeight = (item.height * item.scrollShrinkVal);
324 | item.transition.start = (shrinkableHeightSum);
325 | item.transition.end = (shrinkableHeightSum + shrinkHeight);
326 | item.transition.interval = (shrinkHeight);
327 | shrinkableHeightSum += (shrinkHeight);
328 | break;
329 |
330 | case TransitionType.Translate:
331 |
332 | item.transition.start = (shrinkableHeightSum);
333 | item.transition.end = (shrinkableHeightSum + item.height);
334 | item.transition.interval = (item.height);
335 | shrinkableHeightSum += (item.height);
336 | break;
337 |
338 | case TransitionType.Static:
339 |
340 | item.transition.start = (shrinkableHeightSum);
341 | item.transition.end = (shrinkableHeightSum);
342 | item.transition.interval = (0);
343 | break;
344 | }
345 |
346 | //console.log(item, item.ele.offsetHeight);
347 | });
348 | this.headerHeight = headerHeight;
349 | this.defaultEnd = this.headerHeight * 2;
350 | this.endY = shrinkableHeightSum;
351 |
352 | //console.groupEnd();
353 |
354 |
355 | //console.group("footerItems")
356 | this.footerItems.forEach(item => {
357 | footerHeight += item.ele.offsetHeight;
358 | //console.log(item.ele, item.ele.offsetHeight);
359 | });
360 | this.footerHeight = footerHeight;
361 | //console.groupEnd();
362 |
363 | //console.log("headerHeight:", headerHeight, ", footerHeight:", footerHeight, ", endY:", this.endY);
364 |
365 |
366 | /*
367 | ion-header {
368 | pointer-events: none;
369 | }
370 |
371 | ion-header > * {
372 | pointer-events: all;
373 | }*/
374 |
375 | if(headerEle) {
376 | $(headerEle).css("pointer-events", "none");
377 | $(headerEle).children().css("pointer-events", "all");
378 | }
379 | if(footerEle) {
380 | $(footerEle).css("pointer-events", "none");
381 | $(footerEle).children().css("pointer-events", "all");
382 | }
383 |
384 | this.headerEle = headerEle;
385 | this.footerEle = footerEle;
386 |
387 |
388 |
389 | }
390 |
391 |
392 | private resetStyle() {
393 | this.headerItems.forEach((item, index) => {
394 |
395 | if(item.type === ItemType.Navbar) {
396 | $(item.ele).css("position", "");
397 | }
398 |
399 | if(item.type === ItemType.Tabbar) {
400 | $(item.ele).css("overflow", "");
401 | $(item.ele).children().css("align-self", "center");
402 | } else {
403 | $(item.ele).css("z-index", "");
404 | }
405 |
406 | });
407 |
408 | if(this.headerEle) {
409 | $(this.headerEle).css("pointer-events", "none");
410 | $(this.headerEle).children().css("pointer-events", "all");
411 | }
412 | if(this.footerEle) {
413 | $(this.footerEle).css("pointer-events", "none");
414 | $(this.footerEle).children().css("pointer-events", "all");
415 | }
416 |
417 |
418 | // Header Items
419 | this.headerItems.forEach((item) => {
420 |
421 | $(item.ele).css("height", "");
422 | $(item.ele).css("min-height", "");
423 | $(item.ele).css("webkitTransform", "");
424 |
425 |
426 | if(item.type !== ItemType.Tabbar) {
427 |
428 | $(item.ele).css("marginBottom", "");
429 |
430 | if(item.transition.type === TransitionType.Shrink) {
431 | // Shrinking
432 | $(item.ele).children().not(".toolbar-background, .toolbar-content")
433 | .each((index, navbarChild) => {
434 | $(navbarChild).css("transform", "");
435 | });
436 | $(item.ele).children(".toolbar-content").children()
437 | .each((index, navbarChild) => {
438 | $(navbarChild).css("transform", "");
439 | });
440 |
441 | } else {
442 | // Translating
443 | $(item.ele).children().not(".toolbar-background")
444 | .each((index, navbarChild) => {
445 | $(navbarChild).css("opacity", "");
446 | });
447 | }
448 |
449 | }
450 | });
451 |
452 |
453 | // Footer Items
454 | this.footerItems.forEach((item) => {
455 | $(item.ele).css("webkitTransform", "");
456 | });
457 |
458 |
459 | // Content
460 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
461 | }
462 |
463 |
464 | /*cachedTabbarStayle: {
465 | top: string;
466 | height?: string;
467 | webkitTransform: string;
468 | };
469 | private storeTabbarStyle() {
470 | console.log("storeTabbarStyle()");
471 |
472 | this.headerItems.forEach((item) => {
473 | if(item.type === ItemType.Tabbar) {
474 | this.cachedTabbarStayle = {
475 | top: $(item.ele).css("top"),
476 | height: $(item.ele).css("height"),
477 | webkitTransform: $(item.ele).css("webkitTransform")
478 | };
479 | }
480 | });
481 | this.footerItems.forEach((item) => {
482 | if(item.type === ItemType.Tabbar) {
483 | this.cachedTabbarStayle = {
484 | top: $(item.ele).css("top"),
485 | webkitTransform: $(item.ele).css("webkitTransform")
486 | };
487 | }
488 | });
489 | console.log("this.cachedTabbarStayle:", this.cachedTabbarStayle);
490 | }
491 | private resetTabbarStyle() {
492 | console.log("resetTabbarStyle()");
493 |
494 | this.headerItems.forEach((item) => {
495 | if(item.type === ItemType.Tabbar) {
496 | $(item.ele).css("overflow", "");
497 | $(item.ele).children().css("align-self", "center");
498 |
499 | $(item.ele).css("height", "");
500 | $(item.ele).css("min-height", "");
501 | $(item.ele).css("webkitTransform", "");
502 | }
503 | });
504 | this.footerItems.forEach((item) => {
505 | if(item.type === ItemType.Tabbar) {
506 | $(item.ele).css("webkitTransform", "");
507 | }
508 | });
509 | }
510 | private appplyStoredTabbarStyle() {
511 | console.log("appplyStoredTabbarStyle()");
512 |
513 | this.headerItems.forEach((item) => {
514 | if(item.type === ItemType.Tabbar) {
515 | $(item.ele).css("overflow", "hidden");
516 | $(item.ele).children().css("align-self", "flex-end");
517 | $(item.ele).css("min-height", "0");
518 |
519 | $(item.ele).css("top", this.cachedTabbarStayle.top);
520 | $(item.ele).css("height", this.cachedTabbarStayle.height);
521 | $(item.ele).css("webkitTransform", this.cachedTabbarStayle.webkitTransform);
522 | }
523 | });
524 | this.footerItems.forEach((item) => {
525 | if(item.type === ItemType.Tabbar) {
526 | $(item.ele).css("top", this.cachedTabbarStayle.top);
527 | $(item.ele).css("webkitTransform", this.cachedTabbarStayle.webkitTransform);
528 | }
529 | });
530 | }*/
531 |
532 |
533 |
534 | hasStopMoving = false;
535 | hasClearStopMoving = false;
536 |
537 | extendBottom = false;
538 | hasReachBottomBefore = false;
539 | hasBeyondMaxScrollTop = false;
540 |
541 |
542 | onContentScroll(event: ScrollEvent, isMoveEnd: boolean) {
543 |
544 | // For Ionic 3
545 | let maxScrollTop = this.content._scrollContent.nativeElement.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0);
546 | // For Ionic 2
547 | //let maxScrollTop = this.content._scrollEle.scrollHeight - (event.contentTop + event.contentHeight + event.contentBottom) - (this.extendBottom ? this.footerHeight : 0); // For ionic2
548 |
549 |
550 | var duration = 0;
551 | let scrollTopDiff = event.scrollTop - this.scrollTopPrev;
552 |
553 | let y = (event.scrollTop >= 0) ? constrain(this.yPrev + scrollTopDiff, 0, this.defaultEnd) : 0;
554 |
555 |
556 | //console.log(event.scrollTop + ", maxScrollTop: " + maxScrollTop + ", isMoveEnd: " + isMoveEnd);
557 |
558 |
559 |
560 | //----------------------------------------------------------------------------------------
561 |
562 | if (event.scrollTop >= maxScrollTop) {
563 | if(this.hasReachBottomBefore) {
564 | //console.log("--> 1 show");
565 | y = 0;
566 | duration = this.defaultDuration;
567 | }
568 | if(isMoveEnd) {
569 | //console.log("--> 1 extend padding-bottom");
570 | this.hasReachBottomBefore = true;
571 | this.extendBottom = true;
572 | }
573 | }
574 |
575 |
576 | // Reset bottom
577 | if (event.scrollTop < maxScrollTop - 40) {
578 | this.hasReachBottomBefore = false;
579 | this.extendBottom = false;
580 | //console.log("--> reset padding-bottom");
581 | }
582 |
583 | // Set bottom height
584 | if(this.extendBottom) {
585 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom + this.footerHeight) + "px");
586 | } else {
587 | this.content.setScrollElementStyle("padding-bottom", (this.content._pBottom) + "px");
588 | }
589 |
590 |
591 |
592 | //----------------------------------------------------------------------------------------
593 |
594 |
595 | //if previous and current y are the same, no need to continue
596 | if (this.yPrev !== y) {
597 | //this.modifyDom(y, duration, event.domWrite);
598 |
599 | let yDiff = y - this.yPrev;
600 | if(-30 < yDiff && yDiff < 30) {
601 | this.modifyDom(y, duration, event.domWrite);
602 | } else {
603 | this.modifyDom(y, 50, event.domWrite);
604 | }
605 | }
606 |
607 | //console.log({ y: y, scrollTop: event.scrollTop, maxScrollTop: maxScrollTop});
608 |
609 |
610 | this.yPrev = y;
611 | this.scrollTopPrev = event.scrollTop;
612 | }
613 |
614 |
615 |
616 |
617 |
618 | private modifyDom(y: number, duration: number, dowWrite: DomWrite) {
619 | dowWrite(() => {
620 | y = constrain(y, 0, this.endY);
621 |
622 |
623 |
624 | // Header Items
625 | this.headerItems.forEach((item) => {
626 |
627 | let dy_transit = constrain((y - item.transition.start), 0, item.transition.interval);
628 | let dy_move = constrain((y - item.transition.end), 0, Infinity);
629 |
630 | //console.log(y, "dy_transit:", dy_transit, "dy_move:", dy_move, TransitionType[item.transition.type]);
631 |
632 | if(item.type === ItemType.Tabbar) {
633 |
634 | let val = constrain((dy_transit / item.height), 0, 1);
635 | $(item.ele).css({
636 | height: item.height - dy_transit,
637 | })
638 | this.translateElementY(item.ele, -dy_move, duration);
639 |
640 | } else {
641 |
642 | if(item.transition.type === TransitionType.Shrink) {
643 | // Shrinking
644 |
645 | let val = constrain((dy_transit / item.height), 0, 1);
646 | $(item.ele).css({
647 | height: item.height - dy_transit,
648 | marginBottom: 0, // marginBottom: dy_transit,
649 | })
650 | this.translateElementY(item.ele, 0, duration);
651 |
652 |
653 | $(item.ele).children().not(".toolbar-background, .toolbar-content")
654 | .each((index, navbarChild) => {
655 | $(navbarChild).css("transform", `scale(${1 - val})`);
656 | });
657 | $(item.ele).children(".toolbar-content").children()
658 | .each((index, navbarChild) => {
659 | $(navbarChild).css("transform", `scale(${1 - val})`);
660 | });
661 |
662 | } else {
663 | // Translating
664 |
665 | let val = constrain((dy_transit / item.height), 0, 1);
666 |
667 | $(item.ele).css({
668 | height: item.height,
669 | marginBottom: -dy_transit, // marginBottom: 0,
670 | })
671 | this.translateElementY(item.ele, -dy_transit, duration);
672 |
673 |
674 | $(item.ele).children().not(".toolbar-background")
675 | .each((index, navbarChild) => {
676 | $(navbarChild).css("opacity", 1 - val);
677 | });
678 | }
679 |
680 |
681 | }
682 | });
683 |
684 |
685 | // Footer Items
686 | this.footerItems.forEach((item) => {
687 | this.translateElementY(item.ele, y, duration);
688 | });
689 | });
690 | }
691 |
692 | private translateElementY(ele: HTMLElement, y: number, duration: number) {
693 | this.renderer.setElementStyle(ele, "webkitTransform", `translate3d(0, ${y}px,0)`);
694 |
695 | if (duration && !ele.style.transitionDuration) {
696 | ele.style.transitionDuration = duration + "ms";
697 | setTimeout(() => {
698 | ele.style.transitionDuration = "";
699 | }, this.defaultDelay);
700 | }
701 | }
702 | }
703 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ionic App
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ionic",
3 | "short_name": "Ionic",
4 | "start_url": "index.html",
5 | "display": "standalone",
6 | "icons": [{
7 | "src": "assets/imgs/logo.png",
8 | "sizes": "512x512",
9 | "type": "image/png"
10 | }],
11 | "background_color": "#4e8ef7",
12 | "theme_color": "#4e8ef7"
13 | }
--------------------------------------------------------------------------------
/src/pages/about/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | About
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Help
17 |
18 |
19 |
20 |
21 | Solid
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Paid
32 | Free
33 | Top
34 |
35 |
36 |
37 |
38 |
39 |
40 | Welcome to Ionic!
41 |
42 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
43 |
44 |
45 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
46 |
47 |
48 |
49 | Welcome to Ionic!
50 |
51 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
52 |
53 |
54 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
55 |
56 |
57 |
58 | Welcome to Ionic!
59 |
60 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
61 |
62 |
63 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
64 |
65 |
66 |
67 | Don’t Miss These Navigation Bar Interactions in iOS8
68 |
69 | Have you noticed how nicely the mobile Safari navigation bar condenses on scroll, and how the tab bar disappears?
70 |
71 | Video Player
72 | 00:1400:19
73 | In iOS8, Apple has made this type of interaction (and more!) very easily available to us all – well, almost… While Apple demoed the condensing navigation bar at WWDC, they have since changed it to hiding the navigation instead, and the tab bar is not included (I’m guessing they’ll add separate tab bar hiding properties later on…).
74 |
75 | Here are the cool new navigation bar interactions in iOS8 that will allow the user to see more of your content:
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/src/pages/about/about.scss:
--------------------------------------------------------------------------------
1 | page-about {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/src/pages/about/about.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NavController, ViewController } from 'ionic-angular';
3 |
4 | @Component({
5 | selector: 'page-about',
6 | templateUrl: 'about.html'
7 | })
8 | export class AboutPage {
9 |
10 | constructor(public navCtrl: NavController,
11 | public viewCtrl: ViewController) {
12 |
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/pages/contact/contact.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Contact
5 |
6 |
7 |
8 |
9 |
10 |
11 | Follow us on Twitter
12 |
13 |
14 | @ionicframework
15 |
16 | homePage
17 |
18 | Welcome to Ionic!
19 |
20 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
21 |
22 |
23 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
24 |
25 |
26 |
27 | Don’t Miss These Navigation Bar Interactions in iOS8
28 |
29 | Have you noticed how nicely the mobile Safari navigation bar condenses on scroll, and how the tab bar disappears?
30 |
31 | Video Player
32 | 00:1400:19
33 | In iOS8, Apple has made this type of interaction (and more!) very easily available to us all – well, almost… While Apple demoed the condensing navigation bar at WWDC, they have since changed it to hiding the navigation instead, and the tab bar is not included (I’m guessing they’ll add separate tab bar hiding properties later on…).
34 |
35 | Here are the cool new navigation bar interactions in iOS8 that will allow the user to see more of your content:
36 |
37 | Welcome to Ionic!
38 |
39 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
40 |
41 |
42 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
43 |
44 |
45 |
46 | Don’t Miss These Navigation Bar Interactions in iOS8
47 |
48 | Have you noticed how nicely the mobile Safari navigation bar condenses on scroll, and how the tab bar disappears?
49 |
50 | Video Player
51 | 00:1400:19
52 | In iOS8, Apple has made this type of interaction (and more!) very easily available to us all – well, almost… While Apple demoed the condensing navigation bar at WWDC, they have since changed it to hiding the navigation instead, and the tab bar is not included (I’m guessing they’ll add separate tab bar hiding properties later on…).
53 |
54 | Here are the cool new navigation bar interactions in iOS8 that will allow the user to see more of your content:
55 |
56 | Welcome to Ionic!
57 |
58 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
59 |
60 |
61 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
62 |
63 |
64 |
65 | Don’t Miss These Navigation Bar Interactions in iOS8
66 |
67 | Have you noticed how nicely the mobile Safari navigation bar condenses on scroll, and how the tab bar disappears?
68 |
69 | Video Player
70 | 00:1400:19
71 | In iOS8, Apple has made this type of interaction (and more!) very easily available to us all – well, almost… While Apple demoed the condensing navigation bar at WWDC, they have since changed it to hiding the navigation instead, and the tab bar is not included (I’m guessing they’ll add separate tab bar hiding properties later on…).
72 |
73 | Here are the cool new navigation bar interactions in iOS8 that will allow the user to see more of your content:
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/pages/contact/contact.scss:
--------------------------------------------------------------------------------
1 | page-contact {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/src/pages/contact/contact.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NavController } from 'ionic-angular';
3 |
4 | import { HomePage } from './../home/home';
5 |
6 |
7 | @Component({
8 | selector: 'page-contact',
9 | templateUrl: 'contact.html'
10 | })
11 | export class ContactPage {
12 |
13 | homePage = HomePage;
14 |
15 | constructor(public navCtrl: NavController) {
16 |
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/pages/home/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home
5 |
6 |
7 |
8 |
9 |
10 |
11 |
47 |
48 |
49 |
50 |
80 |
81 |
82 |
83 |
84 |
85 | Welcome to Ionic!
86 |
87 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
88 |
89 |
90 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
91 |
92 |
93 | Goto
94 | Change Root
95 |
96 | Welcome to Ionic!
97 |
98 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
99 |
100 |
101 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
102 |
103 |
104 |
105 | Welcome to Ionic!
106 |
107 | This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
108 |
109 |
110 | Take a look at the src/pages/
directory to add or change tabs, update any existing page or create new pages.
111 |
112 |
113 |
114 | Don’t Miss These Navigation Bar Interactions in iOS8
115 |
116 | Have you noticed how nicely the mobile Safari navigation bar condenses on scroll, and how the tab bar disappears?
117 |
118 | Video Player
119 | 00:1400:19
120 | In iOS8, Apple has made this type of interaction (and more!) very easily available to us all – well, almost… While Apple demoed the condensing navigation bar at WWDC, they have since changed it to hiding the navigation instead, and the tab bar is not included (I’m guessing they’ll add separate tab bar hiding properties later on…).
121 |
122 | Here are the cool new navigation bar interactions in iOS8 that will allow the user to see more of your content:
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/src/pages/home/home.scss:
--------------------------------------------------------------------------------
1 | page-home {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/src/pages/home/home.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NavController, ViewController } from 'ionic-angular';
3 |
4 |
5 | import { ContactPage } from './../contact/contact';
6 |
7 |
8 |
9 | @Component({
10 | selector: 'page-home',
11 | templateUrl: 'home.html'
12 | })
13 | export class HomePage {
14 |
15 | page = ContactPage;
16 |
17 |
18 | constructor(public navCtrl: NavController,
19 | public viewCtrl: ViewController) {
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/pages/tabs/tabs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/pages/tabs/tabs.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { AboutPage } from '../about/about';
4 | import { ContactPage } from '../contact/contact';
5 | import { HomePage } from '../home/home';
6 |
7 | @Component({
8 | templateUrl: 'tabs.html'
9 | })
10 | export class TabsPage {
11 |
12 | tab1Root = HomePage;
13 | tab2Root = AboutPage;
14 | tab3Root = ContactPage;
15 |
16 | constructor() {
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Check out https://googlechrome.github.io/sw-toolbox/ for
3 | * more info on how to use sw-toolbox to custom configure your service worker.
4 | */
5 |
6 |
7 | 'use strict';
8 | importScripts('./build/sw-toolbox.js');
9 |
10 | self.toolbox.options.cache = {
11 | name: 'ionic-cache'
12 | };
13 |
14 | // pre-cache our key assets
15 | self.toolbox.precache(
16 | [
17 | './build/main.js',
18 | './build/main.css',
19 | './build/polyfills.js',
20 | 'index.html',
21 | 'manifest.json'
22 | ]
23 | );
24 |
25 | // dynamically cache any other local assets
26 | self.toolbox.router.any('/*', self.toolbox.cacheFirst);
27 |
28 | // for any other requests go to the network, cache,
29 | // and then only use that cached resource if your user goes offline
30 | self.toolbox.router.default = self.toolbox.networkFirst;
31 |
--------------------------------------------------------------------------------
/src/theme/variables.scss:
--------------------------------------------------------------------------------
1 | // Ionic Variables and Theming. For more info, please see:
2 | // http://ionicframework.com/docs/v2/theming/
3 | $font-path: "../assets/fonts";
4 |
5 | @import "ionic.globals";
6 |
7 |
8 | // Shared Variables
9 | // --------------------------------------------------
10 | // To customize the look and feel of this app, you can override
11 | // the Sass variables found in Ionic's source scss files.
12 | // To view all the possible Ionic variables, see:
13 | // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/
14 |
15 |
16 |
17 |
18 | // Named Color Variables
19 | // --------------------------------------------------
20 | // Named colors makes it easy to reuse colors on various components.
21 | // It's highly recommended to change the default colors
22 | // to match your app's branding. Ionic uses a Sass map of
23 | // colors so you can add, rename and remove colors as needed.
24 | // The "primary" color is the only required color in the map.
25 |
26 | $colors: (
27 | primary: #488aff,
28 | secondary: #32db64,
29 | danger: #f53d3d,
30 | light: #f4f4f4,
31 | dark: #222
32 | );
33 |
34 |
35 | // App iOS Variables
36 | // --------------------------------------------------
37 | // iOS only Sass variables can go here
38 |
39 |
40 |
41 |
42 | // App Material Design Variables
43 | // --------------------------------------------------
44 | // Material Design only Sass variables can go here
45 |
46 |
47 |
48 |
49 | // App Windows Variables
50 | // --------------------------------------------------
51 | // Windows only Sass variables can go here
52 |
53 |
54 |
55 |
56 | // App Theme
57 | // --------------------------------------------------
58 | // Ionic apps can have different themes applied, which can
59 | // then be future customized. This import comes last
60 | // so that the above variables are used and Ionic's
61 | // default are overridden.
62 |
63 | @import "ionic.theme.default";
64 |
65 |
66 | // Ionicons
67 | // --------------------------------------------------
68 | // The premium icon font for Ionic. For more info, please see:
69 | // http://ionicframework.com/docs/v2/ionicons/
70 |
71 | @import "ionic.ionicons";
72 |
73 |
74 | // Fonts
75 | // --------------------------------------------------
76 |
77 | @import "roboto";
78 | @import "noto-sans";
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "lib": [
8 | "dom",
9 | "es2015"
10 | ],
11 | "module": "es2015",
12 | "moduleResolution": "node",
13 | "sourceMap": true,
14 | "target": "es5"
15 | },
16 | "include": [
17 | "src/**/*.ts"
18 | ],
19 | "exclude": [
20 | "node_modules"
21 | ],
22 | "compileOnSave": false,
23 | "atom": {
24 | "rewriteTsconfig": false
25 | },
26 | "files": [
27 | "typings/index.d.ts"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-duplicate-variable": true,
4 | "no-unused-variable": [
5 | true
6 | ]
7 | },
8 | "rulesDirectory": [
9 | "node_modules/tslint-eslint-rules/dist/rules"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "globalDependencies": {
3 | "jquery": "registry:dt/jquery#1.10.0+20170310222111"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/typings/globals/jquery/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "resolution": "main",
3 | "tree": {
4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f4fdcaca9c94f90442dcedb0c8a84399c47e731f/jquery/index.d.ts",
5 | "raw": "registry:dt/jquery#1.10.0+20170310222111",
6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f4fdcaca9c94f90442dcedb0c8a84399c47e731f/jquery/index.d.ts"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------