-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgatsby-browser.js
104 lines (91 loc) · 2.89 KB
/
gatsby-browser.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
import mediumZoom from 'medium-zoom'
import '@fastly/performance-observer-polyfill/polyfill'
// @see https://github.com/francoischalifour/medium-zoom#options
const defaultOptions = {
margin: 24,
background: '#fff',
scrollOffset: 40,
container: null,
template: null,
zIndex: 999,
excludedSelector: null
}
// @see https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-images/src/constants.js#L1
const imageClass = '.gatsby-resp-image-image'
const FIRST_CONTENTFUL_PAINT = 'first-contentful-paint'
const ZOOM_STYLE_ID = 'medium-zoom-styles'
const TRANSITION_EFFECT = 'opacity 0.5s, transform .3s cubic-bezier(.2,0,.2,1)'
function onFCP(callback) {
// @see https://developers.google.com/web/updates/2016/06/performance-observer
if (!window.performance || !window.PerformanceObserver) {
return
}
const po = new PerformanceObserver(list =>
list
.getEntries()
.filter(({ entryType }) => entryType === 'paint')
.map(({ name }) => name === FIRST_CONTENTFUL_PAINT)
.forEach(callback)
)
try {
po.observe({ entryTypes: ['measure', 'paint'] })
} catch (e) {
console.error(e)
po.disconnect()
}
}
function injectStyles(options) {
const styleTag = document.querySelector(`#${ZOOM_STYLE_ID}`)
if (styleTag) {
return
}
const { zIndex } = options
const node = document.createElement('style')
const styles = `
.medium-zoom--opened > .medium-zoom-overlay,
.medium-zoom--opened > .medium-zoom-image,
img.medium-zoom-image--opened {
z-index: ${zIndex}
}
`
node.id = ZOOM_STYLE_ID
node.innerHTML = styles
document.head.appendChild(node)
}
function applyZoomEffect({ excludedSelector, includedSelector, ...options }) {
const imagesSelector = excludedSelector
? `${imageClass}:not(${excludedSelector})`
: imageClass
let imageElements = Array.from(document.querySelectorAll(imagesSelector))
if (includedSelector) {
const includedEls = Array.from(document.querySelectorAll(includedSelector))
imageElements = imageElements.concat(includedEls)
}
const images = imageElements
.filter(el => !el.classList.contains('medium-zoom-image'))
.map(el => {
function onImageLoad() {
const originalTransition = el.style.transition
el.style.transition = `${originalTransition}, ${TRANSITION_EFFECT}`
el.removeEventListener('load', onImageLoad)
}
el.addEventListener('load', onImageLoad)
el.setAttribute('tabIndex', 0)
el.addEventListener('keydown', e => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault()
el.click()
}
})
return el
})
if (images.length > 0) {
mediumZoom(images, options)
}
}
export const onRouteUpdate = (_, pluginOptions) => {
const options = { ...defaultOptions, ...pluginOptions }
injectStyles(options)
onFCP(() => applyZoomEffect(options))
applyZoomEffect(options)
}