在手机端做个滚动效果

用react开发一个antd mobile组件,页面上有多个高度不等的div元素,每个div底部都有一个「核对」按钮。当div的高度大于屏幕高度的时候,就要将这个div底部的「核对」按钮锁定在页面底部,方便用户操作。当用户滚动页面,这个div的底部进入屏幕的时候,就要取消「核对」按钮的锁定,要它回归原处,跟随这个div进行滚动。当第二个div进入屏幕50%的时候,在此将它的「核对」按钮锁定在页面上。你帮我用监听滚动的方式实现这个功能,所有的代码都写在一个文件里面,组件的名字叫:MaterialVerificationSon

如果出现按钮取消锁定有抖动,是样式问题,用 transform: isFixed ? 'translateX(-2px)' : 'none',可以解决

js 复制代码
import { Button } from 'antd-mobile';
import React, { useEffect, useRef, useState } from 'react';

// import { useLocation } from '@tanstack/react-location';

export const MaterialVerificationSon: React.FC = () => {
  // 示例数据:包含不同高度的div内容
  const [items] = useState([
    { id: 1, content: '这是第一个材料项,高度适中', height: 780 },
    { id: 2, content: '这是第二个材料项,高度较高,内容较多,需要滚动查看', height: 800 },
    { id: 3, content: '这是第三个材料项,高度非常高,内容非常多,需要大量滚动', height: 1200 },
    { id: 4, content: '这是第四个材料项,高度适中', height: 400 },
    { id: 5, content: '这是第五个材料项,高度较高', height: 900 },
  ]);
  const contentRef = useRef<HTMLDivElement>(null);

  // 存储每个元素的ref
  const itemRefs = useRef<Array<HTMLDivElement | null>>([]);
  // 存储每个按钮的锁定状态
  const [lockedButtons, setLockedButtons] = useState<Record<number, boolean>>({});

  // 获取屏幕高度
  const getScreenHeight = () => window.innerHeight;

  // 检查元素是否在视口中
  const isElementInViewport = (element: HTMLElement) => {
    const rect = element.getBoundingClientRect();
    return rect.top < getScreenHeight() && rect.bottom > 0;
  };

  // 检查元素底部是否在视口内
  const isElementBottomInViewport = (element: HTMLElement) => {
    const rect = element.getBoundingClientRect();
    const elementBottom = rect.bottom;
    return elementBottom <= getScreenHeight() && elementBottom > 0;
  };

  // 检查元素是否至少有50%在视口内
  const isElementHalfVisible = (element: HTMLElement) => {
    const rect = element.getBoundingClientRect();
    const elementHeight = rect.height;
    const visibleHeight = Math.min(Math.max(getScreenHeight() - rect.top, 0), elementHeight);
    return visibleHeight / elementHeight >= 0.5;
  };

  // 检查元素是否过高(超过屏幕高度)
  const isElementTooTall = (element: HTMLElement) => {
    return element.scrollHeight > getScreenHeight();
  };

  // 处理滚动事件
  const handleScroll = () => {
    const newLockedButtons: Record<number, boolean> = {};

    items.forEach((item, index) => {
      const element = itemRefs.current[index];
      if (!element) return;

      const elementTooTall = isElementTooTall(element); //是否大于屏幕高度
      const elementInViewport = isElementInViewport(element); //是不是在视口里面
      const elementBottomInViewport = isElementBottomInViewport(element); //检查元素底部是否在视口内
      const elementHalfVisible = isElementHalfVisible(element); //视口50%

      console.log(elementInViewport, index);
      if (elementInViewport && elementTooTall && elementHalfVisible) {
        newLockedButtons[item.id] = true;
      }

      if (elementBottomInViewport) {
        newLockedButtons[item.id] = false;
      }
    });

    setLockedButtons(newLockedButtons);
  };
  console.log(lockedButtons, 9999);
  // 添加滚动事件监听器
  useEffect(() => {
    if (contentRef.current) {
      contentRef.current.addEventListener('scroll', handleScroll);
      // 初始执行一次
      handleScroll();
    }

    return () => {
      if (contentRef.current) {
        contentRef.current.removeEventListener('scroll', handleScroll);
      }
    };
  }, []);

  // 核对按钮点击处理
  const handleVerify = (id: number) => {
    console.log(`核对按钮被点击,ID: ${id}`);
    // 这里可以添加具体的核对逻辑
  };

  return (
    <div style={{ padding: '16px', height: '100vh', overflowY: 'auto' }} ref={contentRef}>
      <h2>材料核对页面</h2>

      {items.map((item, index) => (
        <div
          key={item.id}
          ref={el => (itemRefs.current[index] = el)}
          style={{
            border: '1px solid #ddd',
            borderRadius: '8px',
            padding: '16px',
            marginBottom: '16px',
            overflowY: 'auto',
          }}
        >
          <h3>材料项 {item.id}</h3>
          <p>{item.content}</p>
          <p>高度: {item.height}px</p>
          <p style={{ height: `${item.height}px` }}>
            这是一个示例内容,实际应用中这里会有具体的材料信息。
          </p>

          {/* 核对按钮 */}
          <div
            style={{
              position: lockedButtons[item.id] ? 'fixed' : 'static',
              bottom: lockedButtons[item.id] ? '20px' : 'auto',
              left: lockedButtons[item.id] ? '50%' : 'auto',
              transform: lockedButtons[item.id] ? 'translateX(-50%)' : 'none',
              zIndex: 1000,
              width: '100%',
              display: 'flex',
              justifyContent: 'center',
              marginTop: '16px',
            }}
          >
            <Button
              color="primary"
              size="large"
              onClick={() => handleVerify(item.id)}
              style={{
                width: '80%',
                maxWidth: '300px',
              }}
            >
              核对1111{index}
            </Button>
          </div>
        </div>
      ))}
    </div>
  );
};
相关推荐
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte3 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
NEXT063 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法
剪刀石头布啊3 小时前
生成随机数,Math.random的使用
前端
剪刀石头布啊3 小时前
css外边距重叠问题
前端