From 930e3bee64a8972bbcd69ca4cbeee99a74aafe62 Mon Sep 17 00:00:00 2001 From: Aaron Boodman Date: Mon, 17 Feb 2014 23:05:33 -0800 Subject: [PATCH] Fix choppy scrolling when flinging on OS X. This was caused by a bug in Chrome: wheel events stop firing when source element is removed from DOM. Workaround: leave element in DOM for awhile. On the upside, React makes this clean to implement. No need to keep state to remember to remove the dangling element. It automatically gets removed when it is no longer the last element to receive the wheel event! Change-Id: I1b9372bfd1edcfb532f6825d99044a314048c3b4 --- .../camlistored/ui/blob_item_container_react.js | 17 +++++++++++------ server/camlistored/ui/blob_item_react.js | 9 ++++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/server/camlistored/ui/blob_item_container_react.js b/server/camlistored/ui/blob_item_container_react.js index 1e282e922..f7afcc599 100644 --- a/server/camlistored/ui/blob_item_container_react.js +++ b/server/camlistored/ui/blob_item_container_react.js @@ -68,11 +68,13 @@ cam.BlobItemContainerReact = React.createClass({ // TODO(aa): This can be removed when https://code.google.com/p/chromium/issues/detail?id=50298 is fixed and deployed. this.updateHistoryThrottle_ = new goog.async.Throttle(this.updateHistory_, 2000); + + // TODO(aa): This can be removed when https://code.google.com/p/chromium/issues/detail?id=312427 is fixed and deployed. + this.lastWheelItem_ = ''; }, componentDidMount: function() { this.eh_.listen(this.props.searchSession, cam.SearchSession.SEARCH_SESSION_CHANGED, this.handleSearchSessionChanged_); - this.eh_.listen(this.getDOMNode(), 'scroll', this.handleScroll_); if (this.props.history.state && this.props.history.state.scroll) { this.getDOMNode().scrollTop = this.props.history.state.scroll; } @@ -107,6 +109,8 @@ cam.BlobItemContainerReact = React.createClass({ this.childProps_.forEach(function(props) { if (this.isVisible_(props.position.y) || this.isVisible_(props.position.y + props.size.height)) { children.push(cam.BlobItemReact(props)); + } else if (props.blobref == this.lastWheelItem_) { + children.push(cam.BlobItemReact(cam.object.extend(props, {visibility:'hidden'}))); } }.bind(this)); @@ -124,7 +128,7 @@ cam.BlobItemContainerReact = React.createClass({ // If we haven't filled the window with results, add some more. this.fillVisibleAreaWithResults_(); - return React.DOM.div({className:'cam-blobitemcontainer', style:this.props.style, onMouseDown:this.handleMouseDown_}, children); + return React.DOM.div({className:'cam-blobitemcontainer', style:this.props.style, onMouseDown:this.handleMouseDown_, onScroll:this.handleScroll_}, children); }, updateChildProps_: function() { @@ -207,6 +211,7 @@ cam.BlobItemContainerReact = React.createClass({ href: this.props.detailURL(item).toString(), data: item, onCheckClick: this.handleCheckClick_, + onWheel: this.handleChildWheel_, position: new goog.math.Coordinate(currentLeft + this.BLOB_ITEM_MARGIN_, top), size: new goog.math.Size(width, height), thumbnailVersion: this.props.thumbnailVersion, @@ -268,15 +273,15 @@ cam.BlobItemContainerReact = React.createClass({ }, handleScroll_: function() { - if (!this.isMounted()) { - return; - } - this.updateHistoryThrottle_.fire(); this.setState({scroll:this.getDOMNode().scrollTop}); this.fillVisibleAreaWithResults_(); }, + handleChildWheel_: function(child) { + this.lastWheelItem_ = child.props.blobref; + }, + // NOTE: This method causes the URL bar to throb for a split second (at least on Chrome), so it should not be called constantly. updateHistory_: function() { this.props.history.replaceState({scroll:this.getDOMNode().scrollTop}); diff --git a/server/camlistored/ui/blob_item_react.js b/server/camlistored/ui/blob_item_react.js index 906904e36..db8e6d059 100644 --- a/server/camlistored/ui/blob_item_react.js +++ b/server/camlistored/ui/blob_item_react.js @@ -160,7 +160,8 @@ cam.BlobItemReact = React.createClass({ className: this.getRootClassName_(), style: this.getRootStyle_(), onMouseEnter: this.handleMouseEnter_, - onMouseLeave: this.handleMouseLeave_ + onMouseLeave: this.handleMouseLeave_, + onWheel: this.handleWheel_, }, React.DOM.div({className:'checkmark', onClick:this.handleCheckClick_}), React.DOM.a({href:this.props.href}, @@ -209,6 +210,12 @@ cam.BlobItemReact = React.createClass({ this.props.onCheckClick(this.props.blobref, e); }, + handleWheel_: function() { + if (this.props.onWheel) { + this.props.onWheel(this); + } + }, + getThumbClipClassName_: function() { return React.addons.classSet({ 'cam-blobitem-thumbclip': true,