Skip to content

Instantly share code, notes, and snippets.

@pertrai1
Created September 9, 2022 14:26
Show Gist options
  • Save pertrai1/a68e953e2ef8d57f5bc888195cae49ee to your computer and use it in GitHub Desktop.
Save pertrai1/a68e953e2ef8d57f5bc888195cae49ee to your computer and use it in GitHub Desktop.
Back Top
<a id="back-to-top"
class="scroll-top scroll-top-link">
<i class="fa fa-arrow-circle-up"></i>
</a>
.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;
}
}
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