Update color as the user scrolls on the array container

Viewed 38

I have a container and it renders a set of array elements. The array elements have a color associated with it. The colors are pre-defined in the state. On initial load, the first 5 elements have the color. When the container is scrolled, the color on the 1st element should be on the 6th element . the color of the element going out of the visible portion should be added to the incoming element. No two elements should be having the same. The same goes for when the container is scrolled up or down

I am attaching the codepen: https://codepen.io/ericjj94/pen/MWeVvQr?editors=0000

The container can be scrolled fast and slow. I tried to calculate the index of the firstVisible and the lastVisible element but i could not proceed further

 export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      houses: [
        { color: "#E31E50", content: 1 },
        { color: "#E5781D", content: 2 },
        { color: "#E3E51D", content: 3 },
        { color: "#7EE51D", content: 4 },
        { color: "#1DAEE5", content: 5 },
        { color: "", content: 6 },
        { color: "", content: 7 },
        { color: "", content: 8 },
        { color: "", content: 9 },
        { color: "", content: 10 }
      ],
      pinColors: ["#E31E50", "#E5781D", "#E3E51D", "#7EE51D", "#1DAEE5"]
    };
    this.lastScrollTop = 1;
    this.lastDirection = 1;
    this.sidePanelHouses = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // any number
  }

  componentDidMount() {
    const el = document.getElementById("scroll-container");
    el.addEventListener("scroll", this.handleScroll);
  }

  handleScroll = () => {
    const element = document.getElementById("scroll-container");
    const currentScrollTop = element.scrollTop;
    const galleryElementHeight = 80;
    const firstVisible = Math.floor(currentScrollTop / galleryElementHeight);
    const direction = this.lastScrollTop < currentScrollTop ? 1 : -1;
    this.lastScrollTop = currentScrollTop;
    const lastVisible = firstVisible + 4;
    console.log("firstVisible", firstVisible);
    const directionChange =
      this.lastDirection && direction !== this.lastDirection;
    this.lastDirection = direction;
    console.log("directionChange", directionChange);
  };

  renderArray = () => {
    return this.state.houses.map((item, index) => (
      <div
        key={index}
        style={{
          color: "black",
          height: "80px",
          backgroundColor: item.color
        }}
      >
        <span>{item.content} content</span>
      </div>
    ));
  };
  render() {
    return (
      <div
        id="scroll-container"
        style={{
          height: "400px",
          overflow: "auto"
        }}
      >
        {this.renderArray()}
      </div>
    );
  }
}

It is the houses array that needs to be updated with the latest color changes as per the currentScroll.

1 Answers

You don't need to make it that complicated since the scollable div and elements have fixed height. take a look at this codesandbox: https://codesandbox.io/s/quiet-architecture-ktlp1?file=/src/App.js

import React, { useEffect, useState } from "react";
import { cloneDeep } from "lodash";

export default function App() {
  const [houses, setHouses] = useState([
    { color: "#E31E50", content: 1 },
    { color: "#E5781D", content: 2 },
    { color: "#E3E51D", content: 3 },
    { color: "#7EE51D", content: 4 },
    { color: "#1DAEE5", content: 5 },
    { color: "", content: 6 },
    { color: "", content: 7 },
    { color: "", content: 8 },
    { color: "", content: 9 },
    { color: "", content: 10 }
  ]);
  const pinColors = ["#E31E50", "#E5781D", "#E3E51D", "#7EE51D", "#1DAEE5"];

  useEffect(() => {
    const el = document.getElementById("scroll-container");
    el.addEventListener("scroll", handleScroll);
    return () => el.removeEventListener("scroll", handleScroll);
  }, []);

  const handleScroll = (e) => {
    const scrollOffset = e.target.scrollTop;
    if (scrollOffset > 0) {
      const elementsScrolled = Math.floor(scrollOffset / 80);
      const newHouses = cloneDeep(houses).map((house, index) => {
        if (index <= elementsScrolled) {
          house.color = "";
        } else if (index - 5 <= elementsScrolled) {
          house.color = pinColors[index % 5];
        }
        return house;
      });
      setHouses(newHouses);
    }
  };

  const renderArray = () => {
    return houses.map((house, index) => (
      <div
        key={index}
        style={{
          color: "black",
          height: "80px",
          backgroundColor: house.color
        }}
      >
        <span>{house.content} content</span>
      </div>
    ));
  };

  return (
    <div
      id="scroll-container"
      style={{
        height: "400px",
        overflow: "auto"
        // color: "red",
        // backgroundColor: "red"
      }}
    >
      {renderArray()}
    </div>
  );
}

You can play around with these conditions to change color if entire elements are inside/outside the scrollview:

if (index <= elementsScrolled) {
  house.color = "";
} else if (index - 5 <= elementsScrolled) {
  house.color = pinColors[index % 5];
}
Related