├── index.ts
├── bower.json
├── package.json
├── README.md
└── lib
└── ng2-SmoothScroll.directive.ts
/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/ng2-SmoothScroll.directive';
2 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng2SmoothScroll",
3 | "version": "2.0.0",
4 | "authors": [
5 | "kavil@qq.com"
6 | ],
7 | "description": "A pure-javascript library and set of directives to scroll smoothly to an element with easing.",
8 | "main": "lib/ng2-SmoothScroll.directive.ts",
9 | "keywords": [
10 | "angularjs2",
11 | "smooth scroll",
12 | "scrolling",
13 | "scroll effects",
14 | "scroll animations",
15 | "smooth",
16 | "scroll"
17 | ],
18 | "license": "MIT",
19 | "homepage": "https://github.com/kavil/ng2SmoothScroll",
20 | "ignore": [
21 | "**/.*",
22 | "node_modules",
23 | "bower_components",
24 | "test",
25 | "tests"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng2-smooth-scroll",
3 | "version": "2.0.0",
4 | "description": "A pure-javascript library and set of directives to scroll smoothly to an element with easing.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/kavil/ng2SmoothScroll.git"
12 | },
13 | "keywords": [
14 | "angularjs2",
15 | "smooth scroll",
16 | "scrolling",
17 | "scroll effects",
18 | "scroll animations",
19 | "smooth",
20 | "scroll"
21 | ],
22 | "author": "kavil",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/kavil/ng2SmoothScroll/issues"
26 | },
27 | "homepage": "https://github.com/kavil/ng2SmoothScroll#readme"
28 | }
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Angular2 smooth scroll
2 | ==============
3 |
4 | > base on[Angular smooth scroll](https://github.com/d-oliveros/ngSmoothScroll)
5 |
6 | A pure-javascript library and set of directives to scroll smoothly to an element with easing. Easing support contributed by Willem Liu with code from Gaëtan Renaudeau.
7 |
8 | No jQuery required.
9 |
10 | # Features
11 |
12 | * Exposes a service that scrolls the window to an element's location
13 | * Provides two directives that enable smooth scrolling to elements.
14 | * Clean: No classes are added, no jQuery is required, no CSS files or configuration is needed.
15 | * Scrolling within a custom container added in 2.0.0
16 |
17 | # Installation
18 |
19 | ```js
20 | // bower:
21 | import { SmoothScrollToDirective, SmoothScrollDirective } from "ng2SmoothScroll";
22 |
23 | // npm:
24 | import { SmoothScrollToDirective, SmoothScrollDirective } from "ng2-smooth-scroll";
25 | ...
26 | declarations[
27 | ...
28 | SmoothScrollToDirective,
29 | SmoothScrollDirective,
30 | ...
31 | ]
32 | ```
33 |
34 | # Bower
35 |
36 | Install with bower with:
37 |
38 | ```bash
39 | bower install ng2SmoothScroll
40 | ```
41 |
42 | # Npm
43 |
44 | Install with npm with:
45 |
46 | ```bash
47 | npm install ng2-smooth-scroll
48 | ```
49 |
50 | # Usage - As a directive
51 |
52 | This module provides two directives:
53 |
54 | #### smoothScroll:
55 |
56 | Attribute. Scrolls the window to this element, optionally validating the expression inside scroll-if.
57 |
58 | Example:
59 | ```html
60 |
61 | // Basic - The window will scroll to this element's position when compiling this directive
62 |
63 |
64 | // With options
65 |
72 | {{...}}
73 |
74 |
75 | // Inside a custom container
76 |
83 | {{...}}
84 |
85 |
86 | // With condition
87 |
89 | {{...}}
90 |
91 |
92 | // Inside ng-repeat
93 |
96 | {{...}}
97 |
98 | ```
99 |
100 | ####scrollTo:
101 |
102 | Attribute. Scrolls the window to the specified element ID when clicking this element.
103 |
104 | Example:
105 | ```html
106 |
107 | // Basic
108 |
110 | Click me!
111 |
112 |
113 | // Custom containers
114 |
117 | Click me!
118 |
119 |
120 | // onClick for non-anchor tags
121 |
123 | Click me!
124 |
125 |
126 | // With options
127 |
135 |
136 |
137 | ```
138 |
139 | ### Options
140 |
141 | #### duration
142 | Type: `Integer`
143 | Default: `800`
144 |
145 | The duration of the smooth scroll, in miliseconds.
146 |
147 | #### offset
148 | Type: `Integer`
149 | Default: `0`
150 |
151 | The offset from the top of the page in which the scroll should stop.
152 |
153 | #### easing
154 | type: `string`
155 | default: `easeInOutQuart`
156 |
157 | the easing function to be used for this scroll.
158 |
159 | #### middleAlign
160 | type: `boolean`
161 | default: `false`
162 |
163 | Middle align the scrolled element
164 |
165 | #### scrollOnClick
166 | type: `boolean`
167 | default: `false`
168 |
169 | (smoothScroll directive only) Scroll to element when it is clicked
170 |
171 | #### callbackBefore
172 | type: `function`
173 | default: `function(element) {}`
174 |
175 | a callback function to run before the scroll has started. It is passed the
176 | element that will be scrolled to.
177 |
178 | #### callbackAfter
179 | type: `function`
180 | default: `function(element) {}`
181 |
182 | a callback function to run after the scroll has completed. It is passed the
183 | element that was scrolled to.
184 |
185 | #### containerId
186 | type: `string`
187 | default: null
188 |
189 | ID of the scrollable container which the element is a child of.
190 |
191 | ### Easing functions
192 |
193 | The available easing functions are:
194 | * 'easeInQuad'
195 | * 'easeOutQuad'
196 | * 'easeInOutQuad'
197 | * 'easeInCubic'
198 | * 'easeOutCubic'
199 | * 'easeInOutCubic'
200 | * 'easeInQuart'
201 | * 'easeOutQuart'
202 | * 'easeInOutQuart'
203 | * 'easeInQuint'
204 | * 'easeOutQuint'
205 | * 'easeInOutQuint'
206 |
207 | Cheers.
208 |
--------------------------------------------------------------------------------
/lib/ng2-SmoothScroll.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, Input, HostListener, OnInit, ElementRef } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[scrollTo]'
5 | })
6 | export class SmoothScrollToDirective {
7 | targetElement: any;
8 |
9 | constructor() {}
10 |
11 | @Input('scrollTo') public scrollTo: string;
12 | @Input('duration') public duration: number;
13 | @Input('offset') public offset: number;
14 | @Input('easing') public easing: string;
15 | @Input('callbackBefore') public callbackBefore: any;
16 | @Input('callbackAfter') public callbackAfter: any;
17 | @Input('containerId') public containerId: string;
18 | @Input('middleAlign') public middleAlign: any;
19 |
20 | @HostListener('click') onClick() {
21 | this.targetElement = document.getElementById(this.scrollTo);
22 | if (!this.targetElement) return;
23 |
24 | new SmoothScroll(this.targetElement, {
25 | duration: this.duration,
26 | offset: this.offset,
27 | easing: this.easing,
28 | callbackBefore: this.callbackBefore,
29 | callbackAfter: this.callbackAfter,
30 | containerId: this.containerId,
31 | middleAlign: this.middleAlign
32 | });
33 | };
34 |
35 | }
36 |
37 | @Directive({
38 | selector: '[smoothScroll]'
39 | })
40 | export class SmoothScrollDirective implements OnInit {
41 | private el;
42 |
43 | constructor(el: ElementRef) {
44 | this.el = el;
45 | }
46 |
47 | @Input('scrollIf') public scrollIf: boolean;
48 | @Input('duration') public duration: number;
49 | @Input('offset') public offset: number;
50 | @Input('easing') public easing: string;
51 | @Input('callbackBefore') public callbackBefore: any;
52 | @Input('callbackAfter') public callbackAfter: any;
53 | @Input('containerId') public containerId: string;
54 | @Input('scrollOnClick') public scrollOnClick: boolean;
55 | @Input('middleAlign') public middleAlign: any;
56 |
57 | @HostListener('click', ['$event.target']) onClick(target) {
58 | if (this.scrollOnClick) {
59 | this.scroll();
60 | }
61 | };
62 |
63 | public ngOnInit() {
64 | this.scroll();
65 | }
66 |
67 | private scroll() {
68 | if (typeof this.scrollIf === 'undefined' || this.scrollIf === true) {
69 | setTimeout(() => {
70 | new SmoothScroll(this.el.nativeElement, {
71 | duration: this.duration,
72 | offset: this.offset,
73 | easing: this.easing,
74 | callbackBefore: this.callbackBefore,
75 | callbackAfter: this.callbackAfter,
76 | containerId: this.containerId,
77 | middleAlign: this.middleAlign
78 | });
79 | }, 0);
80 | }
81 | }
82 |
83 | }
84 |
85 |
86 | export class SmoothScroll {
87 | constructor(element: any, options: any) {
88 | this.smoothScroll(element, options);
89 | }
90 | private smoothScroll(element, options) {
91 | options = options || {};
92 |
93 | // Options
94 | let duration = options.duration || 800,
95 | offset = options.offset || 0,
96 | easing = options.easing || 'easeInOutQuart',
97 | callbackBefore = options.callbackBefore || function(){},
98 | callbackAfter = options.callbackAfter || function(){},
99 | container = document.getElementById(options.containerId) || null,
100 | containerPresent = (container != undefined && container != null),
101 | middleAlign = options.middleAlign || false;
102 |
103 | /**
104 | * Retrieve current location
105 | */
106 | let getScrollLocation = function () {
107 | if (containerPresent) {
108 | return container.scrollTop;
109 | } else {
110 | if (window.pageYOffset) {
111 | return window.pageYOffset;
112 | } else {
113 | return document.documentElement.scrollTop;
114 | }
115 | }
116 | };
117 |
118 | /**
119 | * Calculate easing pattern.
120 | *
121 | * 20150713 edit - zephinzer
122 | * - changed if-else to switch
123 | * @see http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
124 | */
125 | let getEasingPattern = function (type, time) {
126 | switch (type) {
127 | case 'easeInQuad': return time * time; // accelerating from zero velocity
128 | case 'easeOutQuad': return time * (2 - time); // decelerating to zero velocity
129 | case 'easeInOutQuad': return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
130 | case 'easeInCubic': return time * time * time; // accelerating from zero velocity
131 | case 'easeOutCubic': return (--time) * time * time + 1; // decelerating to zero velocity
132 | case 'easeInOutCubic': return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
133 | case 'easeInQuart': return time * time * time * time; // accelerating from zero velocity
134 | case 'easeOutQuart': return 1 - (--time) * time * time * time; // decelerating to zero velocity
135 | case 'easeInOutQuart': return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
136 | case 'easeInQuint': return time * time * time * time * time; // accelerating from zero velocity
137 | case 'easeOutQuint': return 1 + (--time) * time * time * time * time; // decelerating to zero velocity
138 | case 'easeInOutQuint': return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
139 | default: return time;
140 | }
141 | };
142 |
143 | /**
144 | * Calculate how far to scroll
145 | */
146 | let getEndLocation = function (element) {
147 | let location = 0,
148 | elementRect = element.getBoundingClientRect(),
149 | absoluteElementTop = elementRect.top + window.pageYOffset;
150 |
151 | if (middleAlign) {
152 | location = (absoluteElementTop + (element.offsetHeight / 2)) - (window.innerHeight / 2);
153 | } else {
154 | location = absoluteElementTop;
155 | }
156 |
157 | if (offset) {
158 | location = location - offset;
159 | }
160 |
161 | return Math.max(location, 0);
162 | };
163 |
164 | // Initialize the whole thing
165 | setTimeout(function () {
166 | let currentLocation = null,
167 | startLocation = getScrollLocation(),
168 | endLocation = getEndLocation(element),
169 | timeLapsed = 0,
170 | distance = endLocation - startLocation,
171 | percentage,
172 | position,
173 | scrollHeight,
174 | internalHeight;
175 |
176 | /**
177 | * Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page)
178 | */
179 | let stopAnimation = function () {
180 | currentLocation = getScrollLocation();
181 | if (containerPresent) {
182 | scrollHeight = container.scrollHeight;
183 | internalHeight = container.clientHeight + currentLocation;
184 | } else {
185 | scrollHeight = document.body.scrollHeight;
186 | internalHeight = window.innerHeight + currentLocation;
187 | }
188 |
189 | if (
190 | ( // condition 1
191 | position == endLocation
192 | ) ||
193 | ( // condition 2
194 | currentLocation == endLocation
195 | ) ||
196 | ( // condition 3
197 | internalHeight > scrollHeight
198 | )
199 | ) { // stop
200 | clearInterval(runAnimation);
201 |
202 | callbackAfter(element);
203 | }
204 | };
205 |
206 | /**
207 | * Scroll the page by an increment, and check if it's time to stop
208 | */
209 | let animateScroll = function () {
210 | timeLapsed += 16;
211 | percentage = (timeLapsed / duration);
212 | percentage = (percentage > 1) ? 1 : percentage;
213 | position = startLocation + (distance * getEasingPattern(easing, percentage));
214 | if (containerPresent) {
215 | container.scrollTop = position;
216 | } else {
217 | window.scrollTo(0, position);
218 | }
219 | stopAnimation();
220 | };
221 |
222 | callbackBefore(element);
223 |
224 | let runAnimation = setInterval(animateScroll, 16);
225 | }, 0);
226 |
227 | }
228 | }
229 |
--------------------------------------------------------------------------------