fix: use smooth scroll polyfill in Chrome for scroll-to-top (#1601)
* fix: use smooth scroll polyfill in Chrome for scroll-to-top * rename thunk to __thunk__ for safety
This commit is contained in:
parent
0194a07823
commit
7c04b86405
|
@ -8,6 +8,7 @@ import replace from 'rollup-plugin-replace'
|
||||||
import fromPairs from 'lodash-es/fromPairs'
|
import fromPairs from 'lodash-es/fromPairs'
|
||||||
import babel from 'rollup-plugin-babel'
|
import babel from 'rollup-plugin-babel'
|
||||||
import { themes } from '../src/routes/_static/themes'
|
import { themes } from '../src/routes/_static/themes'
|
||||||
|
import terserOptions from './terserOptions'
|
||||||
|
|
||||||
const writeFile = promisify(fs.writeFile)
|
const writeFile = promisify(fs.writeFile)
|
||||||
|
|
||||||
|
@ -28,11 +29,7 @@ export async function buildInlineScript () {
|
||||||
runtimeHelpers: true,
|
runtimeHelpers: true,
|
||||||
presets: ['@babel/preset-env']
|
presets: ['@babel/preset-env']
|
||||||
}),
|
}),
|
||||||
!process.env.DEBUG && terser({
|
!process.env.DEBUG && terser(terserOptions)
|
||||||
mangle: true,
|
|
||||||
compress: true,
|
|
||||||
ecma: 8
|
|
||||||
})
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const { output } = await bundle.generate({
|
const { output } = await bundle.generate({
|
||||||
|
|
14
bin/terserOptions.js
Normal file
14
bin/terserOptions.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module.exports = {
|
||||||
|
ecma: 8,
|
||||||
|
mangle: true,
|
||||||
|
compress: {
|
||||||
|
pure_funcs: [
|
||||||
|
'console.log', // remove console logs in production
|
||||||
|
'__thunk__' // see thunk.js
|
||||||
|
]
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
comments: false
|
||||||
|
},
|
||||||
|
safari10: true
|
||||||
|
}
|
|
@ -333,7 +333,7 @@
|
||||||
// smooth scroll, so disable smooth scrolling
|
// smooth scroll, so disable smooth scrolling
|
||||||
scroller.scrollLeft = scrollLeft
|
scroller.scrollLeft = scrollLeft
|
||||||
} else {
|
} else {
|
||||||
smoothScroll(scroller, scrollLeft, true)
|
smoothScroll(scroller, scrollLeft, /* horizontal */ true, /* preferFast */ false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('setting scrollLeft', scrollLeft)
|
console.log('setting scrollLeft', scrollLeft)
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
if (index === 0 && movement === -1) {
|
if (index === 0 && movement === -1) {
|
||||||
activeItemKey = null
|
activeItemKey = null
|
||||||
this.set({ activeItemKey })
|
this.set({ activeItemKey })
|
||||||
smoothScroll(getScrollContainer(), 0)
|
smoothScroll(getScrollContainer(), 0, /* horizontal */ false, /* preferFast */ false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import emojiRegex from 'emoji-regex/es2015/text'
|
import emojiRegex from 'emoji-regex/es2015/text'
|
||||||
import { thunk } from './thunk'
|
import { __thunk__ } from './thunk'
|
||||||
|
|
||||||
export const getEmojiRegex = thunk(emojiRegex)
|
export const getEmojiRegex = __thunk__(emojiRegex)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { thunk } from './thunk'
|
import { __thunk__ } from './thunk'
|
||||||
|
|
||||||
export const handleRegex = thunk(() => /(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig)
|
export const handleRegex = __thunk__(() => /(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig)
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
|
@ -66,5 +66,5 @@ export function scrollIntoViewIfNeeded (element) {
|
||||||
}
|
}
|
||||||
const scrollContainer = getScrollContainer()
|
const scrollContainer = getScrollContainer()
|
||||||
const scrollTop = scrollContainer.scrollTop
|
const scrollTop = scrollContainer.scrollTop
|
||||||
smoothScroll(scrollContainer, scrollTop + rect.top - scrollY)
|
smoothScroll(scrollContainer, scrollTop + rect.top - scrollY, /* horizontal */ false, /* preferFast */ false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export function scrollToTop (smooth) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
smoothScroll(scroller, 0)
|
smoothScroll(scroller, 0, /* horizontal */ false, /* preferFast */ true)
|
||||||
} else {
|
} else {
|
||||||
scroller.scrollTop = 0
|
scroller.scrollTop = 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
|
import { isChrome } from './userAgent'
|
||||||
|
|
||||||
// via https://github.com/tootsuite/mastodon/blob/f59ed3a4fafab776b4eeb92f805dfe1fecc17ee3/app/javascript/mastodon/scroll.js
|
// via https://github.com/tootsuite/mastodon/blob/f59ed3a4fafab776b4eeb92f805dfe1fecc17ee3/app/javascript/mastodon/scroll.js
|
||||||
const easingOutQuint = (x, t, b, c, d) =>
|
const easingOutQuint = (x, t, b, c, d) =>
|
||||||
|
@ -63,16 +64,22 @@ function testSupportsSmoothScroll () {
|
||||||
|
|
||||||
export const hasNativeSmoothScroll = process.browser && testSupportsSmoothScroll()
|
export const hasNativeSmoothScroll = process.browser && testSupportsSmoothScroll()
|
||||||
|
|
||||||
export function smoothScroll (node, topOrLeft, horizontal) {
|
export function smoothScroll (node, topOrLeft, horizontal, preferFast) {
|
||||||
if (store.get().reduceMotion) {
|
if (store.get().reduceMotion) {
|
||||||
// don't do smooth-scroll at all for users who prefer reduced motion
|
console.log('smooth scroll: disabled')
|
||||||
|
// Don't do smooth-scroll at all for users who prefer reduced motion.
|
||||||
node[horizontal ? 'scrollLeft' : 'scrollTop'] = topOrLeft
|
node[horizontal ? 'scrollLeft' : 'scrollTop'] = topOrLeft
|
||||||
} else if (hasNativeSmoothScroll) {
|
} else if (hasNativeSmoothScroll && !(preferFast && isChrome())) {
|
||||||
|
// In some cases (e.g. scrolling to the top of the timeline), Chrome can take a really long time
|
||||||
|
// in their native smooth scroll implementation. If preferFast is true, just use the polyfill
|
||||||
|
// so we can control how long it takes.
|
||||||
|
console.log('smooth scroll: using native')
|
||||||
return node.scrollTo({
|
return node.scrollTo({
|
||||||
[horizontal ? 'left' : 'top']: topOrLeft,
|
[horizontal ? 'left' : 'top']: topOrLeft,
|
||||||
behavior: 'smooth'
|
behavior: 'smooth'
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
console.log('smooth scroll: using polyfill')
|
||||||
return smoothScrollPolyfill(node, horizontal ? 'scrollLeft' : 'scrollTop', topOrLeft)
|
return smoothScrollPolyfill(node, horizontal ? 'scrollLeft' : 'scrollTop', topOrLeft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// LocalStorage and IDB may be disabled in private mode, when "blocking cookies" in Safari,
|
// LocalStorage and IDB may be disabled in private mode, when "blocking cookies" in Safari,
|
||||||
// or other cases
|
// or other cases
|
||||||
|
|
||||||
import { thunk } from './thunk'
|
import { __thunk__ } from './thunk'
|
||||||
|
|
||||||
const testKey = '__test__'
|
const testKey = '__test__'
|
||||||
|
|
||||||
export const testHasLocalStorage = thunk(() => {
|
export const testHasLocalStorage = __thunk__(() => {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(testKey, testKey)
|
localStorage.setItem(testKey, testKey)
|
||||||
if (!localStorage.length || localStorage.getItem(testKey) !== testKey) {
|
if (!localStorage.length || localStorage.getItem(testKey) !== testKey) {
|
||||||
|
@ -18,7 +18,7 @@ export const testHasLocalStorage = thunk(() => {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
export const testHasIndexedDB = thunk(async () => {
|
export const testHasIndexedDB = __thunk__(async () => {
|
||||||
if (typeof indexedDB === 'undefined') {
|
if (typeof indexedDB === 'undefined') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export function thunk (func) {
|
// We name this __thunk__ so that we can tell terser that it's a pure function, without possibly
|
||||||
|
// affecting third-party libraries that may also be using a function called "thunk".
|
||||||
|
export function __thunk__ (func) {
|
||||||
let cached
|
let cached
|
||||||
let runOnce
|
let runOnce
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { thunk } from './thunk'
|
import { __thunk__ } from './thunk'
|
||||||
|
|
||||||
export const urlRegex = thunk(() => {
|
export const urlRegex = __thunk__(() => {
|
||||||
// this is provided at build time to avoid having a lot of runtime code just to build
|
// this is provided at build time to avoid having a lot of runtime code just to build
|
||||||
// a static regex
|
// a static regex
|
||||||
return process.env.URL_REGEX
|
return process.env.URL_REGEX
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
import { thunk } from './thunk'
|
import { __thunk__ } from './thunk'
|
||||||
|
|
||||||
export const isKaiOS = thunk(() => process.browser && /KAIOS/.test(navigator.userAgent))
|
export const isKaiOS = __thunk__(() => process.browser && /KAIOS/.test(navigator.userAgent))
|
||||||
|
|
||||||
export const isIOS = thunk(() => process.browser && /iP(?:hone|ad|od)/.test(navigator.userAgent))
|
export const isIOS = __thunk__(() => process.browser && /iP(?:hone|ad|od)/.test(navigator.userAgent))
|
||||||
|
|
||||||
export const isMac = thunk(() => process.browser && /mac/i.test(navigator.platform))
|
export const isMac = __thunk__(() => process.browser && /mac/i.test(navigator.platform))
|
||||||
|
|
||||||
// IntersectionObserver introduced in iOS 12.2 https://caniuse.com/#feat=intersectionobserver
|
// IntersectionObserver introduced in iOS 12.2 https://caniuse.com/#feat=intersectionobserver
|
||||||
export const isIOSPre12Point2 = thunk(() => process.browser && isIOS() &&
|
export const isIOSPre12Point2 = __thunk__(() => process.browser && isIOS() &&
|
||||||
!(typeof IntersectionObserver === 'function' &&
|
!(typeof IntersectionObserver === 'function' &&
|
||||||
IntersectionObserver.toString().includes('[native code]')))
|
IntersectionObserver.toString().includes('[native code]')))
|
||||||
|
|
||||||
// PointerEvent introduced in iOS 13 https://caniuse.com/#feat=pointer
|
// PointerEvent introduced in iOS 13 https://caniuse.com/#feat=pointer
|
||||||
export const isIOSPre13 = thunk(() => process.browser && isIOS() &&
|
export const isIOSPre13 = __thunk__(() => process.browser && isIOS() &&
|
||||||
!(typeof PointerEvent === 'function' &&
|
!(typeof PointerEvent === 'function' &&
|
||||||
PointerEvent.toString().includes('[native code]')))
|
PointerEvent.toString().includes('[native code]')))
|
||||||
|
|
||||||
export const isMobile = thunk(() => process.browser && navigator.userAgent.match(/(?:iPhone|iPod|iPad|Android|KAIOS)/))
|
export const isMobile = __thunk__(() => process.browser && navigator.userAgent.match(/(?:iPhone|iPod|iPad|Android|KAIOS)/))
|
||||||
|
|
||||||
|
export const isSafari = __thunk__(() => process.browser && /Safari/.test(navigator.userAgent) &&
|
||||||
|
!/Chrome/.test(navigator.userAgent))
|
||||||
|
|
||||||
|
export const isChrome = __thunk__(() => process.browser && /Chrome/.test(navigator.userAgent))
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { decode as decodeBlurHash } from 'blurhash'
|
import { decode as decodeBlurHash } from 'blurhash'
|
||||||
import registerPromiseWorker from 'promise-worker/register'
|
import registerPromiseWorker from 'promise-worker/register'
|
||||||
import { BLURHASH_RESOLUTION as RESOLUTION } from '../_static/blurhash'
|
import { BLURHASH_RESOLUTION as RESOLUTION } from '../_static/blurhash'
|
||||||
|
import { isChrome } from '../_utils/userAgent'
|
||||||
const isChrome = /Chrome/.test(navigator.userAgent)
|
|
||||||
|
|
||||||
// Disabled in Chrome because convertToBlob() is slow
|
// Disabled in Chrome because convertToBlob() is slow
|
||||||
// https://github.com/nolanlawson/pinafore/issues/1396
|
// https://github.com/nolanlawson/pinafore/issues/1396
|
||||||
const OFFSCREEN_CANVAS = !isChrome && typeof OffscreenCanvas === 'function'
|
const OFFSCREEN_CANVAS = !isChrome() && typeof OffscreenCanvas === 'function'
|
||||||
? new OffscreenCanvas(RESOLUTION, RESOLUTION) : null
|
? new OffscreenCanvas(RESOLUTION, RESOLUTION) : null
|
||||||
const OFFSCREEN_CANVAS_CONTEXT_2D = OFFSCREEN_CANVAS
|
const OFFSCREEN_CANVAS_CONTEXT_2D = OFFSCREEN_CANVAS
|
||||||
? OFFSCREEN_CANVAS.getContext('2d') : null
|
? OFFSCREEN_CANVAS.getContext('2d') : null
|
||||||
|
|
|
@ -3,11 +3,8 @@ import {
|
||||||
shell as __shell__,
|
shell as __shell__,
|
||||||
routes as __routes__
|
routes as __routes__
|
||||||
} from '../__sapper__/service-worker.js'
|
} from '../__sapper__/service-worker.js'
|
||||||
|
import { get, post } from './routes/_utils/ajax'
|
||||||
import {
|
import { isSafari } from './routes/_utils/userAgent'
|
||||||
get,
|
|
||||||
post
|
|
||||||
} from './routes/_utils/ajax'
|
|
||||||
|
|
||||||
const timestamp = process.env.SAPPER_TIMESTAMP
|
const timestamp = process.env.SAPPER_TIMESTAMP
|
||||||
const ASSETS = `assets_${timestamp}`
|
const ASSETS = `assets_${timestamp}`
|
||||||
|
@ -24,8 +21,6 @@ const ON_DEMAND_CACHE = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrom/.test(navigator.userAgent)
|
|
||||||
|
|
||||||
// `static` is an array of everything in the `static` directory
|
// `static` is an array of everything in the `static` directory
|
||||||
const assets = __assets__
|
const assets = __assets__
|
||||||
.map(file => file.startsWith('/') ? file : `/${file}`)
|
.map(file => file.startsWith('/') ? file : `/${file}`)
|
||||||
|
@ -163,7 +158,7 @@ self.addEventListener('fetch', event => {
|
||||||
// range request need to be be patched with a 206 response to satisfy
|
// range request need to be be patched with a 206 response to satisfy
|
||||||
// Safari (https://stackoverflow.com/questions/52087208)
|
// Safari (https://stackoverflow.com/questions/52087208)
|
||||||
// Once this bug is fixed in WebKit we can remove this https://bugs.webkit.org/show_bug.cgi?id=186050
|
// Once this bug is fixed in WebKit we can remove this https://bugs.webkit.org/show_bug.cgi?id=186050
|
||||||
if (isSafari && event.request.headers.get('range')) {
|
if (isSafari() && event.request.headers.get('range')) {
|
||||||
return returnRangeRequest(req)
|
return returnRangeRequest(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
const TerserWebpackPlugin = require('terser-webpack-plugin')
|
const TerserWebpackPlugin = require('terser-webpack-plugin')
|
||||||
|
const terserOptions = require('../bin/terserOptions')
|
||||||
|
|
||||||
module.exports = () => new TerserWebpackPlugin({
|
module.exports = () => new TerserWebpackPlugin({
|
||||||
exclude: /(tesseract-asset|page-lifecycle)/, // tesseract causes problems, page-lifecycle is pre-minified
|
exclude: /(tesseract-asset|page-lifecycle)/, // tesseract causes problems, page-lifecycle is pre-minified
|
||||||
cache: !process.env.TERSER_DISABLE_CACHE,
|
cache: !process.env.TERSER_DISABLE_CACHE,
|
||||||
parallel: true,
|
parallel: true,
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
terserOptions: {
|
terserOptions
|
||||||
ecma: 8,
|
|
||||||
mangle: true,
|
|
||||||
compress: {
|
|
||||||
pure_funcs: ['console.log']
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
comments: false
|
|
||||||
},
|
|
||||||
safari10: true
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue