React 网页漂浮小元素 页面滚动 元素始终在页面显示范围内运动

69 阅读1分钟
import Styles from './index.less';
import { useEffect, useRef, useState } from 'react';

const AdviceBox = (props: any) => {
  const { onBoxClick, show } = props;
  const activeBox: any = useRef(null);

  //计算最大left和top值
  let left = document.documentElement.clientWidth - 200 //(200->元素自身的宽); 
  let top = document.documentElement.clientHeight - 50; //(50->元素自身的高); 

  const [activeInterval, setActiveInterval] = useState(null);
  const [max_left, setMax_left] = useState<number>(left);
  const [max_top, setMax_top] = useState<number>(top);

  let active_x: number = 1;
  let active_y: number = 1;

  useEffect(() => {
    startTimer();
    return () => {
      clearTimer();
    };
  }, []);

  const changePos = () => {
    //获得元素旧的left top值
    let old_left = activeBox.current?.offsetLeft;
    let old_top = activeBox.current?.offsetTop;

    //计算新的left,top值
    let new_left = old_left + active_x;
    let new_top = old_top + active_y;

    if (new_left > max_left) {
      new_left = max_left;
    }
    if (new_top > max_top + document.documentElement.scrollTop) {
      new_top = max_top + document.documentElement.scrollTop;
    }
    if (new_left < 0) {
      new_left = 0;
    }
    if (new_top < document.documentElement.scrollTop) {
      new_top = document.documentElement.scrollTop;
    }
   
    //将新值赋值回去
    if (activeBox.current) {
      activeBox.current.style.left = new_left + 'px';
      activeBox.current.style.top = new_top + 'px';
    }

    //判断
    if (new_top >= max_top + document.documentElement.scrollTop) {
      active_y = -1;
    }
    if (new_top == document.documentElement.scrollTop) {
      active_y = 1;
    }
    if (new_left >= max_left) {
      active_x = -1;
    }
    if (new_left == 0) {
      active_x = 1;
    }
  };

  const clearTimer = () => {
    if (activeInterval) {
      clearInterval(activeInterval);
      setActiveInterval(null);
    }
  };
  const startTimer = () => {
    const interval: any = setInterval(changePos, 10);
    setActiveInterval(interval);
  };
  const onClickBox = () => {
    clearTimer();
    onBoxClick();
  };

  const resetActive = () => {
    //广告归位,重新计算位置
    setMax_left(document.documentElement.clientWidth - activeBox.current?.offsetWidth);
    setMax_top(document.documentElement.clientHeight - activeBox.current?.offsetHeight);

    //广告回到原位
    activeBox.current.style.left = 0;
    activeBox.current.style.top = 0;
    active_x = 1;
    active_y = 1;
  };

  window.onresize = () => {
    resetActive();
  };
  window.onscroll = () => {};

  return (
    <div
      ref={activeBox}
      className={Styles.advice_box}
      style={show ? { display: 'none' } : { display: 'block' }}
      onMouseOver={clearTimer}
      onMouseLeave={startTimer}
      onClick={() => onClickBox()}
    >
      <div className={Styles.advice_box_content}>
        <div className={Styles.icon}>
          <span
            className="iconfont icon-message-reply"
            style={{
              position: 'absolute',
              top: '-27rem',
              left: '-7rem',
              fontSize: '75rem',
              color: '#FCCA02',
            }}
          />
          <span
            className="iconfont icon-fankuixuanzhong"
            style={{
              position: 'absolute',
              top: '-2rem',
              left: '14rem',
              fontSize: '35rem',
              color: '#fff',
            }}
          />
        </div>
        <div>ご意見箱</div>
      </div>
    </div>
  );
};

export default AdviceBox;