-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 🎸 Add Industry Comparison Section #427
feat: 🎸 Add Industry Comparison Section #427
Conversation
const updateDimensions = () => { | ||
const newDimensions = updateCanvasDimensions(canvasRef, containerRef) | ||
setDimensions(newDimensions) | ||
} | ||
|
||
const handleMouseMove = (e: MouseEvent) => { | ||
if (canvasRef.current && containerRef.current) { | ||
const rect = containerRef.current.getBoundingClientRect() | ||
setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) | ||
} | ||
} | ||
|
||
const updateMobileMousePosition = () => { | ||
if (scrollYProgress && isMobile && containerRef.current) { | ||
const progress = scrollYProgress.get() | ||
const { height } = containerRef.current.getBoundingClientRect() | ||
setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) | ||
} | ||
} | ||
|
||
const lerpMousePosition = () => { | ||
setLerpedMousePosition((prev) => ({ | ||
x: lerp(prev.x, mousePosition.x), | ||
y: lerp(prev.y, mousePosition.y), | ||
})) | ||
requestAnimationFrame(lerpMousePosition) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the dependencies, I'd suggest we move memoise these in useCallback
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 01d5a3b
} | ||
|
||
const handleMouseMove = (e: MouseEvent) => { | ||
if (canvasRef.current && containerRef.current) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is canvasRef.current
required for the mouse position? The value isn't used inside the handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in fffb8ab
if (canvasRef.current && containerRef.current) { | ||
const rect = containerRef.current.getBoundingClientRect() | ||
setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) | ||
} | ||
} | ||
|
||
const updateMobileMousePosition = () => { | ||
if (scrollYProgress && isMobile && containerRef.current) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use early returns here to reduce indentation, e.g.
if (canvasRef.current && containerRef.current) { | |
const rect = containerRef.current.getBoundingClientRect() | |
setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) | |
} | |
} | |
const updateMobileMousePosition = () => { | |
if (scrollYProgress && isMobile && containerRef.current) { | |
if (canvasRef.current && containerRef.current) return | |
const rect = containerRef.current.getBoundingClientRect() | |
setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) | |
} | |
const updateMobileMousePosition = () => { | |
if (scrollYProgress && isMobile && containerRef.current) return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 01d5a3b
x: lerp(prev.x, mousePosition.x), | ||
y: lerp(prev.y, mousePosition.y), | ||
})) | ||
requestAnimationFrame(lerpMousePosition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to clean this up with cancelAnimationFrame
on unmount.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 01d5a3b
useEffect(() => { | ||
const updateDimensions = () => { | ||
const newDimensions = updateCanvasDimensions(canvasRef, containerRef) | ||
setDimensions(newDimensions) | ||
} | ||
|
||
const handleMouseMove = (e: MouseEvent) => { | ||
if (canvasRef.current && containerRef.current) { | ||
const rect = containerRef.current.getBoundingClientRect() | ||
setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) | ||
} | ||
} | ||
|
||
const updateMobileMousePosition = () => { | ||
if (scrollYProgress && isMobile && containerRef.current) { | ||
const progress = scrollYProgress.get() | ||
const { height } = containerRef.current.getBoundingClientRect() | ||
setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) | ||
} | ||
} | ||
|
||
const lerpMousePosition = () => { | ||
setLerpedMousePosition((prev) => ({ | ||
x: lerp(prev.x, mousePosition.x), | ||
y: lerp(prev.y, mousePosition.y), | ||
})) | ||
requestAnimationFrame(lerpMousePosition) | ||
} | ||
|
||
updateDimensions() | ||
window.addEventListener('resize', updateDimensions) | ||
const container = containerRef.current | ||
if (isMobile && scrollYProgress) { | ||
scrollYProgress.onChange(updateMobileMousePosition) | ||
} else { | ||
container?.addEventListener('mousemove', handleMouseMove) | ||
} | ||
lerpMousePosition() | ||
|
||
return () => { | ||
window.removeEventListener('resize', updateDimensions) | ||
if (isMobile && scrollYProgress) { | ||
scrollYProgress.clearListeners() | ||
} else { | ||
container?.removeEventListener('mousemove', handleMouseMove) | ||
} | ||
} | ||
}, [mousePosition.x, mousePosition.y, containerRef, isMobile, scrollYProgress, dimensions.width]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot going on here in one useEffect
. If it's possible, breaking it down would be ideal. Memoising the methods in useCallback
s would help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 01d5a3b
const lerpMousePosition = () => { | ||
setLerpedMousePosition((prev) => ({ | ||
x: lerp(prev.x, mousePosition.x), | ||
y: lerp(prev.y, mousePosition.y), | ||
})) | ||
requestAnimationFrame(lerpMousePosition) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Afaiu, this is related to drawing the dots? Could we locate them together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in drawDots
util in 01d5a3b
canvasRef: React.RefObject<HTMLCanvasElement>, | ||
containerRef: React.RefObject<HTMLDivElement>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of passing the ref, I think we can use the element directly, e.g.
canvasRef: React.RefObject<HTMLCanvasElement>, | |
containerRef: React.RefObject<HTMLDivElement>, | |
canvas: HTMLCanvasElement, | |
container: HTMLDivElement, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in updateCanvas
util in 01d5a3b
export const updateCanvasDimensions = ( | ||
canvasRef: React.RefObject<HTMLCanvasElement>, | ||
containerRef: React.RefObject<HTMLDivElement>, | ||
) => { | ||
if (canvasRef.current && containerRef.current) { | ||
const { clientWidth, clientHeight } = containerRef.current | ||
const pixelRatio = window.devicePixelRatio || 1 | ||
canvasRef.current.width = clientWidth * pixelRatio | ||
canvasRef.current.height = clientHeight * pixelRatio | ||
canvasRef.current.style.width = `${clientWidth}px` | ||
canvasRef.current.style.height = `${clientHeight}px` | ||
|
||
const ctx = canvasRef.current.getContext('2d') | ||
if (ctx) { | ||
ctx.scale(pixelRatio, pixelRatio) | ||
} | ||
return { width: clientWidth, height: clientHeight } | ||
} | ||
return { width: 0, height: 0 } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest we split this - it's updating the canvas, but also getting the dimensions of the container (used in the component). I'd suggest we have two functions: something like getContainerDimensions
(and maybe we don't need to store that in state) and then a function to update the canvas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed by using useContainerSize
hook in 01d5a3b
@iamacook I have addressed all comments I could that did not break the animation. |
const RightPanel = ({ | ||
scrollYProgress, | ||
children, | ||
containerRef, | ||
isMobile, | ||
}: { | ||
scrollYProgress: MotionValue<number> | ||
children: ReactNode | ||
isMobile: boolean | ||
containerRef: React.RefObject<HTMLDivElement> | ||
}) => { | ||
const opacityParams = [0.25, 0.35, 0.65, 0.75] | ||
const translateParams = [0.25, 0.35, 0.65, 0.75] | ||
const opacity = useTransform(scrollYProgress, opacityParams, [0, 1, 1, 0]) | ||
const bgTranslate = useTransform(scrollYProgress, translateParams, ['100%', '0%', '0%', '100%']) | ||
|
||
return ( | ||
<div ref={containerRef} className={css.rightPanelContainer}> | ||
<motion.div | ||
className={css.rightPanelContent} | ||
style={{ | ||
opacity: isMobile ? 1 : opacity, | ||
}} | ||
> | ||
{children} | ||
</motion.div> | ||
<motion.div | ||
className={css.rightPanelBG} | ||
style={{ | ||
translateX: isMobile ? '0%' : bgTranslate, | ||
}} | ||
></motion.div> | ||
</div> | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we consolidate this component with the one used in the CryptoPunks section? They seem almost identical.
I would suggest to move this component under the /common
folder and name it something more descriptive. Example SlidingRightPanel
. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not worth doing imo, both the panels have different behaviour for desktop and different behaviour for mobile view and different styling for the content.
font-size: 80px; | ||
line-height: 88px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values are already defined in the h1
variant. I don't think we need this class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 01d5a3b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can delete the class. Why do you need the z-index
here?
src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected some causes of performance issues
opacity: isMobile ? 1 : opacity, | ||
}} | ||
> | ||
<Typography className={css.title} variant="h1"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need this title
class, do you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need it to apply z-index
and pointer-events
properties. Will remove any font size attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 9f82803
|
||
const animate = () => { | ||
if (dimensions.width > 0 && dimensions.height > 0) { | ||
drawDots(ctx, dots, dimensions, mousePosition, isMobile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is expensive to be called non-stop. Can you only redraw the dots when necessary? (i.e, when the mouse moves or when the resolution change)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also explore a way to not have to draw the whole grid when the mouse moves but only the dots that are changing size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 9f82803
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you address it?
if (!ctx) return | ||
|
||
const dots = createDots(dimensions, isMobile) | ||
updateCanvas(canvas, ctx, dimensions.width, dimensions.height) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also being called unconditionally without any change in the dimensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 9f82803
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you address it ? drawDots
still being called unconditionally at every possible frame and updating the canvas context for every dot
src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still some performance considerations:
-
The animation loop runs continuously, which can be CPU-intensive. Consider adding throttling or debouncing if the animation doesn't need to run at the highest possible frame rate.
-
Canvas Updates: The canvas is updated on every frame. Ensure that
drawDots
function is optimized to minimize the amount of work done per frame.
I have moved the mouse hover effect in the DotGrid component to PR #442 as it is not performant yet. |
Related to #381