
import React, { useEffect, useState, useRef, useCallback } from "react";
import "./scrollarea.css";
const ScrollArea = () => {
const [pin, setPin] = useState("bottom");
const [top, setTop] = useState(64);
const wrapperRef = useRef(null);
const originalStartY = useRef(null);
const startY = useRef(null);
const translateY = useRef(null);
const moving = useRef(false);
const reachedTop = useRef(false);
const [boudingRect, setBoudingRect] = useState([]);
useEffect(() => {
translateY.current = originalStartY.current = getTranslateY();
console.log("translateY:", translateY.current, pin);
}, [pin]);
useEffect(() => {
setBoudingRect([20, window.innerHeight - top]);
}, []);
const handleStart = (event) => {
startY.current = event.touches[0].clientY;
moving.current = true;
console.log("handleStart:-------------", startY.current, moving.current);
};
const handleMove = useCallback(
(event) => {
if (!moving.current) return;
const currentY = event.touches[0].clientY;
console.log("handleMove:", currentY, startY.current, moving.current);
const deltaY = currentY - startY.current;
const distY = Math.max(
boudingRect[0],
Math.min(translateY.current + deltaY, boudingRect[1])
);
if (pin === "top") {
const scrollTop = wrapperRef.current.querySelector(
".scroll-area-content"
).scrollTop;
console.log("scrollTop:", scrollTop, deltaY, reachedTop.current, distY);
if (scrollTop > 0) {
reachedTop.current = false;
} else if (scrollTop === 0 && !reachedTop.current) {
reachedTop.current = true;
startY.current = currentY;
} else if (scrollTop === 0 && deltaY < 0) {
console.log("向上滑动");
} else if (scrollTop === 0 && reachedTop.current) {
event.preventDefault();
wrapperRef.current.style.transform = `translateY(${distY}px)`;
}
} else if (pin === "bottom") {
event.preventDefault();
wrapperRef.current.style.transform = `translateY(${distY}px)`;
}
},
[pin, boudingRect]
);
const handleEnd = useCallback(() => {
if (!moving.current) return;
moving.current = false;
const currentTranslateY = getTranslateY();
const diff = currentTranslateY - originalStartY.current;
let isChangePin = false;
const transitionEndHandler = () => {
if (isChangePin) {
console.log("切换pin状态");
setPin(pin === "bottom" ? "top" : "bottom");
}
wrapperRef.current.style.transition = "none";
wrapperRef.current.removeEventListener(
"transitionend",
transitionEndHandler
);
};
wrapperRef.current.addEventListener("transitionend", transitionEndHandler);
wrapperRef.current.style.transition = "transform 0.3s ease";
if (pin === "bottom") {
if (Math.abs(diff) > 100) {
isChangePin = true;
wrapperRef.current.style.transform = `translateY(${boudingRect[0]}px)`;
} else {
wrapperRef.current.style.transform = `translateY(${translateY.current}px)`;
}
} else if (pin === "top") {
if (Math.abs(diff) > 100 && reachedTop.current) {
isChangePin = true;
wrapperRef.current.style.transform = `translateY(${boudingRect[1]}px)`;
} else {
wrapperRef.current.style.transform = `translateY(${translateY.current}px)`;
}
}
}, [pin, boudingRect]);
const getTranslateY = () => {
const transform = window.getComputedStyle(
document.querySelector(".scroll-area")
).transform;
return parseInt(transform.split(",")[5]);
};
useEffect(() => {
const element = wrapperRef.current;
if (!element) return;
console.log("wrapperRef:", element);
const handleMoveWrapper = (e) => handleMove(e);
element.addEventListener("touchstart", handleStart);
window.addEventListener("touchmove", handleMoveWrapper, { passive: false });
window.addEventListener("touchend", handleEnd);
return () => {
element.removeEventListener("touchstart", handleStart);
window.removeEventListener("touchmove", handleMoveWrapper, {
passive: false,
});
window.removeEventListener("touchend", handleEnd);
};
}, [handleMove, handleEnd]);
return (
<div className="scroll-area" ref={wrapperRef}>
<div className="scroll-area-bar"></div>
<div className="scroll-area-content">
<p>这里是主要内容...</p>
{/* 添加更多内容以便滚动 */}
{Array.from({ length: 50 }).map((_, i) => (
<p key={i} style={{ lineHeight: "24px", fontSize: "14px" }}>
这是第 {i + 1} 行内容。
</p>
))}
</div>
</div>
);
};
export default ScrollArea;
.scroll-area {
position: fixed;
left: 16px;
right: 16px;
bottom: 0;
height: 100vh;
z-index: 50;
background: #fff;
border-radius: 10px;
transform: translateY(calc(100% - 64px));
transition: transform 0.3s ease-in-out;
overflow: hidden;
}
.scroll-area-bar {
height: 20px;
background: #ddd;
text-align: center;
color: #eee;
}
.scroll-area-content {
-webkit-overflow-scrolling: touch;
height: 100%;
background: #fafafa;
padding: 1px 16px;
overflow-x: auto;
}