SOURCE

console 命令行工具 X clear

                    
>
console
let timer;
/* NOTE: Play around with variables: */
const configs = {
  /* texts: Array of strings as list items */
  texts: [
    "BITMOB.CC",
    "103STORE",
    "WTDT",
    "CODEPEN",
    "P5.JS",
    "REACT",
    "ED ♥︎ FRONTEND"
  ],
  /* unitMargin: right margin append to each of the repeat word (unit) */
  unitMargin: 20,
  /* unitCount: how many units to duplicate and display within viewport */
  unitCount: 20,
  /* speed: transition speed by pixel per second */
  speed: 400,
  /* groupSize: unit will be grouped and translated together. 1 to be smallest, but with noticable pause after one translation (before restart from beginning) */
  groupSize: 10,
  /* color: text color when not touched */
  color: "#4f91a1",
  /* hoverColor: text color when hover */
  hoverColor: "#8ecbc7",
  /* stripeBg: text background color when rolling */
  stripeBg: "#184954",
  /* bgColor: list background */
  bgColor: "#16191e"
};

function LoopText(props) {
  const {
    text,
    hovered,
    passive,
    unitMargin,
    groupSize,
    speed,
    unitCount,
    color,
    stripeBg,
    hoverColor
  } = props;

  const textRef = React.useRef();
  const [unitW, setUnitW] = React.useState(0);
  const [stripe, setStripe] = React.useState();
  const [isHover, setIsHover] = React.useState(false);
  const [transX, setTransX] = React.useState(0);
  const [transition, setTransition] = React.useState("none");
  const [duration, setDuration] = React.useState(0);

  React.useEffect(() => {
    if (textRef && textRef.current) {
      let textWidth = Math.round(textRef.current.getBoundingClientRect().width);
      setUnitW(textWidth + unitMargin);
      setDuration((textWidth * groupSize) / speed);
    }
  }, [textRef]);

  React.useEffect(() => {
    if (hovered) {
      handleHover();
    } else {
      handleOff();
    }
  }, [hovered]);

  const createLoop = () => {
    let loop = [];
    for (var i = 0; i < unitCount; i++) {
      loop.push(
        <span key={i} className="unit" style={{ marginRight: unitMargin }}>
          {text}
        </span>
      );
    }
    setStripe(loop);
  };

  const startTransition = (delay) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      setTransX(-unitW * groupSize + 1);
      setTransition(`transform ${duration}s linear`);
    }, delay);
  };

  const removeTransition = () => {
    setTransX(0);
    setTransition("none");
  };

  const handleHover = () => {
    setIsHover(true);
    createLoop();
    startTransition(30);
  };

  const handleOff = () => {
    setIsHover(false);
    setStripe(null);
    removeTransition();
  };

  const handleTransEnd = () => {
    removeTransition();
    startTransition(0);
  };

  const activeProps = !passive
    ? {
        onMouseEnter: handleHover,
        onMouseLeave: handleOff,
        onTouchStart: handleHover,
        onTouchEnd: handleOff
      }
    : {};

  const child = isHover ? (
    <div
      style={{
        width: unitW * unitCount,
        marginLeft: -unitW,
        transform: `translate3d(${transX}px, 0,0)`,
        transition,
        color: hoverColor,
        background: stripeBg
      }}
      className="stripe"
    >
      {stripe}
    </div>
  ) : (
    <div className="stripe" style={{ color }}>
      {text}
    </div>
  );

  const commonProps = {
    ...activeProps,
    ref: textRef,
    className: `loopText ${isHover ? "isHover" : ""}`,
    onTransitionEnd: handleTransEnd
  };

  return <div {...commonProps}>{child}</div>;
}

function List(props) {
  const list = props.texts.map((text) => {
    return <LoopText {...props} key={text} text={text} />;
  });
  return (
    <div style={{ backgroundColor: props.bgColor }} className="list">
      {list}
    </div>
  );
}

ReactDOM.render(
  <div>
    <List {...configs} />
  </div>,
  document.getElementById("root")
);
<div id="root"></div>
.list {
  font-family: "Fredoka One";
  user-select: none;
  padding: 20px;
  display: flex;
  flex-direction: column;
  line-height: 1;
  font-size: 10vh;
  min-height: 100vw;
  > div {
    cursor: pointer;
    margin-right: auto;
  }
}

.stripe {
  height: 1em;
  overflow-y: hidden;
  overflow-x: auto;
}