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:
Nolan Lawson 2019-10-24 19:03:10 -07:00 committed by GitHub
parent 0194a07823
commit 7c04b86405
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 61 additions and 51 deletions

View file

@ -8,6 +8,7 @@ import replace from 'rollup-plugin-replace'
import fromPairs from 'lodash-es/fromPairs'
import babel from 'rollup-plugin-babel'
import { themes } from '../src/routes/_static/themes'
import terserOptions from './terserOptions'
const writeFile = promisify(fs.writeFile)
@ -28,11 +29,7 @@ export async function buildInlineScript () {
runtimeHelpers: true,
presets: ['@babel/preset-env']
}),
!process.env.DEBUG && terser({
mangle: true,
compress: true,
ecma: 8
})
!process.env.DEBUG && terser(terserOptions)
]
})
const { output } = await bundle.generate({

14
bin/terserOptions.js Normal file
View 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
}

View file

@ -333,7 +333,7 @@
// smooth scroll, so disable smooth scrolling
scroller.scrollLeft = scrollLeft
} else {
smoothScroll(scroller, scrollLeft, true)
smoothScroll(scroller, scrollLeft, /* horizontal */ true, /* preferFast */ false)
}
} else {
console.log('setting scrollLeft', scrollLeft)

View file

@ -72,7 +72,7 @@
if (index === 0 && movement === -1) {
activeItemKey = null
this.set({ activeItemKey })
smoothScroll(getScrollContainer(), 0)
smoothScroll(getScrollContainer(), 0, /* horizontal */ false, /* preferFast */ false)
return
}
if (index === -1) {

View file

@ -1,4 +1,4 @@
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)

View file

@ -1,5 +1,5 @@
/* 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 */

View file

@ -66,5 +66,5 @@ export function scrollIntoViewIfNeeded (element) {
}
const scrollContainer = getScrollContainer()
const scrollTop = scrollContainer.scrollTop
smoothScroll(scrollContainer, scrollTop + rect.top - scrollY)
smoothScroll(scrollContainer, scrollTop + rect.top - scrollY, /* horizontal */ false, /* preferFast */ false)
}

View file

@ -8,7 +8,7 @@ export function scrollToTop (smooth) {
return false
}
if (smooth) {
smoothScroll(scroller, 0)
smoothScroll(scroller, 0, /* horizontal */ false, /* preferFast */ true)
} else {
scroller.scrollTop = 0
}

View file

@ -1,4 +1,5 @@
import { store } from '../_store/store'
import { isChrome } from './userAgent'
// via https://github.com/tootsuite/mastodon/blob/f59ed3a4fafab776b4eeb92f805dfe1fecc17ee3/app/javascript/mastodon/scroll.js
const easingOutQuint = (x, t, b, c, d) =>
@ -63,16 +64,22 @@ function testSupportsSmoothScroll () {
export const hasNativeSmoothScroll = process.browser && testSupportsSmoothScroll()
export function smoothScroll (node, topOrLeft, horizontal) {
export function smoothScroll (node, topOrLeft, horizontal, preferFast) {
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
} 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({
[horizontal ? 'left' : 'top']: topOrLeft,
behavior: 'smooth'
})
} else {
console.log('smooth scroll: using polyfill')
return smoothScrollPolyfill(node, horizontal ? 'scrollLeft' : 'scrollTop', topOrLeft)
}
}

View file

@ -1,11 +1,11 @@
// LocalStorage and IDB may be disabled in private mode, when "blocking cookies" in Safari,
// or other cases
import { thunk } from './thunk'
import { __thunk__ } from './thunk'
const testKey = '__test__'
export const testHasLocalStorage = thunk(() => {
export const testHasLocalStorage = __thunk__(() => {
try {
localStorage.setItem(testKey, testKey)
if (!localStorage.length || localStorage.getItem(testKey) !== testKey) {
@ -18,7 +18,7 @@ export const testHasLocalStorage = thunk(() => {
return true
})
export const testHasIndexedDB = thunk(async () => {
export const testHasIndexedDB = __thunk__(async () => {
if (typeof indexedDB === 'undefined') {
return false
}

View file

@ -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 runOnce
return () => {

View file

@ -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
// a static regex
return process.env.URL_REGEX

View file

@ -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
export const isIOSPre12Point2 = thunk(() => process.browser && isIOS() &&
export const isIOSPre12Point2 = __thunk__(() => process.browser && isIOS() &&
!(typeof IntersectionObserver === 'function' &&
IntersectionObserver.toString().includes('[native code]')))
// 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' &&
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))

View file

@ -1,12 +1,11 @@
import { decode as decodeBlurHash } from 'blurhash'
import registerPromiseWorker from 'promise-worker/register'
import { BLURHASH_RESOLUTION as RESOLUTION } from '../_static/blurhash'
const isChrome = /Chrome/.test(navigator.userAgent)
import { isChrome } from '../_utils/userAgent'
// Disabled in Chrome because convertToBlob() is slow
// 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
const OFFSCREEN_CANVAS_CONTEXT_2D = OFFSCREEN_CANVAS
? OFFSCREEN_CANVAS.getContext('2d') : null

View file

@ -3,11 +3,8 @@ import {
shell as __shell__,
routes as __routes__
} from '../__sapper__/service-worker.js'
import {
get,
post
} from './routes/_utils/ajax'
import { get, post } from './routes/_utils/ajax'
import { isSafari } from './routes/_utils/userAgent'
const timestamp = process.env.SAPPER_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
const assets = __assets__
.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
// 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
if (isSafari && event.request.headers.get('range')) {
if (isSafari() && event.request.headers.get('range')) {
return returnRangeRequest(req)
}

View file

@ -1,19 +1,10 @@
const TerserWebpackPlugin = require('terser-webpack-plugin')
const terserOptions = require('../bin/terserOptions')
module.exports = () => new TerserWebpackPlugin({
exclude: /(tesseract-asset|page-lifecycle)/, // tesseract causes problems, page-lifecycle is pre-minified
cache: !process.env.TERSER_DISABLE_CACHE,
parallel: true,
sourceMap: true,
terserOptions: {
ecma: 8,
mangle: true,
compress: {
pure_funcs: ['console.log']
},
output: {
comments: false
},
safari10: true
}
terserOptions
})