作为 React 的新手但熟悉 JavaScript,我很难知道在给定的 React 组件中确切地放置某些功能的位置,并且不确定我遇到的这个特定问题是否与我的 React 逻辑或只是加载图像时的延迟时间,尽管如果是后者,我不明白这一点,因为我将图像预加载到隐藏的 div 中。
我预期的工作流程如下:
height:0
加载两个图像,一个作为类的一部分“预加载”hide
。- 当窗口滚动时,有效地切换
hide
这两个图像中的任何一个上的类,从而有效地根据滚动位置改变显示哪个图像。发生这种情况时,标题的背景颜色也会发生变化。
在我的组件Header.jsx
中,我有以下内容:
componentDidMount() {
if (this.props.changeColorOnScroll) {
window.addEventListener("scroll", this.headerColorChange);
}
}
headerColorChange() {
console.log("Scroll Fire!");
const { classes, color, changeColorOnScroll} = this.props;
const windowsScrollTop = window.pageYOffset;
if (windowsScrollTop > changeColorOnScroll.height) {
document.body
.getElementsByTagName("header")[0]
.classList.remove(classes[color]);
document.body
.getElementsByTagName("header")[0]
.classList.add(classes[changeColorOnScroll.color]);
document
.getElementById("headerLogoContainer").getElementsByClassName("logo")[0]
.classList.add('hide');
document
.getElementById("headerLogoContainer").getElementsByClassName("logoalt")[0]
.classList.remove('hide');
} else {
document.body
.getElementsByTagName("header")[0]
.classList.add(classes[color]);
document.body
.getElementsByTagName("header")[0]
.classList.remove(classes[changeColorOnScroll.color]);
document
.getElementById("headerLogoContainer").getElementsByClassName("logo")[0]
.classList.remove('hide');
document
.getElementById("headerLogoContainer").getElementsByClassName("logoalt")[0]
.classList.add('hide');
}
}
我相信这是在componentDidMount()
. 我的问题是当我滚动时,窗口会随着图像的变化而冻结,即使我在这里预加载了图像:
render() {
const {
classes,
color,
rightLinks,
leftLinks,
brand,
logo,
logoalt,
fixed,
absolute
} = this.props;
const appBarClasses = classNames({
[classes.appBar]: true,
[classes[color]]: color,
[classes.absolute]: absolute,
[classes.fixed]: fixed
});
let brandComponent;
if(!logo){
brandComponent = (
<Button className={classes.title}>
{brand}
</Button>
);
}else{
brandComponent = (
<div id='headerLogoContainer'>
<img className='logo' src={logo}></img>
<img className='logoalt hide' src={logoalt}></img>
</div>
);
}
return (
<AppBar className={appBarClasses}>
<Toolbar className={classes.container}>
{leftLinks !== undefined ? brandComponent : null}
<div className={classes.flex}>
{leftLinks !== undefined ? (
<Hidden smDown implementation="css">
{leftLinks}
</Hidden>
) : (
brandComponent
)}
</div>
如您所见,在componentDidMount()
我设置了事件侦听器,然后在渲染<div id='headerLogoContainer'>
中我将brandComponent
. 我看到brandComponent
成功渲染到 DOM,但在第一次滚动(向下)时,它会锁定屏幕(在 Chrome 中)或不logoalt
立即显示(在 Edge 和 Firefox 中)。几秒钟后图像切换,然后我可以向上或向下滚动,毫无问题地切换它。这让我认为这可能是一个预加载问题,但为什么当我在 DOM 中预加载图像brandComponent
并在 DOM 中看到它时会出现这种情况。没有发出额外的网络请求,只需切换hide
要显示的图像。
所以我的问题是,如何根据滚动事件更改 React 中的图像源,而不会出现延迟或冻结滚动位置?
编辑:
我已经根据建议的答案更改了我的代码以利用内部状态,并且当图像更改时它仍然具有相同的延迟效果:
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
mobileOpen: false,
logoaltState: false
};
this.handleDrawerToggle = this.handleDrawerToggle.bind(this);
this.headerColorChange = this.headerColorChange.bind(this);
}
handleDrawerToggle() {
this.setState({ mobileOpen: !this.state.mobileOpen });
}
componentDidMount() {
if (this.props.changeColorOnScroll) {
window.addEventListener("scroll", this.headerColorChange);
}
}
headerColorChange() {
console.log("Scroll Fire!");
const { classes, color, changeColorOnScroll} = this.props;
const windowsScrollTop = window.pageYOffset;
if (windowsScrollTop > changeColorOnScroll.height) {
document.body
.getElementsByTagName("header")[0]
.classList.remove(classes[color]);
document.body
.getElementsByTagName("header")[0]
.classList.add(classes[changeColorOnScroll.color]);
this.setState({ logoaltState: true });
} else {
document.body
.getElementsByTagName("header")[0]
.classList.add(classes[color]);
document.body
.getElementsByTagName("header")[0]
.classList.remove(classes[changeColorOnScroll.color]);
this.setState({ logoaltState: false });
}
}
然后在我的渲染中......
render() {
const {
classes,
color,
rightLinks,
leftLinks,
brand,
logo,
logoalt,
fixed,
absolute
} = this.props;
const{
mobileOpen,
logoaltState
} = this.state
const appBarClasses = classNames({
[classes.appBar]: true,
[classes[color]]: color,
[classes.absolute]: absolute,
[classes.fixed]: fixed
});
let brandComponent;
if(!logo){
brandComponent = (
<Button className={classes.title}>
{brand}
</Button>
);
}else{
brandComponent = (
<div id='headerLogoContainer'>
<img className='logo' src={logo} style={{display: logoaltState === false ? "block" : "none"}}></img>
<img className='logoalt' src={logoalt} style={{display: logoaltState === true ? "block" : "none"}}></img>
</div>
);
}
不同的代码/方法,同样的问题......
编辑2:
我不认为这是这个应用程序逻辑的问题,因为正如我所提到的,它只会在图像/状态第一次需要更改时冻结。我认为这是某种首次加载问题,即使我没有任何网络请求,也许我错误地包含了图像源?或者在 React 中,图像需要通过其他方法进行预加载,因为图像源在包含时会略微偏离......即/static/media/logo.ad924167.png
相反,require("assets/img/tovlogo.png"
我认为这是因为我的节点服务器设置,但同样,我不确定发生了什么因为我对这个生态系统相当陌生......我正在使用Material UI React Template,这就是这个组件的来源。
主题文件本身是这样的:https ://www.creative-tim.com/product/material-kit-react?partner=104080所以我想知道它如何处理图像,考虑到实际的性质src
而不是require
...但是再次没有对图像的其他请求...这只是 CSS/样式更改...
编辑 3:
这是我的调试器(Chrome:Performance)冻结时的样子:
我意识到查看图像非常大的绘制时间,但不应该预加载地址......我想不是因为那是网络预加载而不是绘制预加载display:none;"
......也许我可以尝试使用opacity
CSS 属性,但是这个使其他 CSS 复杂化...一般有更好的方法吗?
最终编辑:
我的图像超过 10,000 像素宽...我将它用作徽标,所以直到我做了性能指标才意识到它有多糟糕...我猜这就是它冻结的原因...抱歉做出反应。:(