Skip to content
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

Merged
merged 16 commits into from
Aug 20, 2024

Conversation

Malayvasa
Copy link
Contributor

Related to #381

Comment on lines 25 to 51
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)
}
Copy link
Member

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 useCallbacks.

Copy link
Contributor Author

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) {
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in fffb8ab

Comment on lines 31 to 38
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) {
Copy link
Member

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.

Suggested change
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

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 01d5a3b

Comment on lines 24 to 71
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])
Copy link
Member

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 useCallbacks would help.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 01d5a3b

Comment on lines 45 to 51
const lerpMousePosition = () => {
setLerpedMousePosition((prev) => ({
x: lerp(prev.x, mousePosition.x),
y: lerp(prev.y, mousePosition.y),
}))
requestAnimationFrame(lerpMousePosition)
}
Copy link
Member

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?

Copy link
Contributor Author

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

Comment on lines 2 to 3
canvasRef: React.RefObject<HTMLCanvasElement>,
containerRef: React.RefObject<HTMLDivElement>,
Copy link
Member

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.

Suggested change
canvasRef: React.RefObject<HTMLCanvasElement>,
containerRef: React.RefObject<HTMLDivElement>,
canvas: HTMLCanvasElement,
container: HTMLDivElement,

Copy link
Contributor Author

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

Comment on lines 1 to 20
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 }
}
Copy link
Member

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.

Copy link
Contributor Author

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

@Malayvasa Malayvasa requested a review from iamacook August 1, 2024 14:03
@Malayvasa
Copy link
Contributor Author

@iamacook I have addressed all comments I could that did not break the animation.

@Malayvasa Malayvasa changed the base branch from malay-branch-1 to main August 2, 2024 15:14
Comment on lines 35 to 69
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>
)
}
Copy link
Member

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?

Copy link
Contributor Author

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.

Comment on lines 38 to 39
font-size: 80px;
line-height: 88px;
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 01d5a3b

Copy link
Member

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/lerp.ts Outdated Show resolved Hide resolved
src/components/DataRoom/IndustryComparison/DotGrid.tsx Outdated Show resolved Hide resolved
src/components/DataRoom/IndustryComparison/DotGrid.tsx Outdated Show resolved Hide resolved
src/components/DataRoom/IndustryComparison/DotGrid.tsx Outdated Show resolved Hide resolved
Copy link
Member

@DiogoSoaress DiogoSoaress left a 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">
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9f82803

src/components/common/SlidingPanel/index.tsx Outdated Show resolved Hide resolved

const animate = () => {
if (dimensions.width > 0 && dimensions.height > 0) {
drawDots(ctx, dots, dimensions, mousePosition, isMobile)
Copy link
Member

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)

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9f82803

Copy link
Member

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?

src/components/DataRoom/IndustryComparison/Content.tsx Outdated Show resolved Hide resolved
src/components/DataRoom/IndustryComparison/Content.tsx Outdated Show resolved Hide resolved
if (!ctx) return

const dots = createDots(dimensions, isMobile)
updateCanvas(canvas, ctx, dimensions.width, dimensions.height)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9f82803

Copy link
Member

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

Copy link
Member

@DiogoSoaress DiogoSoaress left a 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.

@Malayvasa
Copy link
Contributor Author

I have moved the mouse hover effect in the DotGrid component to PR #442 as it is not performant yet.

@DiogoSoaress DiogoSoaress merged commit ca43bb3 into safe-global:main Aug 20, 2024
3 of 4 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Aug 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants