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 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
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 // 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)

View file

@ -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) {

View file

@ -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)

View file

@ -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 */

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -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)
} }
} }

View file

@ -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
} }

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 cached
let runOnce let runOnce
return () => { 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 // 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

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 // 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))

View file

@ -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

View file

@ -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)
} }

View file

@ -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
}
}) })