I have the following the component, I made a custom scrollbar for fabric js. What I’m trying to do is, I want to move the scroll thumbs relative to the canvas once I run the mouse:down event. It would be a great help if someone can suggest a logic for the same:
import { useMemo, useRef, useEffect, useState } from "react";
import { useAtomValue } from "jotai";
import { zoomAtom } from "../Atoms";
import useEditorService from "@/hooks/useEditorService";
const CustomScroll = () => {
const zoom = useAtomValue(zoomAtom);
const { activeEditor } = useEditorService();
const [editorPos, setEditorPos] = useState({ x: 0, y: 0 });
// =========================================================================================================
const minFitZoom = useMemo(
() =>
Math.min(
activeEditor?.dimensions?.width / activeEditor?.originalSize?.width,
activeEditor?.dimensions?.height / activeEditor?.originalSize?.height
),
[activeEditor]
);
const boxYRef = useRef(null);
const scrollThumbHeight = useMemo(() => {
return ((5 - zoom) / 5) * activeEditor?.dimensions.height;
}, [zoom, activeEditor]);
const minHeight = "100px";
const customYScrollStyle = {
display: minFitZoom < zoom ? "block" : "none",
};
// Reset Scroll Position after full zoom out
useEffect(() => {
setEditorPos((p) => ({ ...p, y: 0 }));
}, [zoom, minFitZoom]);
// Handle Drags on Y axis
const handleYDragStart = (e) => {
e.dataTransfer.setData("text/plain", "");
e.dataTransfer.setDragImage(new Image(), 0, 0);
e.stopPropagation();
boxYRef.current.dragging = true;
boxYRef.current.startY = e.clientY; // position of the cursor from browser's top
boxYRef.current.startTop = boxYRef.current.offsetTop; // position of thumb from its parent's top
};
const handleYDrag = (e) => {
if (!boxYRef.current.dragging) return;
e.preventDefault();
const deltaY = e.clientY - boxYRef.current.startY; // change in the previous and current position based on mouse movements
const newTop = boxYRef.current.startTop + deltaY; // Setting the new position of the thumb by adding how much it has scrolled (up/down) + distance from the top
const maxTop =
boxYRef.current.parentElement.clientHeight - boxYRef.current.clientHeight; // setting the top limit
const clampedTop = Math.min(Math.max(newTop, 0), maxTop); // To watch the current max Top limit of the thumb so that its visible within the parent area
// Update the top position of the scroll thumb
boxYRef.current.style.top = `${clampedTop}px`;
// Calculate the ratio of scroll position to canvas position
const scrollRatio = clampedTop / maxTop;
// Calculate the new top position for the canvas
const canvasTop =
scrollRatio *
(zoom * activeEditor.canvas.height - activeEditor.dimensions.height);
setEditorPos((p) => ({ ...p, y: canvasTop }));
// Update the canvas position
activeEditor.canvas.absolutePan({ x: editorPos.x, y: editorPos.y });
};
const handleYDragEnd = () => {
boxYRef.current.dragging = false;
};
// =========================================================================================================
const maxFitZoom = useMemo(
() =>
Math.max(
activeEditor?.dimensions?.width / activeEditor?.originalSize?.width,
activeEditor?.dimensions?.height / activeEditor?.originalSize?.height
),
[activeEditor]
);
const boxXRef = useRef(null);
const scrollThumbWidth = useMemo(() => {
return ((5 - zoom) / 5) * activeEditor?.dimensions.width;
}, [zoom, activeEditor]);
const minWidth = "100px";
const customXScrollStyle = {
display: maxFitZoom < zoom ? "block" : "none",
};
// Reset Scroll Position after full zoom out
useEffect(() => {
setEditorPos((p) => ({ ...p, x: 0 }));
}, [zoom, maxFitZoom]);
// Handle Drags on X axis
const handleXDragStart = (e) => {
e.dataTransfer.setData("text/plain", "");
e.dataTransfer.setDragImage(new Image(), 0, 0);
e.stopPropagation();
boxXRef.current.dragging = true;
boxXRef.current.startX = e.clientX; // position of the cursor from browser's top
boxXRef.current.startLeft = boxYRef.current.offsetLeft; // position of thumb from its parent's top
};
const handleXDrag = (e) => {
if (!boxXRef.current.dragging) return;
e.preventDefault();
const deltaX = e.clientX - boxXRef.current.startX; // change in the previous and current position based on mouse movements
const newLeft = boxXRef.current.startLeft + deltaX; // Setting the new position of the thumb by adding how much it has scrolled (up/down) + distance from the top
const maxLeft =
boxXRef.current.parentElement.clientWidth - boxXRef.current.clientWidth; // setting the top limit
const clampedLeft = Math.min(Math.max(newLeft, 0), maxLeft); // To watch the current max Top limit of the thumb so that its visible within the parent area
// Update the top position of the scroll thumb
boxXRef.current.style.left = `${clampedLeft}px`;
// Calculate the ratio of scroll position to canvas position
const scrollRatio = clampedLeft / maxLeft;
// Calculate the new top position for the canvas
const canvasLeft =
scrollRatio *
(zoom * activeEditor.canvas.width - activeEditor.dimensions.width);
setEditorPos((p) => ({ ...p, x: canvasLeft }));
// Update the canvas position
activeEditor.canvas.absolutePan({ x: editorPos.x, y: editorPos.y });
};
const handleXDragEnd = () => {
boxXRef.current.dragging = false;
};
// =========================================================================================================
// Change Thumb Positions based on
// useEffect(() => {
// if (!activeEditor) return;
// activeEditor?.canvas.on("mouse:wheel", (e) => {
// const pointer = activeEditor?.canvas.getPointer(e.e);
// const editorWidth = activeEditor?.canvas.getWidth();
// const editorHeight = activeEditor?.canvas.getHeight();
// const verticalThumbY = (pointer.y / editorHeight) * 100;
// const horizontalThumbX = (pointer.x / editorWidth) * 100;
// // Update thumb positions
// boxYRef.current.style.top = `${verticalThumbY}%`;
// boxXRef.current.style.left = `${horizontalThumbX}%`;
// });
// }, [activeEditor]);
useEffect(() => {
if (zoom > minFitZoom || zoom > maxFitZoom) {
activeEditor?.canvas.on("mouse:down", (e) => {
if (e.e.altKey === true) {
}
});
}
}, [activeEditor]);
return (
<div>
<div id="customYscroll" style={customYScrollStyle}>
<div
className="custom-y-scroll-thumb"
style={{ minHeight, height: scrollThumbHeight, position: "relative" }}
draggable="true"
onDragStart={handleYDragStart}
onDrag={handleYDrag}
onDragEnd={handleYDragEnd}
ref={boxYRef}
></div>
</div>
<div id="customXscroll" style={customXScrollStyle}>
<div
className="custom-x-scroll-thumb"
style={{ minWidth, width: scrollThumbWidth, position: "relative" }}
draggable="true"
onDragStart={handleXDragStart}
onDrag={handleXDrag}
onDragEnd={handleXDragEnd}
ref={boxXRef}
></div>
</div>
</div>
);
};
export default CustomScroll;
CSS:
#customYscroll {
position: absolute;
right: 0px;
background: #888888;
width: 14px;
height: 95%;
z-index: 10;
padding: 2px;
border-radius: 50px;
margin: 10px;
transition: all .1s ease-in-out;
overflow: hidden;
}
#customYscroll:hover {
width: 16px;
}
.custom-y-scroll-thumb {
height: 100%;
z-index: 11;
background: #bcbcbc;
border-radius: 50px;
}
#customXscroll {
position: absolute;
bottom: 0px;
background: #888888;
height: 14px; /* Height of the x-axis scrollbar */
width: 95%; /* Width of the x-axis scrollbar */
z-index: 10;
padding: 2px;
border-radius: 50px;
margin: 10px;
transition: all 0.1s ease-in-out;
overflow: hidden;
}
#customXscroll:hover {
height: 16px; /* Increase height on hover for better visibility */
}
.custom-x-scroll-thumb {
height: 100%;
z-index: 11;
background: #bcbcbc;
border-radius: 50px;
}
Tried to set the top and left of the x and y thumbs.
New contributor
Vansh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.