mic*_*nil 6 html javascript css
问题:
蓝色<div>的地方overflow:hidden。现在我想以自定义的同步 * 方式滚动这些 div,而不管<div>我在白色容器中滚动的位置。我的想法是我可以创建一个绝对定位透明<div>作为白色容器的直接子级,并给它一个溢出子级:
其中蓝色容器的 z-index 高于原始两个文本容器:
.container {
width: 100vw;
height: 100vh;
z-index: 10;
position: absolute;
overflow-y: scroll;
}
Run Code Online (Sandbox Code Playgroud)
所以最终的结果是这样的:
现在我希望能够滚动覆盖容器,但在底层元素中捕获其他鼠标事件(如文本选择)。
我的目标是在滚动覆盖容器时使用 JavaScript 手动滚动底层容器。
题:
鉴于无法通过css 属性pointer-events有选择地禁用指针事件,有没有其他方法可以仅启用覆盖元素的滚动事件,同时将其他指针事件传递给底层元素?
背景:
*我正在尝试的实现类似于 Perforce P4Merge 使用他们的 diff 工具所做的。它们有一个用于 2 个代码块的垂直滚动条,我假设滚动高度大于两个代码块中的任何一个。在某些情况下,滚动事件会滚动两个代码块,有时只是其中一个,而在其他情况下,它们会以不同的速度滚动(取决于添加和删除的内容)。
更新:
原来的实现是写在反应,在该代码我不必有margin-left: -18px;上scrollable-container显示滚动条。不知道为什么。此外,如果您愿意,这里有一个 codepen:codepen 片段
.container {
width: 100vw;
height: 100vh;
z-index: 10;
position: absolute;
overflow-y: scroll;
}
Run Code Online (Sandbox Code Playgroud)
body {
overflow-y: hidden;
}
.app {
overflow-y: hidden;
position: relative;
display: flex;
flex-direction: row;
z-index: 0;
}
.scrollable-container {
width: 100vw;
height: 100vh;
z-index: 10;
margin-left: -18px;
position: absolute;
overflow-y: scroll;
}
.scrollable-content {
width: 500px;
height: 1600px;
}
.non-scrollable-container {
flex: 1;
height: 100vh;
overflow-y: hidden;
}
.bridge {
width: 40px;
background: linear-gradient(white, black);
cursor: ew-resize;
height: 100vh;
}
#original {
background: linear-gradient(red, yellow);
height: 2100px;
}
#modified {
background: linear-gradient(blue, green);
height: 1600px;
}Run Code Online (Sandbox Code Playgroud)
现在已经过去了几天了,根据我的研究,似乎不可能以这种方式实现我想要的目标。不可能有选择地禁用指针事件,而且我找不到任何解决方法。
相反,我能想到的最好的方法是实现我自己的“假”滚动条。此滚动条实现订阅wheel容器的事件,然后我同步子滚动容器以具有相同的位置。我暂时不会接受这个问题,以防有人针对我的要求提出更好的解决方案。
对于任何感兴趣的人,您都可以在下面找到我的解决方案。 注意:选择整页视图以获得更好的体验。
let appStyles = {
original: {
background: 'linear-gradient(red, yellow)',
height: '1600px',
},
modified: {
background: 'linear-gradient(blue, green)',
height: '2100px',
},
};
let Pane = React.forwardRef((props, ref) => {
return <PaneComponent {...props} forwardedRef={ref} />;
});
let PaneWithScrollSync = withScrollSync(Pane);
class App extends React.Component {
render() {
return (
<div className="app">
<FakeScrollBar scrollHeight={2100}>
<Splitter>
<PaneWithScrollSync>
<pre className="code" style={appStyles.original}>
<code>Content with height: 1600px</code>
</pre>
</PaneWithScrollSync>
<PaneWithScrollSync>
<pre className="code" style={appStyles.modified}>
<code>Ccontent with height: 2100px</code>
</pre>
</PaneWithScrollSync>
</Splitter>
</FakeScrollBar>
</div>
);
}
}
let scrollStyles = {
container: {
display: 'flex',
flexDirection: 'row',
flex: 1,
},
scrollTrack: {
width: 30,
borderLeft: '1px solid',
borderLeftColor: '#000',
background: '#212121',
position: 'relative',
},
scrollThumb: {
position: 'absolute',
background: 'red',
width: '100%',
},
scrollThumbHover: {
background: 'blue',
},
};
const ScrollContext = React.createContext();
class FakeScrollBar extends React.Component {
state = {
scrollTopRelative: 0,
thumbRelativeHeight: 0,
thumbMouseOver: false,
};
constructor(props) {
super(props);
this.scrollTrack = React.createRef();
}
get trackPosition() {
if (!this.scrollTrack.current) {
return 0;
}
return (this.scrollTop / this.props.scrollHeight) * this.scrollTrack.current.clientHeight;
}
get scrollTop() {
return this.state.scrollTopRelative * this.scrollTopMax;
}
get scrollTopMax() {
return this.props.scrollHeight - this.scrollTrack.current.clientHeight;
}
get thumbHeight() {
if (!this.scrollTrack.current) {
return 0;
}
return this.state.thumbRelativeHeight * this.scrollTrack.current.clientHeight;
}
handleWheel = e => {
if (e.deltaMode !== 0) {
console.error('The scrolling is not in pixel mode!');
return false;
}
let deltaYPercentage = e.deltaY / this.scrollTopMax;
let scrollTopRelative = Math.min(
Math.max(this.state.scrollTopRelative + deltaYPercentage, 0),
1
);
this.setState({
scrollTopRelative,
});
};
handleMouseEnterThumb = e => {
this.setState({ thumbMouseOver: true });
};
handleMouseLeaveThumb = e => {
this.setState({ thumbMouseOver: false });
};
getSyncedPosition = container => {};
componentDidMount() {
this.updateScrollThumbHeight();
window.addEventListener('resize', this.updateScrollThumbHeight);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateScrollThumbHeight);
}
updateScrollThumbHeight = e => {
this.setState({
thumbRelativeHeight: this.scrollTrack.current.clientHeight / this.props.scrollHeight,
});
};
render() {
let { thumbMouseOver } = this.state;
return (
<ScrollContext.Provider value={this.state}>
<div style={scrollStyles.container} onWheel={this.handleWheel}>
{this.props.children}
<div ref={this.scrollTrack} style={scrollStyles.scrollTrack}>
<div
onMouseEnter={this.handleMouseEnterThumb}
onMouseLeave={this.handleMouseLeaveThumb}
style={Object.assign(
{ top: this.trackPosition },
{ height: this.thumbHeight },
scrollStyles.scrollThumb,
thumbMouseOver && scrollStyles.scrollThumbHover
)}
/>
</div>
</div>
</ScrollContext.Provider>
);
}
}
let splitterStyles = {
container: {
display: 'flex',
flexDirection: 'row',
flex: 1,
},
bridge: {
width: '40px',
height: '100vh',
position: 'relative',
background: 'linear-gradient(white, black)',
cursor: 'ew-resize',
},
};
class Splitter extends React.Component {
state = {
dragging: false,
leftPaneFlex: 0.5,
rightPaneFlex: 0.5,
};
componentDidMount() {
if (this.props.children.length !== 2) {
console.error('The splitter needs to `Pane` children to work');
}
}
handleMouseUp = e => {
this.setState({ dragging: false });
this.bridge.removeEventListener('mouseup', this.handleMouseUp);
};
handleMouseMove = e => {
if (!this.state.dragging) {
return;
}
let splitterPosition = this.getRelativeContainerX(e.clientX);
console.log(splitterPosition);
this.setState({
leftPaneFlex: splitterPosition,
rightPaneFlex: 1 - splitterPosition,
});
};
handleMouseDown = e => {
this.setState({ dragging: true });
document.addEventListener('mouseup', this.handleMouseUp);
document.addEventListener('mousemove', this.handleMouseMove);
};
getRelativeContainerX(x) {
var rect = this.container.getBoundingClientRect();
return (x - rect.left) / rect.width;
}
render() {
const { children } = this.props;
let commonProps = {
dragging: this.state.dragging,
};
const leftPane = React.cloneElement(children[0], {
...commonProps,
flex: this.state.leftPaneFlex,
});
const rightPane = React.cloneElement(children[1], {
...commonProps,
flex: this.state.rightPaneFlex,
});
return (
<div style={splitterStyles.container} ref={container => (this.container = container)}>
{leftPane}
<div
style={{ ...splitterStyles.bridge }}
ref={bridge => (this.bridge = bridge)}
onDrag={this.handleDrag}
onMouseDown={this.handleMouseDown}
/>
{rightPane}
</div>
);
}
}
let paneStyles = {
scrollContainer: {
height: '100vh',
overflow: 'hidden',
},
pane: {
flex: 1,
minWidth: 'fit-content',
border: '5px solid', // remove
borderColor: 'cyan', // remove
},
};
class PaneComponent extends React.Component {
render() {
const { forwardedRef, dragging, ...rest } = this.props;
return (
<div
ref={forwardedRef}
style={{ flex: this.props.flex, ...paneStyles.scrollContainer }}
{...rest}
>
<div
style={{
userSelect: dragging ? 'none' : 'auto',
...paneStyles.pane,
}}
>
{this.props.children}
</div>
</div>
);
}
}
function withScrollSync(WrappedComponent) {
class ScrollSynced extends React.Component {
constructor(props) {
super(props);
this.wrappedComponent = React.createRef();
}
componentDidUpdate() {
let { scrollTopRelative } = this.props;
if (!this.wrappedComponent) {
return;
}
let { scrollHeight, clientHeight } = this.wrappedComponent.current;
this.wrappedComponent.current.scrollTop = (scrollHeight - clientHeight) * scrollTopRelative;
}
render() {
let { scrollTopRelative, forwardedRef, ...rest } = this.props;
return <WrappedComponent ref={this.wrappedComponent} {...rest} />;
}
}
ScrollSynced.propTypes = WrappedComponent.propTypes;
return React.forwardRef((props, ref) => (
<ScrollContext.Consumer>
{state => (
<ScrollSynced
{...props}
forwardedRef={ref}
scrollTopRelative={state.scrollTopRelative}
/>
)}
</ScrollContext.Consumer>
));
}
ReactDOM.render(
<App />,
document.getElementById('root')
)Run Code Online (Sandbox Code Playgroud)
body {
margin: 0;
overflow-y: hidden;
}
.app {
display: flex;
flex-direction: row;
}
.code {
margin: 0;
}Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root">
</div>Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5380 次 |
| 最近记录: |