我在React中构建了一个组件,它应该在窗口滚动上更新自己的风格,以创建视差效果。
组件渲染方法是这样的:
function() {
let style = { transform: 'translateY(0px)' };
window.addEventListener('scroll', (event) => {
let scrollTop = event.srcElement.body.scrollTop,
itemTranslate = Math.min(0, scrollTop/3 - 60);
style.transform = 'translateY(' + itemTranslate + 'px)');
});
return (
<div style={style}></div>
);
}
这是行不通的,因为React不知道组件已经更改,因此组件不会重新呈现。
我已经尝试在组件的状态中存储itemTranslate的值,并在滚动回调中调用setState。然而,这使得滚动无法使用,因为它非常慢。
有什么建议吗?
下面是另一个使用HOOKS fontAwesomeIcon和Kendo UI React的例子
[![这里截图][1]][1]
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const ScrollBackToTop = () => {
const [show, handleShow] = useState(false);
useEffect(() => {
window.addEventListener('scroll', () => {
if (window.scrollY > 1200) {
handleShow(true);
} else handleShow(false);
});
return () => {
window.removeEventListener('scroll');
};
}, []);
const backToTop = () => {
window.scroll({ top: 0, behavior: 'smooth' });
};
return (
<div>
{show && (
<div className="backToTop text-center">
<button className="backToTop-btn k-button " onClick={() => backToTop()} >
<div className="d-none d-xl-block mr-1">Top</div>
<FontAwesomeIcon icon="chevron-up"/>
</button>
</div>
)}
</div>
);
};
export default ScrollBackToTop;```
[1]: https://i.stack.imgur.com/ZquHI.png
您应该在componentDidMount中绑定侦听器,这样它只创建一次。您应该能够在状态中存储样式,侦听器可能是性能问题的原因。
就像这样:
componentDidMount: function() {
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount: function() {
window.removeEventListener('scroll', this.handleScroll);
},
handleScroll: function(event) {
let scrollTop = event.srcElement.body.scrollTop,
itemTranslate = Math.min(0, scrollTop/3 - 60);
this.setState({
transform: itemTranslate
});
},
用React Hooks更新答案
这是两个钩子——一个用于方向(上/下/无),另一个用于实际位置
像这样使用:
useScrollPosition(position => {
console.log(position)
})
useScrollDirection(direction => {
console.log(direction)
})
下面是钩子:
import { useState, useEffect } from "react"
export const SCROLL_DIRECTION_DOWN = "SCROLL_DIRECTION_DOWN"
export const SCROLL_DIRECTION_UP = "SCROLL_DIRECTION_UP"
export const SCROLL_DIRECTION_NONE = "SCROLL_DIRECTION_NONE"
export const useScrollDirection = callback => {
const [lastYPosition, setLastYPosition] = useState(window.pageYOffset)
const [timer, setTimer] = useState(null)
const handleScroll = () => {
if (timer !== null) {
clearTimeout(timer)
}
setTimer(
setTimeout(function () {
callback(SCROLL_DIRECTION_NONE)
}, 150)
)
if (window.pageYOffset === lastYPosition) return SCROLL_DIRECTION_NONE
const direction = (() => {
return lastYPosition < window.pageYOffset
? SCROLL_DIRECTION_DOWN
: SCROLL_DIRECTION_UP
})()
callback(direction)
setLastYPosition(window.pageYOffset)
}
useEffect(() => {
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
})
}
export const useScrollPosition = callback => {
const handleScroll = () => {
callback(window.pageYOffset)
}
useEffect(() => {
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
})
}
为了帮助那些在使用austin answer时注意到延迟行为/性能问题的人,并想要一个使用评论中提到的引用的例子,这里有一个我用来切换一个类的滚动向上/向下图标的例子:
在渲染方法中:
<i ref={(ref) => this.scrollIcon = ref} className="fa fa-2x fa-chevron-down"></i>
在handler方法中:
if (this.scrollIcon !== null) {
if(($(document).scrollTop() + $(window).height() / 2) > ($('body').height() / 2)){
$(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-up');
}else{
$(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-down');
}
}
然后像Austin提到的那样添加/删除你的处理程序:
componentDidMount(){
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount(){
window.removeEventListener('scroll', this.handleScroll);
},
裁判的文件。
我通过使用和修改CSS变量解决了这个问题。这样我就不必修改会导致性能问题的组件状态。
index.css
:root {
--navbar-background-color: rgba(95,108,255,1);
}
Navbar.jsx
import React, { Component } from 'react';
import styles from './Navbar.module.css';
class Navbar extends Component {
documentStyle = document.documentElement.style;
initalNavbarBackgroundColor = 'rgba(95, 108, 255, 1)';
scrolledNavbarBackgroundColor = 'rgba(95, 108, 255, .7)';
handleScroll = () => {
if (window.scrollY === 0) {
this.documentStyle.setProperty('--navbar-background-color', this.initalNavbarBackgroundColor);
} else {
this.documentStyle.setProperty('--navbar-background-color', this.scrolledNavbarBackgroundColor);
}
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
render () {
return (
<nav className={styles.Navbar}>
<a href="/">Home</a>
<a href="#about">About</a>
</nav>
);
}
};
export default Navbar;
Navbar.module.css
.Navbar {
background: var(--navbar-background-color);
}