-
Notifications
You must be signed in to change notification settings - Fork 27
/
index.js
148 lines (134 loc) · 4.58 KB
/
index.js
1
2
3
4
5
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['exports'], factory)
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
factory(exports)
} else {
factory((root.commonJsStrict = {}))
}
}(typeof self !== 'undefined' ? self : this, function (exports) {
exports.addBackToTop = addBackToTop
// FUNCTION START
'use strict'
function addBackToTop (params = {}) {
const {
backgroundColor = '#000',
cornerOffset = 20, // px
diameter = 56, // px
ease = inOutSine, // any one from https://www.npmjs.com/package/ease-component will do
id = 'back-to-top',
innerHTML = '<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></svg>', // https://material.io/icons/#ic_keyboard_arrow_up
onClickScrollTo = 0, // px, or a function returning number
scrollContainer = document.body, // or a DOM element, e.g., document.getElementById('content')
scrollDuration = 100, // ms
showWhenScrollTopIs = 1, // px
size = diameter, // alias for diameter
textColor = '#fff',
zIndex = 1
} = params
const scrollContainerIsBody = scrollContainer === document.body
const scrollDocumentElement = scrollContainerIsBody && document.documentElement
appendStyles()
const upEl = appendElement()
let hidden = true
const scrollEmitter = scrollContainerIsBody ? window : scrollContainer
scrollEmitter.addEventListener('scroll', adapt)
adapt()
function adapt () {
getScrollTop() >= showWhenScrollTopIs ?
show() :
hide()
}
function show () {
if (!hidden) { return }
upEl.className = ''
hidden = false
}
function hide () {
if (hidden) { return }
upEl.className = 'hidden'
hidden = true
}
function appendElement () {
const upEl = document.createElement('div')
upEl.id = id
upEl.className = 'hidden'
upEl.innerHTML = innerHTML
upEl.addEventListener('click', event => {
event.preventDefault()
scrollUp()
})
document.body.appendChild(upEl)
return upEl
}
function appendStyles () {
const svgSize = Math.round(0.43 * size)
const svgTop = Math.round(0.29 * size)
const styles = /*css*/`
#${id} {
background: ${backgroundColor};
border-radius: 50%;
bottom: ${cornerOffset}px;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26);
color: ${textColor};
cursor: pointer;
display: block;
height: ${size}px;
opacity: 1;
outline: none;
position: fixed;
right: ${cornerOffset}px;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
transition: bottom 0.2s, opacity 0.2s;
user-select: none;
width: ${size}px;
z-index: ${zIndex};
}
#${id} svg {
display: block;
fill: currentColor;
height: ${svgSize}px;
margin: ${svgTop}px auto 0;
width: ${svgSize}px;
}
#${id}.hidden {
bottom: -${size}px;
opacity: 0;
}
`
const styleEl = document.createElement('style')
styleEl.appendChild(document.createTextNode(styles))
document.head.insertAdjacentElement('afterbegin', styleEl)
}
function scrollUp () {
const scrollTo = typeof onClickScrollTo === 'function' ? onClickScrollTo() : onClickScrollTo
const { performance, requestAnimationFrame } = window
if (scrollDuration <= 0 || typeof performance === 'undefined' || typeof requestAnimationFrame === 'undefined') {
return setScrollTop(scrollTo)
}
const start = performance.now()
const initScrollTop = getScrollTop()
const pxsToScrollBy = initScrollTop - scrollTo
requestAnimationFrame(step)
function step (timestamp) {
const progress = Math.min((timestamp - start) / scrollDuration, 1)
setScrollTop(initScrollTop - Math.round(ease(progress) * pxsToScrollBy))
if (progress < 1) { requestAnimationFrame(step) }
}
}
function getScrollTop () {
return scrollContainer.scrollTop || (scrollDocumentElement && document.documentElement.scrollTop || 0)
}
function setScrollTop (value) {
scrollContainer.scrollTop = value
if (scrollDocumentElement) {
document.documentElement.scrollTop = value
}
}
function inOutSine (n) {
return 0.5 * (1 - Math.cos(Math.PI * n))
}
}
// FUNCTION END
}))