From f73bf8336e2c83e75c5c9d4e02f7d4d8bf513e67 Mon Sep 17 00:00:00 2001 From: Pete Miller Date: Mon, 6 Nov 2017 10:49:35 -0800 Subject: [PATCH] Avoid 'flashes of white' when tabs open or close Remove white flash for new tab pages which use about:newtab. Sets the frame background to the same background as the newtab page, for a smooth transition between frame creation and the tab beginning to load content. Keeps the frame around until the next frame is ready, to avoid flash when closing tabs. Addresses #5309 --- app/renderer/components/frame/frame.js | 18 ++++++- app/renderer/components/main/main.js | 23 +++++++-- less/window.less | 69 +++++++++++++++++++------- package-lock.json | 31 ++++++++++++ package.json | 1 + 5 files changed, 119 insertions(+), 23 deletions(-) diff --git a/app/renderer/components/frame/frame.js b/app/renderer/components/frame/frame.js index e190d58aa44..63bbcc7859f 100644 --- a/app/renderer/components/frame/frame.js +++ b/app/renderer/components/frame/frame.js @@ -859,12 +859,15 @@ class Frame extends React.Component { const props = {} // used in renderer + props.transitionState = ownProps.transitionState props.partition = frameStateUtil.getPartition(frame) props.isFullScreen = frame.get('isFullScreen') props.isPreview = frame.get('key') === currentWindow.get('previewFrameKey') props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, frame.get('key')) props.showFullScreenWarning = frame.get('showFullScreenWarning') props.location = location + props.isDefaultNewTabLocation = location === 'about:newtab' + props.isBlankLocation = location === 'about:blank' props.tabId = tabId props.showMessageBox = tabMessageBoxState.hasMessageBoxDetail(state, tabId) props.isFocused = isFocused(state) @@ -906,7 +909,16 @@ class Frame extends React.Component { return props } + getTransitionStateClassName (stateName) { + if (!stateName) { + return null + } else { + return `is${stateName[0].toUpperCase()}${stateName.slice(1)}` + } + } + render () { + const transitionClassName = this.getTransitionStateClassName(this.props.transitionState) return
{ this.props.isFullScreen && this.props.showFullScreenWarning diff --git a/app/renderer/components/main/main.js b/app/renderer/components/main/main.js index 1a3e65793a8..aeefb98da7c 100644 --- a/app/renderer/components/main/main.js +++ b/app/renderer/components/main/main.js @@ -17,6 +17,7 @@ const contextMenus = require('../../../../js/contextMenus') const {getSetting} = require('../../../../js/settings') // Components +const { Transition, TransitionGroup } = require('react-transition-group') const Navigator = require('../navigation/navigator') const Frame = require('../frame/frame') const TabPages = require('../tabs/tabPages') @@ -733,15 +734,27 @@ class Main extends React.Component { }
-
+ { this.props.sortedFrames.map((frameKey) => - ) + // after how long (ms) + // should the state 'entering' switch to 'entered' + // and also how long should state switch from 'exiting' + // to the component actually being removed + timeout={150}> + { + (transitionState) => + + } + + ) } -
+
{ this.props.showDownloadBar diff --git a/less/window.less b/less/window.less index e943183db1f..33ee3d82255 100644 --- a/less/window.less +++ b/less/window.less @@ -62,16 +62,66 @@ html, .frameWrapper { height: 100%; width: 100%; - z-index: @zindexWindow; + position: absolute; top: 0; left: 0; // Needed to be able to click and drag to select content in pages. user-select: none; + // default frame background + --frame-bg: #fff; + // custom frame background(s) + &.isDefaultNewTabLocation { + // matches tab dashboard background + // will also show when about:newtab === about:blank + --frame-bg: #222; + } + // Webviews can cause flickers w/ Chrome v49 if left visible + // in the background. This also has a major benefit for background + // tabs that have video and animations in them. + visibility: hidden; + // delay frames getting hidden when new tabs show up + // in order to avoid showing 0 frames and just the window + // background + // whilst this does also affect visibility when switching tabs, + // z-index ensures the delayed-hidden tab is not visible + // Note that this value can be as large as we want, it does not + // need to match the timeout specified in the frame's element + // (in renderer main.js). However, there is no reason to set it higher than that. + transition: visibility 0s linear 150ms; + // 1000 + z-index: @zindexWindow; + &:not(.isActive) { + // 900 + z-index: @zindexWindowNotActive; + } + &.isPreview { + background: #222; + // 1100 + z-index: @zindexWindowIsPreview; + } + // show frame when active or preview + &.isActive, + &.isPreview, + // keep window contents around when exiting, to allow + // time for next active frame to be shown + &.isExiting { + visibility: visible; + // show instantly + transition-delay: 0s; + } + // hide frame whilst it's figuring out which location to be + // note: that isEntering is timed-out at a value set in renderer main.js + // but isBlankLocation will change at the exact moment that the frame + // has an actual location + &.isEntering.isBlankLocation { + visibility: hidden; + transition-delay: 0s; + } webview { - background-color: #fff; + background-color: var(--frame-bg); border: 0; height: 100%; outline: none; @@ -89,21 +139,6 @@ html, } } - &:not(.isActive) { - z-index: @zindexWindowNotActive; - - &.isPreview { - background: gray; - z-index: @zindexWindowIsPreview; - } - - // Webviews can cause flickers w/ Chrome v49 if left visible - // in the background. This also has a major benefit for background - // tabs that have video and animations in them. - &:not(.isPreview) { - visibility: hidden; - } - } } .hrefPreview { diff --git a/package-lock.json b/package-lock.json index badbe14f71c..47c12da8e61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2702,6 +2702,11 @@ "integrity": "sha1-CdekApCKpw39vq1T5YU/x50+8hw=", "dev": true }, + "chain-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz", + "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w=" + }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -4771,6 +4776,11 @@ } } }, + "dom-helpers": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.2.1.tgz", + "integrity": "sha1-MgPgf+0he9H0JLAZc1WC/Deyglo=" + }, "dom-serializer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", @@ -16617,6 +16627,19 @@ "object-assign": "4.1.1" } }, + "react-transition-group": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.2.1.tgz", + "integrity": "sha512-q54UBM22bs/CekG8r3+vi9TugSqh0t7qcEVycaRc9M0p0aCEu+h6rp/RFiW7fHfgd1IKpd9oILFTl5QK+FpiPA==", + "requires": { + "chain-function": "1.0.0", + "classnames": "2.2.5", + "dom-helpers": "3.2.1", + "loose-envify": "1.3.1", + "prop-types": "15.6.0", + "warning": "3.0.0" + } + }, "read-all-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", @@ -19954,6 +19977,14 @@ "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=", "dev": true }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "1.3.1" + } + }, "watchpack": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", diff --git a/package.json b/package.json index e09eefb2113..bd8ff389691 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "react-dnd-html5-backend": "^2.1.2", "react-dom": "^15.5.4", "react-select": "^0.9.1", + "react-transition-group": "^2.2.1", "snazzy": "^7.0.0", "string.prototype.endswith": "^0.2.0", "string.prototype.startswith": "^0.2.0",