如何在 React 中滚动时切换图像(就像在 Apple 网站上一样)?

How to switch image on scroll in React (like on Apple website)?

我尝试在使用 React 滚动时从一张图片切换到另一张图片,但我不知道该怎么做...也许使用 React hook。我知道 jQuery 可以,但我不想使用它。

此处示例:https://www.apple.com/iphone-12/,查看“五个新鲜完成”。

import React, {useState} from 'react';
import './BackgroundDiscover.css';
import logoOrdiW from "../../images/mathis_computer_white.jpg";
import logoOrdiB from "../../images/mathis_computer_black.jpg";


const BackgroundDiscover = () => {
    const [imageSrc, SetSrc] = useState(logoOrdiW);
    const switchBrackground = () => {
        SetSrc(logoOrdiB);
    }
    return (
        <>
            <div className="container-discover">
                <div className='container-logo-white'>
                    <img src={imageSrc} alt="logo ordinateur mathis blanc" onScroll={switchBrackground}/>
                </div>
            </div>
        </>
    );
};



export default BackgroundDiscover;
.container-discover {
    display: flex;
    flex-direction: column;
}

.container-logo-white img {
    width: 100%;
}

.container-logo-black img {
    width: 100%;
}

这个问题有很多解决办法,但基本上你可以这样设置。 Wheel 事件侦听器变为正值或负值,因此如果您想实现不同的设计,您应该创建一个算法。

import React, { useState } from "react";
export default function App() {
  const [bgImage, handleImage] = useState(
    "https://media.wired.com/photos/5e9f56f143e5800008514457/1:1/w_1277,h_1277,c_limit/Gear-Feature-Apple_new-iphone-se-white_04152020.jpg"
  );
  window.addEventListener("wheel", (e) => {
    if (e.deltaY > 0) {
      handleImage(
        "https://media.wired.com/photos/5bcea2642eea7906bba84c67/master/w_2560%2Cc_limit/iphonexr.jpg"
      );
    } else {
      handleImage(
        "https://media.wired.com/photos/5e9f56f143e5800008514457/1:1/w_1277,h_1277,c_limit/Gear-Feature-Apple_new-iphone-se-white_04152020.jpg"
      );
    }
  });

  return (
    <div className="App">
      <img
        style={{ width: "400px", height: "300px" }}
        id="myimage"
        src={bgImage}
        alt=""
      />
    </div>
  );
}
   

因此,您可以在图像源中使用 bgImage 状态。

这是一个codesandbox示例https://codesandbox.io/s/pensive-vaughan-cfc63?fontsize=14&hidenavigation=1&theme=dark

您不需要 jQuery 或类似的东西。您可以订阅滚动容器的 scroll event in useEffect() hook with addEventListener and check scrollTop

如果你想做和苹果网站一样的效果,你可以用position: sticky, height in vh units and checking window.innerHeight添加一些魔法。

注意这段代码是简化的,没有优化,只需要理解思路即可:

CodeSandbox

index.js:

import { useLayoutEffect, useState } from "react";
import { render } from "react-dom";
import classnames from "classnames";
import "./index.css";

const images = [0, 1, 2, 3, 4];

const App = () => {
  const [visibleImagesMap, setVisibleImagesMap] = useState(
    images.reduce((map, image) => {
      map[image] = false;
      return map;
    }, {})
  );

  useLayoutEffect(() => {
    const handleScroll = () => {
      const scrollTop = document.documentElement.scrollTop;
      const viewportHeight = window.innerHeight;

      const newVisibleImagesMap = images.reduce((map, image) => {
        map[image] = scrollTop >= image * viewportHeight;
        return map;
      }, {});

      setVisibleImagesMap(newVisibleImagesMap);
    };

    window.addEventListener("scroll", handleScroll);
    handleScroll();

    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  return (
    <div className="app">
      <div className="sticky">
        <div className="frame">
          {images.map((image) => (
            <div
              className={classnames("image", `image_${image}`, {
                image_visible: visibleImagesMap[image]
              })}
              key={image}
            />
          ))}
        </div>
      </div>
    </div>
  );
};

render(<App />, document.getElementById("root"));

index.css:

body {
  margin: 0;
}

.app {
  height: 500vh;
}

.sticky {
  position: sticky;
  top: 0;
  height: 100vh;
}

.frame {
  z-index: 1;
  position: relative;
  height: 100%;
  width: 100%;
}

.image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  background-repeat: no-repeat;
  background-size: cover;
}

.image_0 {
  z-index: 0;
  background-image: url("./images/0.jpeg");
}

.image_1 {
  z-index: 1;
  background-image: url("./images/1.jpeg");
}

.image_2 {
  z-index: 2;
  background-image: url("./images/2.jpeg");
}

.image_3 {
  z-index: 3;
  background-image: url("./images/3.jpeg");
}

.image_4 {
  z-index: 4;
  background-image: url("./images/4.jpeg");
}

.image_visible {
  opacity: 1;
}

你可以试试 useEffect 和 useRef。

useEffect 是旁加载函数,useRef 是真正的 dom 引用。

import React, { useRef, useState, useEffect } from 'react';

import logoOrdiW from "../../images/mathis_computer_white.jpg";
import logoOrdiB from "../../images/mathis_computer_black.jpg";

import './BackgroundDiscover.css';

const BackgroundDiscover = () => {
  const img = useRef();
  const [src, setSrc] = useState(logoOrdiW);
  const switchBrackground = () => {
    setSrc(logoOrdiB);
  }
  useEffect(() => {
    img.current.onscroll = switchBrackground;
  }, []);

  return (
    <div className="container-discover">
      <div className='container-logo-white'>
        <img src={src} alt="logo ordinateur mathis blanc" ref={img} />
      </div>
    </div>
  );
};

export default BackgroundDiscover;