Created
September 9, 2022 14:26
-
-
Save pertrai1/a68e953e2ef8d57f5bc888195cae49ee to your computer and use it in GitHub Desktop.
Back Top
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<a id="back-to-top" | |
class="scroll-top scroll-top-link"> | |
<i class="fa fa-arrow-circle-up"></i> | |
</a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.scroll-top { | |
position: fixed; | |
bottom: 65px; | |
right: 15px; | |
display: inline-block; | |
&-link { | |
visibility: hidden; | |
opacity: 0; | |
transition: opacity 0.3s, visibility 0.3s; | |
z-index: 1001; | |
.fa-arrow-circle-up { | |
color: #5b616b; | |
font-size: 3rem; | |
} | |
} | |
&-is-visible { | |
visibility: visible; | |
opacity: 1; | |
} | |
&-fade-out { | |
opacity: 0.5; | |
} | |
&:hover { | |
opacity: 1; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, HostListener, OnInit } from '@angular/core'; | |
@Component({ | |
selector: 'scroll-top', | |
templateUrl: './scroll-top.component.html', | |
styleUrls: ['./scroll-top.component.scss'] | |
}) | |
export class ScrollTopComponent implements OnInit { | |
public backTopEl: HTMLElement; | |
public offset = 300; | |
public offsetOpacity = 1200; | |
public scrollDuration = 700; | |
public scrolling = false; | |
public topIsVisibleCSS = 'scroll-top-is-visible'; | |
public topFadeOutCSS = 'scroll-top-is-visible scroll-top-fade-out'; | |
public ngOnInit(): void { | |
this.backTopEl = document.getElementById('back-to-top'); | |
} | |
@HostListener('window:scroll', []) | |
public onWindowScroll(): void { | |
if (this.backTopEl) { | |
if (!this.scrolling) { | |
this.scrolling = true; | |
!window.requestAnimationFrame | |
? setTimeout(() => { | |
const windowTop = document.documentElement.scrollTop; | |
windowTop > this.offset | |
? addClass(this.backTopEl, this.topIsVisibleCSS) | |
: removeClass(this.backTopEl, this.topFadeOutCSS); | |
/* eslint-disable-next-line */ | |
windowTop > this.offsetOpacity && | |
addClass(this.backTopEl, this.topFadeOutCSS); | |
this.scrolling = false; | |
}, 250) | |
: window.requestAnimationFrame(() => { | |
const windowTop = document.documentElement.scrollTop; | |
this.scrolling = false; | |
windowTop > this.offset | |
? addClass(this.backTopEl, this.topIsVisibleCSS) | |
: removeClass(this.backTopEl, this.topFadeOutCSS); | |
/* eslint-disable-next-line */ | |
windowTop > this.offsetOpacity && | |
addClass(this.backTopEl, this.topFadeOutCSS); | |
}); | |
} | |
} | |
} | |
@HostListener('click', ['$event']) | |
public scrollBackUp(event: Event): void { | |
event.preventDefault(); | |
event.stopPropagation(); | |
!window.requestAnimationFrame | |
? window.scrollTo(0, 0) | |
: this.scrollTo(0, this.scrollDuration); | |
} | |
public scrollTo( | |
final: number, | |
duration: number, | |
scrollCallback?: () => void | |
): void { | |
const start = window.scrollY || document.documentElement.scrollTop; | |
let currentTime = null; | |
const animateScroll = (timestamp: number) => { | |
if (!currentTime) { | |
currentTime = timestamp; | |
} | |
let progress = timestamp - currentTime; | |
if (progress > duration) { | |
progress = duration; | |
} | |
const val = easeInOutQuad(progress, start, final, duration); | |
window.scrollTo(0, val); | |
if (progress < duration) { | |
window.requestAnimationFrame(animateScroll); | |
} else { | |
/* eslint-disable-next-line */ | |
scrollCallback && scrollCallback(); | |
} | |
}; | |
window.requestAnimationFrame(animateScroll); | |
} | |
} | |
/** | |
* These take place of having to use jQuery. We can consider placing these in platform as there are more that can be added. | |
*/ | |
function hasClass(el: Element, className: string): boolean { | |
if (el.classList) { | |
return el.classList.contains(className); | |
} else { | |
return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|&)')); | |
} | |
} | |
function addClass(el: Element, className: string): void { | |
const classList = className.split(' '); | |
if (el.classList) { | |
el.classList.add(classList[0]); | |
} else if (hasClass(el, classList[0])) { | |
el.className += ' ' + classList[0]; | |
} | |
if (classList.length > 1) { | |
addClass(el, classList.slice(1).join(' ')); | |
} | |
} | |
function easeInOutQuad(t: number, b: number, c: number, d: number): number { | |
t /= d / 2; | |
if (t < 1) { | |
return (c / 2) * t * t + b; | |
} | |
t--; | |
return (-c / 2) * (t * (t - 2) - 1) * b; | |
} | |
function removeClass(el: Element, className: string): void { | |
const classList = className.split(' '); | |
if (el.classList) { | |
el.classList.remove(classList[0]); | |
} else if (hasClass(el, classList[0])) { | |
const cssClassName = new RegExp('(\\s|^)' + classList[0] + '(\\s|$)'); | |
el.className = el.className.replace(cssClassName, ' '); | |
} | |
if (classList.length > 1) { | |
removeClass(el, classList.slice(1).join(' ')); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment