React PC端懸浮錨點吸頂導航


需求描述

  • 實現PC端吸頂導航

實現分析

  • 先采用absolute絕對定位
  • 滾動超過錨點導航時,修改該導航定位為fixed

效果預覽

實現方法

/** 錨點導航*/
import React from "react";
import styles from "./index.less";
import classnames from "classnames";
import { ActiveType } from "@17zwd/fe-egg-factory-page/detail/type";

interface PageState {
  // 目錄激活項
  activeItem?: string;
  // 是否吸頂
  isSticky?: boolean;
}

class AnchorNav extends React.Component<any, PageState> {
  private scrollTimer;
  constructor(props) {
    super(props);
    this.state = {
      activeItem: ActiveType.baseInfo,
      isSticky: false,
    };
  }

  componentDidMount(): void {
    // 綁定頁面滾動事件
    window.addEventListener("scroll", this.onPageScroll);
  }

  componentWillUnmount(): void {
    // 解綁頁面滾動事件
    window.removeEventListener("scroll", this.onPageScroll);
  }

  compare = (prop) => {
    return function (a, b) {
      const value1 = a[prop];
      const value2 = b[prop];
      return value1 - value2;
    };
  };

  // 頁面滾動事件
  onPageScroll = (): void => {
    if (this.scrollTimer) {
      clearTimeout(this.scrollTimer);
    }
    this.scrollTimer = setTimeout(() => {
      // 吸頂
      const firstItemTop = document
        .getElementById(ActiveType.baseInfo)
        ?.getBoundingClientRect().top;
      if (firstItemTop && firstItemTop < 0) {
        this.setState({
          isSticky: true,
        });
      } else {
        this.setState({
          isSticky: false,
        });
      }
      // 動態高亮
      const viewTops: Array<any> = [];
      for (const i in ActiveType) {
        const id = ActiveType[i];
        const ele = document.getElementById(id);
        if (!ele) return;
        const top = ele?.getBoundingClientRect().top;
        if (top < 0) continue;
        viewTops.push({
          id,
          top,
        });
      }
      viewTops.sort(this.compare("top"));
      this.setState({
        activeItem: viewTops[0]?.id,
      });
    }, 90);
  };

  //跳轉到的錨點
  onScrollToAnchor = (activeItem: string): void => {
    if (activeItem) {
      // 找到錨點
      const anchorElement = document.getElementById(activeItem);
      // 如果對應id的錨點存在,就跳轉到錨點
      if (anchorElement) {
        anchorElement.scrollIntoView({ block: "start", behavior: "smooth" });
        this.setState({
          activeItem,
        });
      }
    }
  };

  getItemName = (en: string): string => {
    let res = "";
    switch (en) {
      case ActiveType.baseInfo:
        res = "基礎信息";
        break;
      case ActiveType.certify:
        res = "資質認證";
        break;
      case ActiveType.overview:
        res = "綜合概覽";
        break;
      case ActiveType.remark:
        res = "工廠簡介";
        break;
      case ActiveType.pics:
        res = "工廠實拍";
        break;
      case ActiveType.product:
        res = "產品案例";
        break;
      default:
        break;
    }
    return res;
  };

  renderItem = (): JSX.Element => {
    const { activeItem } = this.state;
    const items: Array<any> = [];
    for (const i in ActiveType) {
      const element = ActiveType[i];
      if (element) {
        items.push(element);
      }
    }
    return (
      <React.Fragment>
        {items.map((item, index) => {
          return (
            <li
              key={index}
              onClick={() => this.onScrollToAnchor(item)}
              className={activeItem === item ? styles.active : ""}
            >
              <span
                className={classnames(styles.anchorItem, styles.anchorNoBorder)}
              >
                {this.getItemName(item)}
              </span>
            </li>
          );
        })}
      </React.Fragment>
    );
  };

  render(): JSX.Element {
    const { isSticky } = this.state;
    return (
      <React.Fragment>
        <div
          className={classnames(
            styles.mainAnchor,
            isSticky ? styles.positionSticky : styles.positionAbsolute
          )}
        >
          <ul className={styles.anchorList}>{this.renderItem()}</ul>
        </div>
      </React.Fragment>
    );
  }
}

export default AnchorNav;

@import "@/less/mixin.less";

// 導航
.positionSticky {
  position: fixed;
  margin-left: -124px;
  z-index: 1;
}
.positionAbsolute {
  position: absolute;
  left: -124px;
}
.mainAnchor {
  top: 0;
  width: 124px;
  .anchorList {
    margin-bottom: 0;
    padding: 0 24px;
    width: 112px;
    box-sizing: border-box;
    background-color: @background-secondary-color;
    > li {
      position: relative;
      color: @text-thrid-color;
      transition: all 0.2s linear;
      &:hover {
        color: @main-primary-color;
      }
    }
    .anchorItem {
      display: block;
      padding: 16px 0;
      height: 53px;
      font-size: 16px;
      line-height: 21px;
      box-sizing: border-box;
      border-top: solid 1px @border-primary-color;
      cursor: pointer;
    }
    .anchorNoBorder {
      border-top: none;
    }
    .active {
      color: @main-primary-color;
      &::before {
        position: absolute;
        content: "";
        left: -14px;
        top: 24px;
        width: 6px;
        height: 6px;
        border-radius: 50%;
        background-color: @main-primary-color;
      }
    }
  }
  .navBox {
    visibility: hidden;
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM