From 918350ce0dfbf89a509644ecb269bd936c171b80 Mon Sep 17 00:00:00 2001 From: iamacook Date: Thu, 25 Jul 2024 10:44:44 +0200 Subject: [PATCH 01/13] Open branch From c4aebba49baca7e820e52b6982827a42c9446b7d Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 30 Jul 2024 15:58:54 +0530 Subject: [PATCH 02/13] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20Industry=20C?= =?UTF-8?q?omparison=20Section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/DotGrid.tsx | 107 ++++++++++++++++++ .../DataRoom/IndustryComparison/index.tsx | 71 ++++++++++++ .../IndustryComparison/styles.module.css | 69 +++++++++++ .../IndustryComparison/utils/createDots.ts | 16 +++ .../IndustryComparison/utils/drawDots.ts | 35 ++++++ .../DataRoom/IndustryComparison/utils/lerp.ts | 5 + .../utils/updateCanvasDimensions.ts | 20 ++++ src/content/dataroom.json | 4 + 8 files changed, 327 insertions(+) create mode 100644 src/components/DataRoom/IndustryComparison/DotGrid.tsx create mode 100644 src/components/DataRoom/IndustryComparison/index.tsx create mode 100644 src/components/DataRoom/IndustryComparison/styles.module.css create mode 100644 src/components/DataRoom/IndustryComparison/utils/createDots.ts create mode 100644 src/components/DataRoom/IndustryComparison/utils/drawDots.ts create mode 100644 src/components/DataRoom/IndustryComparison/utils/lerp.ts create mode 100644 src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx new file mode 100644 index 00000000..398d03f2 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -0,0 +1,107 @@ +import type { MotionValue } from 'framer-motion' +import { useEffect, useMemo, useRef, useState } from 'react' +import { createDots } from './utils/createDots' +import { updateCanvasDimensions } from './utils/updateCanvasDimensions' +import { drawDots } from './utils/drawDots' +import { lerp } from './utils/lerp' + +export default function DotGrid({ + containerRef, + isMobile, + scrollYProgress, +}: { + containerRef: React.RefObject + isMobile: boolean + scrollYProgress?: MotionValue +}) { + const canvasRef = useRef(null) + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) + const [lerpedMousePosition, setLerpedMousePosition] = useState({ x: 0, y: 0 }) + + const dots = useMemo(() => createDots(dimensions, isMobile), [dimensions, isMobile]) + + 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]) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const ctx = canvas.getContext('2d') + if (!ctx) return + + let animationFrameId: number + + const animate = () => { + drawDots(ctx, dots, dimensions, lerpedMousePosition, isMobile) + animationFrameId = requestAnimationFrame(animate) + } + + animate() + + return () => { + cancelAnimationFrame(animationFrameId) + } + }, [dots, dimensions, lerpedMousePosition, isMobile]) + + return ( + + ) +} diff --git a/src/components/DataRoom/IndustryComparison/index.tsx b/src/components/DataRoom/IndustryComparison/index.tsx new file mode 100644 index 00000000..c0f3f792 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/index.tsx @@ -0,0 +1,71 @@ +import type { BaseBlock } from '@/components/Home/types' +import type { MotionValue } from 'framer-motion' +import { useTransform, motion, useScroll } from 'framer-motion' +import type { ReactNode } from 'react' +import React, { useRef } from 'react' +import css from './styles.module.css' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' +import { Typography } from '@mui/material' +import DotGrid from './DotGrid' + +const IndustryComparison = ({ title }: BaseBlock) => { + const backgroundRef = useRef(null) + const gridContainerRef = useRef(null) + const isMobile = useIsMediumScreen() + + const { scrollYProgress } = useScroll({ + target: backgroundRef, + offset: ['start end', 'end start'], + }) + + return ( +
+
+ + + {title} + + + +
+
+ ) +} + +const RightPanel = ({ + scrollYProgress, + children, + containerRef, + isMobile, +}: { + scrollYProgress: MotionValue + children: ReactNode + isMobile: boolean + containerRef: React.RefObject +}) => { + 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 ( +
+ + {children} + + +
+ ) +} + +export default IndustryComparison diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css new file mode 100644 index 00000000..c5b746f2 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -0,0 +1,69 @@ +.sectionContainer { + height: 300vh; + display: flex; +} + +.stickyContainer { + position: sticky; + top: 0; + width: 100%; + height: 100dvh; + display: flex; +} + +.rightPanelContainer { + width: 100%; + height: 100%; + position: absolute; + right: 0; + color: var(--mui-palette-text-dark); + overflow-x: hidden; + display: flex; + align-items: flex-end; + padding-top: 64px; +} + +.rightPanelContent { + z-index: 20; + display: flex; + justify-items: center; + align-items: center; + width: 100%; + padding: 64px; + height: 100%; + gap: 30px; +} + +.title { + font-size: 80px; + line-height: 88px; + z-index: 30; +} + +.rightPanelBG { + position: absolute; + inset: 0; + background-color: var(--mui-palette-primary-main); + z-index: 10; + margin-top: 72px; +} + +@media (max-width: 900px) { + .rightPanelContainer { + width: 100%; + height: 100%; + position: absolute; + bottom: 0; + } + .rightPanelContent { + padding-left: 32px; + padding-right: 108px; + } + .title { + font-size: 56px; + line-height: 60px; + } + .rightPanelBG { + margin-top: 0px; + } +} diff --git a/src/components/DataRoom/IndustryComparison/utils/createDots.ts b/src/components/DataRoom/IndustryComparison/utils/createDots.ts new file mode 100644 index 00000000..b78cd7a8 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/createDots.ts @@ -0,0 +1,16 @@ +const ROWS = 30 +const MOBILE_COLS = 15 +const DESKTOP_COLS = 60 + +export const createDots = (dimensions: { width: number; height: number }, isMobile: boolean) => { + const cols = isMobile ? MOBILE_COLS : DESKTOP_COLS + const newDots = [] + for (let row = 0; row < ROWS; row++) { + for (let col = 0; col < cols; col++) { + const x = (dimensions.width / (cols - 1)) * col + dimensions.width / cols / 2 + const y = (dimensions.height / (ROWS - 1)) * row + dimensions.height / ROWS / 2 + newDots.push({ x, y }) + } + } + return newDots +} diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts new file mode 100644 index 00000000..44942011 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -0,0 +1,35 @@ +const MAX_SCALE_DISTANCE = 15 +const MAX_POSITION_DISTANCE = 100 +const MOBILE_MAX_SCALE = 8 +const DESKTOP_MAX_SCALE = 12 +const DOT_COLOR = '#121312' + +export const drawDots = ( + ctx: CanvasRenderingContext2D, + dots: { x: number; y: number }[], + dimensions: { width: number; height: number }, + lerpedMousePosition: { x: number; y: number }, + isMobile: boolean, +) => { + ctx.clearRect(0, 0, dimensions.width, dimensions.height) + + const maxScale = isMobile ? MOBILE_MAX_SCALE : DESKTOP_MAX_SCALE + + ctx.fillStyle = DOT_COLOR + + dots.forEach((dot) => { + const dx = lerpedMousePosition.x - dot.x + const dy = lerpedMousePosition.y - dot.y + const distance = Math.sqrt(dx * dx + dy * dy) + + // Apply scale effect + const scale = Math.max(1, maxScale - distance / MAX_SCALE_DISTANCE) + + // Calculate force for translation + const force = Math.max(0, (MAX_POSITION_DISTANCE - distance) / MAX_POSITION_DISTANCE) + + ctx.beginPath() + ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) + ctx.fill() + }) +} diff --git a/src/components/DataRoom/IndustryComparison/utils/lerp.ts b/src/components/DataRoom/IndustryComparison/utils/lerp.ts new file mode 100644 index 00000000..609026b1 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/lerp.ts @@ -0,0 +1,5 @@ +const LERP_FACTOR = 0.1 + +export const lerp = (start: number, end: number) => { + return start + (end - start) * LERP_FACTOR +} diff --git a/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts b/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts new file mode 100644 index 00000000..ec34207e --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts @@ -0,0 +1,20 @@ +export const updateCanvasDimensions = ( + canvasRef: React.RefObject, + containerRef: React.RefObject, +) => { + 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 } +} diff --git a/src/content/dataroom.json b/src/content/dataroom.json index 8293537e..881b7545 100644 --- a/src/content/dataroom.json +++ b/src/content/dataroom.json @@ -49,6 +49,10 @@ "href": "https://dune.com/queries/3737066" } }, + { + "component": "DataRoom/IndustryComparison", + "title": "How we compare
to others industry leaders" + }, { "component": "DataRoom/ExternalLinksGrid", "title": "Dune Boards", From c119de29aa94c562b6a7fbca2b6022c6474b87f8 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Thu, 1 Aug 2024 19:33:27 +0530 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20=F0=9F=90=9B=20address=20PR=20comm?= =?UTF-8?q?ents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/DotGrid.tsx | 66 +++++++++++-------- .../IndustryComparison/utils/drawDots.ts | 9 +++ .../utils/getContainerDimensions.ts | 9 +++ .../utils/updateCanvasDimensions.ts | 18 +++-- src/hooks/useLerpedMousePosition.ts | 24 +++++++ 5 files changed, 90 insertions(+), 36 deletions(-) create mode 100644 src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts create mode 100644 src/hooks/useLerpedMousePosition.ts diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index 398d03f2..63937dc6 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -1,9 +1,10 @@ import type { MotionValue } from 'framer-motion' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState, useCallback } from 'react' import { createDots } from './utils/createDots' import { updateCanvasDimensions } from './utils/updateCanvasDimensions' import { drawDots } from './utils/drawDots' import { lerp } from './utils/lerp' +import { getContainerDimensions } from './utils/getContainerDimensions' export default function DotGrid({ containerRef, @@ -15,50 +16,54 @@ export default function DotGrid({ scrollYProgress?: MotionValue }) { const canvasRef = useRef(null) - const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) + const dimensions = getContainerDimensions(containerRef) const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) const [lerpedMousePosition, setLerpedMousePosition] = useState({ x: 0, y: 0 }) const dots = useMemo(() => createDots(dimensions, isMobile), [dimensions, isMobile]) - useEffect(() => { - const updateDimensions = () => { - const newDimensions = updateCanvasDimensions(canvasRef, containerRef) - setDimensions(newDimensions) - } + const updateDimensions = useCallback(() => { + updateCanvasDimensions(canvasRef, dimensions.width, dimensions.height) + }, [dimensions.width, dimensions.height]) - const handleMouseMove = (e: MouseEvent) => { + const handleMouseMove = useCallback( + (e: MouseEvent) => { if (canvasRef.current && containerRef.current) { const rect = containerRef.current.getBoundingClientRect() setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) } - } + }, + [containerRef], + ) - 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 updateMobileMousePosition = useCallback(() => { + if (scrollYProgress && isMobile && containerRef.current) { + const progress = scrollYProgress.get() + const { height } = containerRef.current.getBoundingClientRect() + setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) } + }, [scrollYProgress, isMobile, containerRef, dimensions.width]) - const lerpMousePosition = () => { - setLerpedMousePosition((prev) => ({ - x: lerp(prev.x, mousePosition.x), - y: lerp(prev.y, mousePosition.y), - })) - requestAnimationFrame(lerpMousePosition) - } + const lerpMousePosition = useCallback(() => { + setLerpedMousePosition((prev) => ({ + x: lerp(prev.x, mousePosition.x), + y: lerp(prev.y, mousePosition.y), + })) + return requestAnimationFrame(lerpMousePosition) + }, [mousePosition.x, mousePosition.y]) + useEffect(() => { updateDimensions() window.addEventListener('resize', updateDimensions) const container = containerRef.current + if (isMobile && scrollYProgress) { scrollYProgress.onChange(updateMobileMousePosition) } else { container?.addEventListener('mousemove', handleMouseMove) } - lerpMousePosition() + + const animationFrameId = lerpMousePosition() return () => { window.removeEventListener('resize', updateDimensions) @@ -67,8 +72,17 @@ export default function DotGrid({ } else { container?.removeEventListener('mousemove', handleMouseMove) } + cancelAnimationFrame(animationFrameId) } - }, [mousePosition.x, mousePosition.y, containerRef, isMobile, scrollYProgress, dimensions.width]) + }, [ + updateDimensions, + handleMouseMove, + updateMobileMousePosition, + lerpMousePosition, + containerRef, + isMobile, + scrollYProgress, + ]) useEffect(() => { const canvas = canvasRef.current @@ -80,7 +94,7 @@ export default function DotGrid({ let animationFrameId: number const animate = () => { - drawDots(ctx, dots, dimensions, lerpedMousePosition, isMobile) + drawDots(ctx, dots, dimensions, mousePosition, lerpedMousePosition, isMobile) animationFrameId = requestAnimationFrame(animate) } @@ -89,7 +103,7 @@ export default function DotGrid({ return () => { cancelAnimationFrame(animationFrameId) } - }, [dots, dimensions, lerpedMousePosition, isMobile]) + }, [dots, dimensions, mousePosition, lerpedMousePosition, isMobile]) return ( { @@ -17,6 +20,10 @@ export const drawDots = ( ctx.fillStyle = DOT_COLOR + // Update lerpedMousePosition + lerpedMousePosition.x = lerp(lerpedMousePosition.x, mousePosition.x) + lerpedMousePosition.y = lerp(lerpedMousePosition.y, mousePosition.y) + dots.forEach((dot) => { const dx = lerpedMousePosition.x - dot.x const dy = lerpedMousePosition.y - dot.y @@ -32,4 +39,6 @@ export const drawDots = ( ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) ctx.fill() }) + + return lerpedMousePosition } diff --git a/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts b/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts new file mode 100644 index 00000000..8fe90982 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts @@ -0,0 +1,9 @@ +export const getContainerDimensions = ( + containerRef: React.RefObject, +): { width: number; height: number } => { + if (containerRef.current) { + const { clientWidth, clientHeight } = containerRef.current + return { width: clientWidth, height: clientHeight } + } + return { width: 0, height: 0 } +} diff --git a/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts b/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts index ec34207e..cee5ab06 100644 --- a/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts +++ b/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts @@ -1,20 +1,18 @@ export const updateCanvasDimensions = ( canvasRef: React.RefObject, - containerRef: React.RefObject, -) => { - if (canvasRef.current && containerRef.current) { - const { clientWidth, clientHeight } = containerRef.current + width: number, + height: number, +): void => { + if (canvasRef.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` + canvasRef.current.width = width * pixelRatio + canvasRef.current.height = height * pixelRatio + canvasRef.current.style.width = `${width}px` + canvasRef.current.style.height = `${height}px` const ctx = canvasRef.current.getContext('2d') if (ctx) { ctx.scale(pixelRatio, pixelRatio) } - return { width: clientWidth, height: clientHeight } } - return { width: 0, height: 0 } } diff --git a/src/hooks/useLerpedMousePosition.ts b/src/hooks/useLerpedMousePosition.ts new file mode 100644 index 00000000..fd38de44 --- /dev/null +++ b/src/hooks/useLerpedMousePosition.ts @@ -0,0 +1,24 @@ +import { lerp } from '@/components/DataRoom/IndustryComparison/utils/lerp' +import { useState, useCallback, useEffect } from 'react' + +export const useLerpedMousePosition = (mousePosition: { x: number; y: number }) => { + const [lerpedMousePosition, setLerpedMousePosition] = useState({ x: 0, y: 0 }) + + const lerpMousePosition = useCallback(() => { + setLerpedMousePosition((prev) => ({ + x: lerp(prev.x, mousePosition.x), + y: lerp(prev.y, mousePosition.y), + })) + }, [mousePosition.x, mousePosition.y]) + + useEffect(() => { + const animationFrameId = requestAnimationFrame(function animate() { + lerpMousePosition() + requestAnimationFrame(animate) + }) + + return () => cancelAnimationFrame(animationFrameId) + }, [lerpMousePosition]) + + return lerpedMousePosition +} From 01d5a3b81b192928a2dfd2e13c8e479a781df37b Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 13 Aug 2024 14:17:44 +0530 Subject: [PATCH 04/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20address=20PR?= =?UTF-8?q?=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/DotGrid.tsx | 105 +++--------------- .../DataRoom/IndustryComparison/index.tsx | 2 +- .../IndustryComparison/styles.module.css | 17 ++- .../IndustryComparison/utils/createDots.ts | 8 ++ .../IndustryComparison/utils/drawDots.ts | 21 ++-- .../utils/getContainerDimensions.ts | 9 -- .../DataRoom/IndustryComparison/utils/lerp.ts | 5 - .../IndustryComparison/utils/updateCanvas.ts | 15 +++ .../utils/updateCanvasDimensions.ts | 18 --- .../utils/useMousePosition.ts | 37 ++++++ src/hooks/useContainerSize.ts | 23 ++++ src/hooks/useLerpedMousePosition.ts | 24 ---- src/lib/lerp.ts | 12 ++ 13 files changed, 138 insertions(+), 158 deletions(-) delete mode 100644 src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts delete mode 100644 src/components/DataRoom/IndustryComparison/utils/lerp.ts create mode 100644 src/components/DataRoom/IndustryComparison/utils/updateCanvas.ts delete mode 100644 src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts create mode 100644 src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts create mode 100644 src/hooks/useContainerSize.ts delete mode 100644 src/hooks/useLerpedMousePosition.ts create mode 100644 src/lib/lerp.ts diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index 63937dc6..d6688ff9 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -1,88 +1,24 @@ import type { MotionValue } from 'framer-motion' -import { useEffect, useMemo, useRef, useState, useCallback } from 'react' +import { useEffect, useRef } from 'react' import { createDots } from './utils/createDots' -import { updateCanvasDimensions } from './utils/updateCanvasDimensions' import { drawDots } from './utils/drawDots' -import { lerp } from './utils/lerp' -import { getContainerDimensions } from './utils/getContainerDimensions' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' +import css from './styles.module.css' +import useContainerSize from '@/hooks/useContainerSize' +import { updateCanvas } from './utils/updateCanvas' +import useMousePosition from './utils/useMousePosition' export default function DotGrid({ containerRef, - isMobile, scrollYProgress, }: { containerRef: React.RefObject - isMobile: boolean scrollYProgress?: MotionValue }) { const canvasRef = useRef(null) - const dimensions = getContainerDimensions(containerRef) - const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) - const [lerpedMousePosition, setLerpedMousePosition] = useState({ x: 0, y: 0 }) - - const dots = useMemo(() => createDots(dimensions, isMobile), [dimensions, isMobile]) - - const updateDimensions = useCallback(() => { - updateCanvasDimensions(canvasRef, dimensions.width, dimensions.height) - }, [dimensions.width, dimensions.height]) - - const handleMouseMove = useCallback( - (e: MouseEvent) => { - if (canvasRef.current && containerRef.current) { - const rect = containerRef.current.getBoundingClientRect() - setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) - } - }, - [containerRef], - ) - - const updateMobileMousePosition = useCallback(() => { - if (scrollYProgress && isMobile && containerRef.current) { - const progress = scrollYProgress.get() - const { height } = containerRef.current.getBoundingClientRect() - setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) - } - }, [scrollYProgress, isMobile, containerRef, dimensions.width]) - - const lerpMousePosition = useCallback(() => { - setLerpedMousePosition((prev) => ({ - x: lerp(prev.x, mousePosition.x), - y: lerp(prev.y, mousePosition.y), - })) - return requestAnimationFrame(lerpMousePosition) - }, [mousePosition.x, mousePosition.y]) - - useEffect(() => { - updateDimensions() - window.addEventListener('resize', updateDimensions) - const container = containerRef.current - - if (isMobile && scrollYProgress) { - scrollYProgress.onChange(updateMobileMousePosition) - } else { - container?.addEventListener('mousemove', handleMouseMove) - } - - const animationFrameId = lerpMousePosition() - - return () => { - window.removeEventListener('resize', updateDimensions) - if (isMobile && scrollYProgress) { - scrollYProgress.clearListeners() - } else { - container?.removeEventListener('mousemove', handleMouseMove) - } - cancelAnimationFrame(animationFrameId) - } - }, [ - updateDimensions, - handleMouseMove, - updateMobileMousePosition, - lerpMousePosition, - containerRef, - isMobile, - scrollYProgress, - ]) + const isMobile = useIsMediumScreen() + const dimensions = useContainerSize(containerRef) + const mousePosition = useMousePosition(containerRef, canvasRef, dimensions, scrollYProgress) useEffect(() => { const canvas = canvasRef.current @@ -91,10 +27,15 @@ export default function DotGrid({ const ctx = canvas.getContext('2d') if (!ctx) return + const dots = createDots(dimensions, isMobile) + updateCanvas(canvas, ctx, dimensions.width, dimensions.height) + let animationFrameId: number const animate = () => { - drawDots(ctx, dots, dimensions, mousePosition, lerpedMousePosition, isMobile) + if (dimensions.width > 0 && dimensions.height > 0) { + drawDots(ctx, dots, dimensions, mousePosition, isMobile) + } animationFrameId = requestAnimationFrame(animate) } @@ -103,19 +44,7 @@ export default function DotGrid({ return () => { cancelAnimationFrame(animationFrameId) } - }, [dots, dimensions, mousePosition, lerpedMousePosition, isMobile]) + }, [dimensions, mousePosition, isMobile]) - return ( - - ) + return } diff --git a/src/components/DataRoom/IndustryComparison/index.tsx b/src/components/DataRoom/IndustryComparison/index.tsx index c0f3f792..fcd1c7b0 100644 --- a/src/components/DataRoom/IndustryComparison/index.tsx +++ b/src/components/DataRoom/IndustryComparison/index.tsx @@ -25,7 +25,7 @@ const IndustryComparison = ({ title }: BaseBlock) => { {title} - + diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css index c5b746f2..7d950a28 100644 --- a/src/components/DataRoom/IndustryComparison/styles.module.css +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -17,7 +17,7 @@ position: absolute; right: 0; color: var(--mui-palette-text-dark); - overflow-x: hidden; + overflow: hidden; display: flex; align-items: flex-end; padding-top: 64px; @@ -34,9 +34,17 @@ gap: 30px; } +.canvasStyles { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + margin-top: 72px; +} + .title { - font-size: 80px; - line-height: 88px; z-index: 30; } @@ -59,6 +67,9 @@ padding-left: 32px; padding-right: 108px; } + .canvasStyles { + margin-top: 64px; + } .title { font-size: 56px; line-height: 60px; diff --git a/src/components/DataRoom/IndustryComparison/utils/createDots.ts b/src/components/DataRoom/IndustryComparison/utils/createDots.ts index b78cd7a8..3f6254bb 100644 --- a/src/components/DataRoom/IndustryComparison/utils/createDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/createDots.ts @@ -1,3 +1,10 @@ +/** + * Creates an array of dot coordinates based on given dimensions and device type. + * @param dimensions - The width and height of the container. + * @param isMobile - Boolean indicating if the device is mobile. + * @returns An array of objects containing x and y coordinates for each dot. + */ + const ROWS = 30 const MOBILE_COLS = 15 const DESKTOP_COLS = 60 @@ -12,5 +19,6 @@ export const createDots = (dimensions: { width: number; height: number }, isMobi newDots.push({ x, y }) } } + return newDots } diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts index 3d7a21bd..b400ef45 100644 --- a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -1,17 +1,21 @@ -import { lerp } from './lerp' +import { lerp } from '@/lib/lerp' + +type Position = { x: number; y: number } const MAX_SCALE_DISTANCE = 15 -const MAX_POSITION_DISTANCE = 100 const MOBILE_MAX_SCALE = 8 const DESKTOP_MAX_SCALE = 12 const DOT_COLOR = '#121312' +const LERP_FACTOR = 0.07 + +// Initialize lerpedMousePosition outside the function +let lerpedMousePosition: Position = { x: 0, y: 0 } export const drawDots = ( ctx: CanvasRenderingContext2D, - dots: { x: number; y: number }[], + dots: Position[], dimensions: { width: number; height: number }, - mousePosition: { x: number; y: number }, - lerpedMousePosition: { x: number; y: number }, + mousePosition: Position, isMobile: boolean, ) => { ctx.clearRect(0, 0, dimensions.width, dimensions.height) @@ -21,8 +25,8 @@ export const drawDots = ( ctx.fillStyle = DOT_COLOR // Update lerpedMousePosition - lerpedMousePosition.x = lerp(lerpedMousePosition.x, mousePosition.x) - lerpedMousePosition.y = lerp(lerpedMousePosition.y, mousePosition.y) + lerpedMousePosition.x = lerp(lerpedMousePosition.x, mousePosition.x, LERP_FACTOR) + lerpedMousePosition.y = lerp(lerpedMousePosition.y, mousePosition.y, LERP_FACTOR) dots.forEach((dot) => { const dx = lerpedMousePosition.x - dot.x @@ -32,9 +36,6 @@ export const drawDots = ( // Apply scale effect const scale = Math.max(1, maxScale - distance / MAX_SCALE_DISTANCE) - // Calculate force for translation - const force = Math.max(0, (MAX_POSITION_DISTANCE - distance) / MAX_POSITION_DISTANCE) - ctx.beginPath() ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) ctx.fill() diff --git a/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts b/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts deleted file mode 100644 index 8fe90982..00000000 --- a/src/components/DataRoom/IndustryComparison/utils/getContainerDimensions.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const getContainerDimensions = ( - containerRef: React.RefObject, -): { width: number; height: number } => { - if (containerRef.current) { - const { clientWidth, clientHeight } = containerRef.current - return { width: clientWidth, height: clientHeight } - } - return { width: 0, height: 0 } -} diff --git a/src/components/DataRoom/IndustryComparison/utils/lerp.ts b/src/components/DataRoom/IndustryComparison/utils/lerp.ts deleted file mode 100644 index 609026b1..00000000 --- a/src/components/DataRoom/IndustryComparison/utils/lerp.ts +++ /dev/null @@ -1,5 +0,0 @@ -const LERP_FACTOR = 0.1 - -export const lerp = (start: number, end: number) => { - return start + (end - start) * LERP_FACTOR -} diff --git a/src/components/DataRoom/IndustryComparison/utils/updateCanvas.ts b/src/components/DataRoom/IndustryComparison/utils/updateCanvas.ts new file mode 100644 index 00000000..38ac16b9 --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/updateCanvas.ts @@ -0,0 +1,15 @@ +/** + * Updates the canvas size and scale to account for device pixel ratio. + * @param canvas - The HTML canvas element to update + * @param ctx - The 2D rendering context of the canvas + * @param width - The desired width of the canvas in CSS pixels + * @param height - The desired height of the canvas in CSS pixels + */ +export function updateCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, width: number, height: number) { + const dpr = window.devicePixelRatio || 1 + canvas.width = width * dpr + canvas.height = height * dpr + canvas.style.width = `${width}px` + canvas.style.height = `${height}px` + ctx.scale(dpr, dpr) +} diff --git a/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts b/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts deleted file mode 100644 index cee5ab06..00000000 --- a/src/components/DataRoom/IndustryComparison/utils/updateCanvasDimensions.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const updateCanvasDimensions = ( - canvasRef: React.RefObject, - width: number, - height: number, -): void => { - if (canvasRef.current) { - const pixelRatio = window.devicePixelRatio || 1 - canvasRef.current.width = width * pixelRatio - canvasRef.current.height = height * pixelRatio - canvasRef.current.style.width = `${width}px` - canvasRef.current.style.height = `${height}px` - - const ctx = canvasRef.current.getContext('2d') - if (ctx) { - ctx.scale(pixelRatio, pixelRatio) - } - } -} diff --git a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts new file mode 100644 index 00000000..373d088d --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts @@ -0,0 +1,37 @@ +import { useState, useEffect } from 'react' +import type { MotionValue } from 'framer-motion' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' + +export default function useMousePosition( + containerRef: React.RefObject, + canvasRef: React.RefObject, + dimensions: { width: number; height: number }, + scrollYProgress?: MotionValue, +) { + const isMobile = useIsMediumScreen() + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) + + useEffect(() => { + const container = containerRef.current + + if (isMobile && scrollYProgress) { + const updatePositionMobile = () => { + const progress = scrollYProgress.get() + const height = container?.getBoundingClientRect().height || 0 + setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) + } + return scrollYProgress.on('change', updatePositionMobile) + } else { + const updatePositionDesktop = (e: MouseEvent) => { + const rect = canvasRef.current?.getBoundingClientRect() + if (rect) { + setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) + } + } + container?.addEventListener('mousemove', updatePositionDesktop) + return () => container?.removeEventListener('mousemove', updatePositionDesktop) + } + }, [containerRef, canvasRef, isMobile, scrollYProgress, dimensions.width]) + + return mousePosition +} diff --git a/src/hooks/useContainerSize.ts b/src/hooks/useContainerSize.ts new file mode 100644 index 00000000..c7efe298 --- /dev/null +++ b/src/hooks/useContainerSize.ts @@ -0,0 +1,23 @@ +import { useState, useEffect, type RefObject } from 'react' + +function useContainerSize(ref: RefObject): { width: number; height: number } { + const [size, setSize] = useState({ width: 0, height: 0 }) + + useEffect(() => { + if (!ref.current) return + + const updateSize = (entries: ResizeObserverEntry[]) => { + const { width, height } = entries[0].contentRect + setSize({ width, height }) + } + + const resizeObserver = new ResizeObserver(updateSize) + resizeObserver.observe(ref.current) + + return () => resizeObserver.disconnect() + }, [ref]) + + return size +} + +export default useContainerSize diff --git a/src/hooks/useLerpedMousePosition.ts b/src/hooks/useLerpedMousePosition.ts deleted file mode 100644 index fd38de44..00000000 --- a/src/hooks/useLerpedMousePosition.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { lerp } from '@/components/DataRoom/IndustryComparison/utils/lerp' -import { useState, useCallback, useEffect } from 'react' - -export const useLerpedMousePosition = (mousePosition: { x: number; y: number }) => { - const [lerpedMousePosition, setLerpedMousePosition] = useState({ x: 0, y: 0 }) - - const lerpMousePosition = useCallback(() => { - setLerpedMousePosition((prev) => ({ - x: lerp(prev.x, mousePosition.x), - y: lerp(prev.y, mousePosition.y), - })) - }, [mousePosition.x, mousePosition.y]) - - useEffect(() => { - const animationFrameId = requestAnimationFrame(function animate() { - lerpMousePosition() - requestAnimationFrame(animate) - }) - - return () => cancelAnimationFrame(animationFrameId) - }, [lerpMousePosition]) - - return lerpedMousePosition -} diff --git a/src/lib/lerp.ts b/src/lib/lerp.ts new file mode 100644 index 00000000..cba09621 --- /dev/null +++ b/src/lib/lerp.ts @@ -0,0 +1,12 @@ +/** + * Performs linear interpolation between two numbers. + * + * @param {number} start - The starting value. + * @param {number} end - The ending value. + * @param {number} factor - The interpolation factor (0-1). + * @returns {number} The interpolated value. + */ + +export const lerp = (start: number, end: number, factor: number) => { + return start + (end - start) * factor +} From ccf8dfe8551dc33614d1cc8604f34cf0627a240e Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 13 Aug 2024 14:29:20 +0530 Subject: [PATCH 05/13] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20add=20descri?= =?UTF-8?q?pition=20for=20useMousePosition=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IndustryComparison/utils/useMousePosition.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts index 373d088d..a1db7286 100644 --- a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts +++ b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts @@ -2,6 +2,17 @@ import { useState, useEffect } from 'react' import type { MotionValue } from 'framer-motion' import { useIsMediumScreen } from '@/hooks/useMaxWidth' +/** + * Custom hook to track mouse position or simulate it based on scroll progress. + * @param containerRef - Reference to the container element. + * @param canvasRef - Reference to the canvas element. + * @param dimensions - Object containing width and height of the container. + * @param scrollYProgress - MotionValue for scroll progress, used on mobile devices. + * @returns An object with x and y coordinates representing either: + * - Actual mouse position relative to the canvas (on desktop) + * - Simulated position based on scroll progress (on mobile) + */ + export default function useMousePosition( containerRef: React.RefObject, canvasRef: React.RefObject, From fffb8abf9ee13e3c765c484887f3174e190bed9c Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 13 Aug 2024 14:42:30 +0530 Subject: [PATCH 06/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20remove=20conta?= =?UTF-8?q?iner=20ref=20from=20useMousePosition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/DotGrid.tsx | 2 +- .../IndustryComparison/styles.module.css | 1 - .../IndustryComparison/utils/useMousePosition.ts | 16 ++++++---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index d6688ff9..03849bd6 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -18,7 +18,7 @@ export default function DotGrid({ const canvasRef = useRef(null) const isMobile = useIsMediumScreen() const dimensions = useContainerSize(containerRef) - const mousePosition = useMousePosition(containerRef, canvasRef, dimensions, scrollYProgress) + const mousePosition = useMousePosition(canvasRef, dimensions, scrollYProgress) useEffect(() => { const canvas = canvasRef.current diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css index 7d950a28..d4f47b13 100644 --- a/src/components/DataRoom/IndustryComparison/styles.module.css +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -40,7 +40,6 @@ left: 0; width: 100%; height: 100%; - pointer-events: none; margin-top: 72px; } diff --git a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts index a1db7286..b0a59ef8 100644 --- a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts +++ b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts @@ -4,7 +4,6 @@ import { useIsMediumScreen } from '@/hooks/useMaxWidth' /** * Custom hook to track mouse position or simulate it based on scroll progress. - * @param containerRef - Reference to the container element. * @param canvasRef - Reference to the canvas element. * @param dimensions - Object containing width and height of the container. * @param scrollYProgress - MotionValue for scroll progress, used on mobile devices. @@ -14,7 +13,6 @@ import { useIsMediumScreen } from '@/hooks/useMaxWidth' */ export default function useMousePosition( - containerRef: React.RefObject, canvasRef: React.RefObject, dimensions: { width: number; height: number }, scrollYProgress?: MotionValue, @@ -23,26 +21,24 @@ export default function useMousePosition( const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) useEffect(() => { - const container = containerRef.current - if (isMobile && scrollYProgress) { const updatePositionMobile = () => { const progress = scrollYProgress.get() - const height = container?.getBoundingClientRect().height || 0 - setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * height }) + setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * dimensions.height }) } return scrollYProgress.on('change', updatePositionMobile) } else { + const canvas = canvasRef.current const updatePositionDesktop = (e: MouseEvent) => { - const rect = canvasRef.current?.getBoundingClientRect() + const rect = canvas?.getBoundingClientRect() if (rect) { setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) } } - container?.addEventListener('mousemove', updatePositionDesktop) - return () => container?.removeEventListener('mousemove', updatePositionDesktop) + canvas?.addEventListener('mousemove', updatePositionDesktop) + return () => canvas?.removeEventListener('mousemove', updatePositionDesktop) } - }, [containerRef, canvasRef, isMobile, scrollYProgress, dimensions.width]) + }, [canvasRef, isMobile, scrollYProgress, dimensions]) return mousePosition } From 74afb38ab7169ad06fada1844e68942f9bccf197 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Thu, 15 Aug 2024 17:54:18 +0530 Subject: [PATCH 07/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20add=20=20Slidi?= =?UTF-8?q?ngPanel=20component=20and=20update=20Cryptopunks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/CryptoPunks/Content.tsx | 58 ++++++++ .../DataRoom/CryptoPunks/PunksGrid.tsx | 53 +++++++ src/components/DataRoom/CryptoPunks/index.tsx | 134 ++---------------- .../DataRoom/CryptoPunks/styles.module.css | 44 +----- .../DataRoom/IndustryComparison/index.tsx | 67 +++------ .../IndustryComparison/styles.module.css | 36 +---- src/components/common/SlidingPanel/index.tsx | 39 +++++ .../common/SlidingPanel/styles.module.css | 31 ++++ 8 files changed, 229 insertions(+), 233 deletions(-) create mode 100644 src/components/DataRoom/CryptoPunks/Content.tsx create mode 100644 src/components/DataRoom/CryptoPunks/PunksGrid.tsx create mode 100644 src/components/common/SlidingPanel/index.tsx create mode 100644 src/components/common/SlidingPanel/styles.module.css diff --git a/src/components/DataRoom/CryptoPunks/Content.tsx b/src/components/DataRoom/CryptoPunks/Content.tsx new file mode 100644 index 00000000..033ccda1 --- /dev/null +++ b/src/components/DataRoom/CryptoPunks/Content.tsx @@ -0,0 +1,58 @@ +import { Typography } from '@mui/material' +import type { BaseBlock } from '@/components/Home/types' +import type { MotionValue } from 'framer-motion' +import { motion, useTransform } from 'framer-motion' +import dynamic from 'next/dynamic' +import LinksWrapper from '../LinksWrapper' +import css from './styles.module.css' +import { useSafeDataRoomStats } from '@/hooks/useSafeDataRoomStats' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' + +const PunksGrid = dynamic(() => import('./PunksGrid')) +const SlidingPanel = dynamic(() => import('@/components/common/SlidingPanel')) + +const CRYPTOPUNKS_TOTAL = 10000 +const CRYPTOPUNKS_PERCENTAGE_STORED_FALLBACK = 0.092 + +const Content = ({ scrollYProgress, title, text, link }: BaseBlock & { scrollYProgress: MotionValue }) => { + const { cryptoPunksStoredPercentage } = useSafeDataRoomStats() + const isMobile = useIsMediumScreen() + + const opacityParams = isMobile ? [0.4, 0.45, 0.65, 0.66] : [0.25, 0.35, 0.65, 0.7] + const opacity = useTransform(scrollYProgress, opacityParams, [0, 1, 1, 0]) + + const percentageValue = cryptoPunksStoredPercentage || CRYPTOPUNKS_PERCENTAGE_STORED_FALLBACK + // Converts to percentage with 1 decimal place + const percentageToDisplay = (percentageValue * 100).toFixed(1) + '%' + + const cryptoPunksStored = (percentageValue * CRYPTOPUNKS_TOTAL).toFixed(0) + const fractionToDisplay = `${cryptoPunksStored}/${CRYPTOPUNKS_TOTAL}` + + return ( + + + {text} + + + {title} + +
+ + {percentageToDisplay} + + + {fractionToDisplay} + +
+ + {link && } +
+ ) +} + +export default Content diff --git a/src/components/DataRoom/CryptoPunks/PunksGrid.tsx b/src/components/DataRoom/CryptoPunks/PunksGrid.tsx new file mode 100644 index 00000000..f3340dea --- /dev/null +++ b/src/components/DataRoom/CryptoPunks/PunksGrid.tsx @@ -0,0 +1,53 @@ +import { getRandomColor } from '@/components/DataRoom/CryptoPunks/utils/getRandomColor' +import CryptoPunk from '@/public/images/DataRoom/cryptopunk-silhouette.svg' +import type { MotionValue } from 'framer-motion' +import { motion, useTransform } from 'framer-motion' +import css from './styles.module.css' + +const CRYPTOPUNK_ROWS_NR = 8 +const CRYPTOPUNK_COLUMNS_NR = 24 + +const PunksGrid = ({ scrollYProgress, isMobile }: { scrollYProgress: MotionValue; isMobile: boolean }) => { + const translateParams = isMobile ? [0, 1] : [0.25, 0.75] + const opacity = useTransform(scrollYProgress, [0, 0.25, 0.7, 0.75], [0, 1, 1, 0]) + const translateLTR = useTransform(scrollYProgress, translateParams, ['-50%', '0%']) + const translateRTL = useTransform(scrollYProgress, translateParams, ['0%', '-50%']) + + const translateDirection = (index: number) => (index % 2 === 1 ? translateLTR : translateRTL) + + return ( + + {Array.from({ length: CRYPTOPUNK_ROWS_NR }).map((_, outerIndex) => ( + + {Array.from({ length: CRYPTOPUNK_COLUMNS_NR }).map((_, innerIndex) => ( + + + + ))} + + ))} + + ) +} + +export default PunksGrid diff --git a/src/components/DataRoom/CryptoPunks/index.tsx b/src/components/DataRoom/CryptoPunks/index.tsx index 87ca400d..974265c6 100644 --- a/src/components/DataRoom/CryptoPunks/index.tsx +++ b/src/components/DataRoom/CryptoPunks/index.tsx @@ -1,25 +1,15 @@ -import { Typography } from '@mui/material' import type { BaseBlock } from '@/components/Home/types' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' +import { useScroll } from 'framer-motion' +import dynamic from 'next/dynamic' import { useRef } from 'react' -import { useScroll, motion, useTransform } from 'framer-motion' -import type { ReactNode } from 'react' -import type { MotionValue } from 'framer-motion' import css from './styles.module.css' -import LinksWrapper from '../LinksWrapper' -import { getRandomColor } from '@/components/DataRoom/CryptoPunks/utils/getRandomColor' -import CryptoPunk from '@/public/images/DataRoom/cryptopunk-silhouette.svg' -import { useIsMediumScreen } from '@/hooks/useMaxWidth' -import { useSafeDataRoomStats } from '@/hooks/useSafeDataRoomStats' - -const CRYPTOPUNKS_TOTAL = 10000 -const CRYPTOPUNKS_PERCENTAGE_STORED_FALLBACK = 0.092 -const CRYPTOPUNK_ROWS_NR = 8 -const CRYPTOPUNK_COLUMNS_NR = 24 +const PunksGrid = dynamic(() => import('./PunksGrid')) +const Content = dynamic(() => import('./Content')) +const SlidingPanel = dynamic(() => import('@/components/common/SlidingPanel')) const CryptoPunks = ({ title, text, link }: BaseBlock) => { - const { cryptoPunksStoredPercentage } = useSafeDataRoomStats() - const backgroundRef = useRef(null) const isMobile = useIsMediumScreen() @@ -28,113 +18,19 @@ const CryptoPunks = ({ title, text, link }: BaseBlock) => { offset: ['start end', 'end start'], }) - const percentageValue = cryptoPunksStoredPercentage || CRYPTOPUNKS_PERCENTAGE_STORED_FALLBACK - // Converts to percentage with 1 decimal place - const percentageToDisplay = (percentageValue * 100).toFixed(1) + '%' - - const cryptoPunksStored = (percentageValue * CRYPTOPUNKS_TOTAL).toFixed(0) - const fractionToDisplay = `${cryptoPunksStored}/${CRYPTOPUNKS_TOTAL}` - return (
- - - - {text} - - - {title} - -
- - {percentageToDisplay} - - - {fractionToDisplay} - -
- - {link && } -
-
-
- ) -} - -const LeftPanel = ({ scrollYProgress, isMobile }: { scrollYProgress: MotionValue; isMobile: boolean }) => { - const translateParams = isMobile ? [0, 1] : [0.25, 0.75] - const opacity = useTransform(scrollYProgress, [0, 0.25, 0.7, 0.75], [0, 1, 1, 0]) - const translateLTR = useTransform(scrollYProgress, translateParams, ['-50%', '0%']) - const translateRTL = useTransform(scrollYProgress, translateParams, ['0%', '-50%']) - - const translateDirection = (index: number) => (index % 2 === 1 ? translateLTR : translateRTL) - - return ( - - {Array.from({ length: CRYPTOPUNK_ROWS_NR }).map((_, outerIndex) => ( - + - {Array.from({ length: CRYPTOPUNK_COLUMNS_NR }).map((_, innerIndex) => ( - - - - ))} - - ))} - - ) -} - -const RightPanel = ({ - scrollYProgress, - children, - isMobile, -}: { - scrollYProgress: MotionValue - children: ReactNode - isMobile: boolean -}) => { - const opacityParams = isMobile ? [0.4, 0.45, 0.65, 0.66] : [0.25, 0.35, 0.65, 0.7] - const translateParams = isMobile ? [0.4, 0.45, 0.65, 0.7] : [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 ( -
- - {children} - - + + +
) } diff --git a/src/components/DataRoom/CryptoPunks/styles.module.css b/src/components/DataRoom/CryptoPunks/styles.module.css index d8fc804a..61dbb322 100644 --- a/src/components/DataRoom/CryptoPunks/styles.module.css +++ b/src/components/DataRoom/CryptoPunks/styles.module.css @@ -11,19 +11,7 @@ display: flex; } -.rightPanelContainer { - width: 50%; - height: 100%; - position: absolute; - right: 0; - color: var(--mui-palette-text-dark); - overflow-x: hidden; - display: flex; - align-items: flex-end; - padding-top: 64px; -} - -.rightPanelContent { +.slidingPanelContent { z-index: 20; display: flex; flex-direction: column; @@ -33,14 +21,6 @@ gap: 30px; } -.rightPanelBG { - position: absolute; - inset: 0; - background-color: var(--mui-palette-primary-main); - z-index: 10; - margin-top: 72px; -} - .percentage { font-size: 120px; line-height: 120px; @@ -65,7 +45,7 @@ align-items: baseline; } -.leftPanelContainer { +.punksGrid { width: 100%; height: 100%; overflow: hidden; @@ -94,39 +74,25 @@ flex-direction: column-reverse; } - .leftPanelContainer { + .punksGrid { padding-top: 72px; width: 100%; height: 100%; + overflow: hidden; } - .rightPanelContainer { - width: 100%; - height: 100%; - position: absolute; - bottom: 0; - } - - .rightPanelContent { + .slidingPanelContent { justify-content: end; padding: 30px; gap: 30px; } - .rightPanelBG { - margin-top: 64px; - } - .statsContainer { display: flex; flex-direction: column; gap: 30px; } - .leftPanelContainer { - overflow: hidden; - } - .cryptopunk { width: 60px; } diff --git a/src/components/DataRoom/IndustryComparison/index.tsx b/src/components/DataRoom/IndustryComparison/index.tsx index fcd1c7b0..1eebcb83 100644 --- a/src/components/DataRoom/IndustryComparison/index.tsx +++ b/src/components/DataRoom/IndustryComparison/index.tsx @@ -1,13 +1,14 @@ import type { BaseBlock } from '@/components/Home/types' -import type { MotionValue } from 'framer-motion' import { useTransform, motion, useScroll } from 'framer-motion' -import type { ReactNode } from 'react' +import dynamic from 'next/dynamic' import React, { useRef } from 'react' import css from './styles.module.css' import { useIsMediumScreen } from '@/hooks/useMaxWidth' import { Typography } from '@mui/material' import DotGrid from './DotGrid' +const SlidingPanel = dynamic(() => import('@/components/common/SlidingPanel')) + const IndustryComparison = ({ title }: BaseBlock) => { const backgroundRef = useRef(null) const gridContainerRef = useRef(null) @@ -18,52 +19,30 @@ const IndustryComparison = ({ title }: BaseBlock) => { offset: ['start end', 'end start'], }) - return ( -
-
- - - {title} - - - -
-
- ) -} - -const RightPanel = ({ - scrollYProgress, - children, - containerRef, - isMobile, -}: { - scrollYProgress: MotionValue - children: ReactNode - isMobile: boolean - containerRef: React.RefObject -}) => { 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 ( -
- - {children} - - +
+
+ + + + {title} + + + + +
) } diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css index d4f47b13..aa49ed07 100644 --- a/src/components/DataRoom/IndustryComparison/styles.module.css +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -11,19 +11,7 @@ display: flex; } -.rightPanelContainer { - width: 100%; - height: 100%; - position: absolute; - right: 0; - color: var(--mui-palette-text-dark); - overflow: hidden; - display: flex; - align-items: flex-end; - padding-top: 64px; -} - -.rightPanelContent { +.slidingPanelContent { z-index: 20; display: flex; justify-items: center; @@ -45,35 +33,21 @@ .title { z-index: 30; -} - -.rightPanelBG { - position: absolute; - inset: 0; - background-color: var(--mui-palette-primary-main); - z-index: 10; - margin-top: 72px; + pointer-events: none; } @media (max-width: 900px) { - .rightPanelContainer { - width: 100%; - height: 100%; - position: absolute; - bottom: 0; - } - .rightPanelContent { + .slidingPanelContent { padding-left: 32px; padding-right: 108px; } + .canvasStyles { margin-top: 64px; } + .title { font-size: 56px; line-height: 60px; } - .rightPanelBG { - margin-top: 0px; - } } diff --git a/src/components/common/SlidingPanel/index.tsx b/src/components/common/SlidingPanel/index.tsx new file mode 100644 index 00000000..c05b4d7f --- /dev/null +++ b/src/components/common/SlidingPanel/index.tsx @@ -0,0 +1,39 @@ +import type { MotionValue } from 'framer-motion' +import { motion, useTransform } from 'framer-motion' +import type { ReactNode } from 'react' +import css from './styles.module.css' + +const SlidingPanel = ({ + scrollYProgress, + children, + scrollParams, + translateParams, + panelWidth = '100%', +}: { + scrollYProgress: MotionValue + children: ReactNode + scrollParams: number[] + translateParams: string[] + panelWidth?: string +}) => { + const bgTranslate = useTransform(scrollYProgress, scrollParams, translateParams) + + return ( +
+ {children} + +
+ ) +} + +export default SlidingPanel diff --git a/src/components/common/SlidingPanel/styles.module.css b/src/components/common/SlidingPanel/styles.module.css new file mode 100644 index 00000000..e5131c6d --- /dev/null +++ b/src/components/common/SlidingPanel/styles.module.css @@ -0,0 +1,31 @@ +.slidingPanelContainer { + height: 100%; + position: absolute; + right: 0; + color: var(--mui-palette-text-dark); + overflow: hidden; + display: flex; + align-items: flex-end; + padding-top: 64px; +} + +.slidingPanelBG { + position: absolute; + inset: 0; + background-color: var(--mui-palette-primary-main); + z-index: 10; + margin-top: 72px; +} + +@media (max-width: 900px) { + .slidingPanelContainer { + width: 100%; + height: 100%; + position: absolute; + bottom: 0; + } + + .slidingPanelBG { + margin-top: 0px; + } +} From a8a5d94e01a19f003f2874b7fbcef06523b14a70 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Thu, 15 Aug 2024 18:05:51 +0530 Subject: [PATCH 08/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20dynamic=20impo?= =?UTF-8?q?rt=20for=20framer=20motion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/Content.tsx | 38 +++++++++++++++++++ .../DataRoom/IndustryComparison/index.tsx | 20 ++-------- 2 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 src/components/DataRoom/IndustryComparison/Content.tsx diff --git a/src/components/DataRoom/IndustryComparison/Content.tsx b/src/components/DataRoom/IndustryComparison/Content.tsx new file mode 100644 index 00000000..cef8161d --- /dev/null +++ b/src/components/DataRoom/IndustryComparison/Content.tsx @@ -0,0 +1,38 @@ +import type { BaseBlock } from '@/components/Home/types' +import { Typography } from '@mui/material' +import type { MotionValue } from 'framer-motion' +import { motion, useTransform } from 'framer-motion' +import DotGrid from './DotGrid' +import css from './styles.module.css' +import { useIsMediumScreen } from '@/hooks/useMaxWidth' +import type { RefObject } from 'react' + +const Content = ({ + scrollYProgress, + title, + containerRef, +}: { + title: BaseBlock['title'] + scrollYProgress: MotionValue + containerRef: RefObject +}) => { + const isMobile = useIsMediumScreen() + + const opacityParams = [0.25, 0.35, 0.65, 0.75] + const opacity = useTransform(scrollYProgress, opacityParams, [0, 1, 1, 0]) + return ( + + + {title} + + + + ) +} + +export default Content diff --git a/src/components/DataRoom/IndustryComparison/index.tsx b/src/components/DataRoom/IndustryComparison/index.tsx index 1eebcb83..625b014f 100644 --- a/src/components/DataRoom/IndustryComparison/index.tsx +++ b/src/components/DataRoom/IndustryComparison/index.tsx @@ -1,12 +1,11 @@ import type { BaseBlock } from '@/components/Home/types' -import { useTransform, motion, useScroll } from 'framer-motion' +import { useScroll } from 'framer-motion' import dynamic from 'next/dynamic' import React, { useRef } from 'react' import css from './styles.module.css' import { useIsMediumScreen } from '@/hooks/useMaxWidth' -import { Typography } from '@mui/material' -import DotGrid from './DotGrid' +const Content = dynamic(() => import('./Content')) const SlidingPanel = dynamic(() => import('@/components/common/SlidingPanel')) const IndustryComparison = ({ title }: BaseBlock) => { @@ -19,9 +18,6 @@ const IndustryComparison = ({ title }: BaseBlock) => { offset: ['start end', 'end start'], }) - const opacityParams = [0.25, 0.35, 0.65, 0.75] - const opacity = useTransform(scrollYProgress, opacityParams, [0, 1, 1, 0]) - return (
@@ -30,17 +26,7 @@ const IndustryComparison = ({ title }: BaseBlock) => { translateParams={isMobile ? ['0%', '0%'] : ['100%', '0%', '0%', '100%']} scrollYProgress={scrollYProgress} > - - - {title} - - - +
From 9f82803813cd4d8eb61e15075107da6c580c3864 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Thu, 15 Aug 2024 21:56:47 +0530 Subject: [PATCH 09/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20address=20PR?= =?UTF-8?q?=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/Content.tsx | 6 ++- .../DataRoom/IndustryComparison/DotGrid.tsx | 48 +++++++++++-------- .../IndustryComparison/styles.module.css | 5 -- .../IndustryComparison/utils/createDots.ts | 9 ++-- .../IndustryComparison/utils/drawDots.ts | 47 ++++++++++-------- .../utils/useMousePosition.ts | 1 - src/components/common/SlidingPanel/index.tsx | 2 +- src/lib/lerp.ts | 12 ----- 8 files changed, 65 insertions(+), 65 deletions(-) delete mode 100644 src/lib/lerp.ts diff --git a/src/components/DataRoom/IndustryComparison/Content.tsx b/src/components/DataRoom/IndustryComparison/Content.tsx index cef8161d..499ae0dd 100644 --- a/src/components/DataRoom/IndustryComparison/Content.tsx +++ b/src/components/DataRoom/IndustryComparison/Content.tsx @@ -18,8 +18,10 @@ const Content = ({ }) => { const isMobile = useIsMediumScreen() - const opacityParams = [0.25, 0.35, 0.65, 0.75] - const opacity = useTransform(scrollYProgress, opacityParams, [0, 1, 1, 0]) + const scrollParams = [0.25, 0.35, 0.65, 0.75] + const opacityParams = [0, 1, 1, 0] + const opacity = useTransform(scrollYProgress, scrollParams, opacityParams) + return ( { - const canvas = canvasRef.current - if (!canvas) return - - const ctx = canvas.getContext('2d') - if (!ctx) return + const prevRenderStateRef = useRef({ dimensions, mousePosition, isMobile }) + const dotsRef = useRef | null>(null) + const animationFrameId = useRef() - const dots = createDots(dimensions, isMobile) - updateCanvas(canvas, ctx, dimensions.width, dimensions.height) + const renderFrame = useCallback(() => { + const canvas = canvasRef.current + const ctx = canvas?.getContext('2d') + if (!canvas || !ctx || dimensions.width <= 0 || dimensions.height <= 0) return + + const currentRenderState = { dimensions, mousePosition, isMobile } + const prevRenderState = prevRenderStateRef.current + + if ( + !dotsRef.current || + currentRenderState.dimensions !== prevRenderState.dimensions || + currentRenderState.isMobile !== prevRenderState.isMobile + ) { + dotsRef.current = createDots(dimensions, isMobile) + updateCanvas(canvas, ctx, dimensions.width, dimensions.height) + } - let animationFrameId: number + const isAnimationComplete = drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) + prevRenderStateRef.current = currentRenderState - const animate = () => { - if (dimensions.width > 0 && dimensions.height > 0) { - drawDots(ctx, dots, dimensions, mousePosition, isMobile) - } - animationFrameId = requestAnimationFrame(animate) + if (!isAnimationComplete) { + animationFrameId.current = requestAnimationFrame(renderFrame) } + }, [dimensions, mousePosition, isMobile]) - animate() - + useEffect(() => { + renderFrame() return () => { - cancelAnimationFrame(animationFrameId) + if (animationFrameId.current) cancelAnimationFrame(animationFrameId.current) } - }, [dimensions, mousePosition, isMobile]) + }, [renderFrame]) return } diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css index aa49ed07..907f5e47 100644 --- a/src/components/DataRoom/IndustryComparison/styles.module.css +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -45,9 +45,4 @@ .canvasStyles { margin-top: 64px; } - - .title { - font-size: 56px; - line-height: 60px; - } } diff --git a/src/components/DataRoom/IndustryComparison/utils/createDots.ts b/src/components/DataRoom/IndustryComparison/utils/createDots.ts index 3f6254bb..70facd02 100644 --- a/src/components/DataRoom/IndustryComparison/utils/createDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/createDots.ts @@ -1,14 +1,13 @@ +const ROWS = 30 +const MOBILE_COLS = 15 +const DESKTOP_COLS = 60 + /** * Creates an array of dot coordinates based on given dimensions and device type. * @param dimensions - The width and height of the container. * @param isMobile - Boolean indicating if the device is mobile. * @returns An array of objects containing x and y coordinates for each dot. */ - -const ROWS = 30 -const MOBILE_COLS = 15 -const DESKTOP_COLS = 60 - export const createDots = (dimensions: { width: number; height: number }, isMobile: boolean) => { const cols = isMobile ? MOBILE_COLS : DESKTOP_COLS const newDots = [] diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts index b400ef45..5dccf30e 100644 --- a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -1,15 +1,11 @@ -import { lerp } from '@/lib/lerp' - type Position = { x: number; y: number } const MAX_SCALE_DISTANCE = 15 const MOBILE_MAX_SCALE = 8 const DESKTOP_MAX_SCALE = 12 const DOT_COLOR = '#121312' -const LERP_FACTOR = 0.07 -// Initialize lerpedMousePosition outside the function -let lerpedMousePosition: Position = { x: 0, y: 0 } +let lastUpdatedArea: { x: number; y: number; radius: number } | null = null export const drawDots = ( ctx: CanvasRenderingContext2D, @@ -18,28 +14,39 @@ export const drawDots = ( mousePosition: Position, isMobile: boolean, ) => { - ctx.clearRect(0, 0, dimensions.width, dimensions.height) - const maxScale = isMobile ? MOBILE_MAX_SCALE : DESKTOP_MAX_SCALE + const updatedRadius = MAX_SCALE_DISTANCE * maxScale + + // Clear only the previously updated area and the new updated area + if (lastUpdatedArea) { + ctx.clearRect( + lastUpdatedArea.x - lastUpdatedArea.radius, + lastUpdatedArea.y - lastUpdatedArea.radius, + lastUpdatedArea.radius * 2, + lastUpdatedArea.radius * 2, + ) + } + ctx.clearRect(mousePosition.x - updatedRadius, mousePosition.y - updatedRadius, updatedRadius * 2, updatedRadius * 2) ctx.fillStyle = DOT_COLOR - // Update lerpedMousePosition - lerpedMousePosition.x = lerp(lerpedMousePosition.x, mousePosition.x, LERP_FACTOR) - lerpedMousePosition.y = lerp(lerpedMousePosition.y, mousePosition.y, LERP_FACTOR) - dots.forEach((dot) => { - const dx = lerpedMousePosition.x - dot.x - const dy = lerpedMousePosition.y - dot.y + const dx = mousePosition.x - dot.x + const dy = mousePosition.y - dot.y const distance = Math.sqrt(dx * dx + dy * dy) - // Apply scale effect - const scale = Math.max(1, maxScale - distance / MAX_SCALE_DISTANCE) - - ctx.beginPath() - ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) - ctx.fill() + if (distance <= updatedRadius) { + const scale = Math.max(1, maxScale - distance / MAX_SCALE_DISTANCE) + ctx.beginPath() + ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) + ctx.fill() + } else { + // Draw unupdated dots with normal size + ctx.beginPath() + ctx.arc(dot.x, dot.y, 1, 0, 2 * Math.PI) + ctx.fill() + } }) - return lerpedMousePosition + lastUpdatedArea = { x: mousePosition.x, y: mousePosition.y, radius: updatedRadius } } diff --git a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts index b0a59ef8..5ce684bc 100644 --- a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts +++ b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts @@ -11,7 +11,6 @@ import { useIsMediumScreen } from '@/hooks/useMaxWidth' * - Actual mouse position relative to the canvas (on desktop) * - Simulated position based on scroll progress (on mobile) */ - export default function useMousePosition( canvasRef: React.RefObject, dimensions: { width: number; height: number }, diff --git a/src/components/common/SlidingPanel/index.tsx b/src/components/common/SlidingPanel/index.tsx index c05b4d7f..ba5ff04b 100644 --- a/src/components/common/SlidingPanel/index.tsx +++ b/src/components/common/SlidingPanel/index.tsx @@ -31,7 +31,7 @@ const SlidingPanel = ({ style={{ translateX: bgTranslate, }} - > + />
) } diff --git a/src/lib/lerp.ts b/src/lib/lerp.ts deleted file mode 100644 index cba09621..00000000 --- a/src/lib/lerp.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Performs linear interpolation between two numbers. - * - * @param {number} start - The starting value. - * @param {number} end - The ending value. - * @param {number} factor - The interpolation factor (0-1). - * @returns {number} The interpolated value. - */ - -export const lerp = (start: number, end: number, factor: number) => { - return start + (end - start) * factor -} From 27b64cae7bdf12ccb7a7bd8253dc8594d1385372 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Mon, 19 Aug 2024 21:04:12 +0530 Subject: [PATCH 10/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20conditionally?= =?UTF-8?q?=20call=20drawDots=20to=20improve=20performance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DataRoom/IndustryComparison/DotGrid.tsx | 10 ++++++---- .../DataRoom/IndustryComparison/utils/drawDots.ts | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index ba3058d7..9110ae28 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -39,14 +39,16 @@ export default function DotGrid({ ) { dotsRef.current = createDots(dimensions, isMobile) updateCanvas(canvas, ctx, dimensions.width, dimensions.height) + // Draw dots immediately after creating or updating them + // This draw call ensure dots are already visible when canvas scrolls into view + drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) + } else if (currentRenderState.mousePosition !== prevRenderState.mousePosition) { + drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) } - const isAnimationComplete = drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) prevRenderStateRef.current = currentRenderState - if (!isAnimationComplete) { - animationFrameId.current = requestAnimationFrame(renderFrame) - } + animationFrameId.current = requestAnimationFrame(renderFrame) }, [dimensions, mousePosition, isMobile]) useEffect(() => { diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts index 5dccf30e..5fbcc6eb 100644 --- a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -17,7 +17,7 @@ export const drawDots = ( const maxScale = isMobile ? MOBILE_MAX_SCALE : DESKTOP_MAX_SCALE const updatedRadius = MAX_SCALE_DISTANCE * maxScale - // Clear only the previously updated area and the new updated area + // Clear the previously updated area if (lastUpdatedArea) { ctx.clearRect( lastUpdatedArea.x - lastUpdatedArea.radius, @@ -26,6 +26,7 @@ export const drawDots = ( lastUpdatedArea.radius * 2, ) } + // Clear the new (to be updated) area ctx.clearRect(mousePosition.x - updatedRadius, mousePosition.y - updatedRadius, updatedRadius * 2, updatedRadius * 2) ctx.fillStyle = DOT_COLOR From 3daff5770420c420e08cb9500a0a87788c07e1ea Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 20 Aug 2024 13:49:06 +0530 Subject: [PATCH 11/13] =?UTF-8?q?style:=20=F0=9F=92=84=20remove=20unused?= =?UTF-8?q?=20title=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DataRoom/IndustryComparison/Content.tsx | 4 +--- src/components/DataRoom/IndustryComparison/styles.module.css | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/DataRoom/IndustryComparison/Content.tsx b/src/components/DataRoom/IndustryComparison/Content.tsx index 499ae0dd..4d162cb6 100644 --- a/src/components/DataRoom/IndustryComparison/Content.tsx +++ b/src/components/DataRoom/IndustryComparison/Content.tsx @@ -29,9 +29,7 @@ const Content = ({ opacity: isMobile ? 1 : opacity, }} > - - {title} - + {title} ) diff --git a/src/components/DataRoom/IndustryComparison/styles.module.css b/src/components/DataRoom/IndustryComparison/styles.module.css index 907f5e47..12378e99 100644 --- a/src/components/DataRoom/IndustryComparison/styles.module.css +++ b/src/components/DataRoom/IndustryComparison/styles.module.css @@ -31,11 +31,6 @@ margin-top: 72px; } -.title { - z-index: 30; - pointer-events: none; -} - @media (max-width: 900px) { .slidingPanelContent { padding-left: 32px; From 07dd56ac3e8e40268950222eb04694106b0aa4d3 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 20 Aug 2024 15:14:09 +0530 Subject: [PATCH 12/13] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20remove=20unuse?= =?UTF-8?q?d=20prop=20dimensions=20from=20drawDots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DataRoom/IndustryComparison/DotGrid.tsx | 4 ++-- src/components/DataRoom/IndustryComparison/utils/drawDots.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index 9110ae28..c32c7af2 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -41,9 +41,9 @@ export default function DotGrid({ updateCanvas(canvas, ctx, dimensions.width, dimensions.height) // Draw dots immediately after creating or updating them // This draw call ensure dots are already visible when canvas scrolls into view - drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) + drawDots(ctx, dotsRef.current, mousePosition, isMobile) } else if (currentRenderState.mousePosition !== prevRenderState.mousePosition) { - drawDots(ctx, dotsRef.current, dimensions, mousePosition, isMobile) + drawDots(ctx, dotsRef.current, mousePosition, isMobile) } prevRenderStateRef.current = currentRenderState diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts index 5fbcc6eb..f0d02f9c 100644 --- a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -10,7 +10,6 @@ let lastUpdatedArea: { x: number; y: number; radius: number } | null = null export const drawDots = ( ctx: CanvasRenderingContext2D, dots: Position[], - dimensions: { width: number; height: number }, mousePosition: Position, isMobile: boolean, ) => { From 4e7f1808ad4a883462df6e7121fc6055bc9332e9 Mon Sep 17 00:00:00 2001 From: Malay Vasa Date: Tue, 20 Aug 2024 15:30:56 +0530 Subject: [PATCH 13/13] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20remove=20mou?= =?UTF-8?q?se=20hover=20effect=20from=20dot=20grid=20animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataRoom/IndustryComparison/Content.tsx | 2 +- .../DataRoom/IndustryComparison/DotGrid.tsx | 29 +++-------- .../IndustryComparison/utils/drawDots.ts | 48 ++----------------- .../utils/useMousePosition.ts | 43 ----------------- 4 files changed, 12 insertions(+), 110 deletions(-) delete mode 100644 src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts diff --git a/src/components/DataRoom/IndustryComparison/Content.tsx b/src/components/DataRoom/IndustryComparison/Content.tsx index 4d162cb6..702cc5f3 100644 --- a/src/components/DataRoom/IndustryComparison/Content.tsx +++ b/src/components/DataRoom/IndustryComparison/Content.tsx @@ -30,7 +30,7 @@ const Content = ({ }} > {title} - + ) } diff --git a/src/components/DataRoom/IndustryComparison/DotGrid.tsx b/src/components/DataRoom/IndustryComparison/DotGrid.tsx index c32c7af2..0f6b3a6e 100644 --- a/src/components/DataRoom/IndustryComparison/DotGrid.tsx +++ b/src/components/DataRoom/IndustryComparison/DotGrid.tsx @@ -1,4 +1,3 @@ -import type { MotionValue } from 'framer-motion' import { useEffect, useRef, useCallback } from 'react' import { createDots } from './utils/createDots' import { drawDots } from './utils/drawDots' @@ -6,21 +5,13 @@ import { useIsMediumScreen } from '@/hooks/useMaxWidth' import css from './styles.module.css' import useContainerSize from '@/hooks/useContainerSize' import { updateCanvas } from './utils/updateCanvas' -import useMousePosition from './utils/useMousePosition' - -export default function DotGrid({ - containerRef, - scrollYProgress, -}: { - containerRef: React.RefObject - scrollYProgress?: MotionValue -}) { + +export default function DotGrid({ containerRef }: { containerRef: React.RefObject }) { const canvasRef = useRef(null) const isMobile = useIsMediumScreen() const dimensions = useContainerSize(containerRef) - const mousePosition = useMousePosition(canvasRef, dimensions, scrollYProgress) - const prevRenderStateRef = useRef({ dimensions, mousePosition, isMobile }) + const prevRenderStateRef = useRef({ dimensions }) const dotsRef = useRef | null>(null) const animationFrameId = useRef() @@ -29,27 +20,21 @@ export default function DotGrid({ const ctx = canvas?.getContext('2d') if (!canvas || !ctx || dimensions.width <= 0 || dimensions.height <= 0) return - const currentRenderState = { dimensions, mousePosition, isMobile } + const currentRenderState = { dimensions } const prevRenderState = prevRenderStateRef.current - if ( - !dotsRef.current || - currentRenderState.dimensions !== prevRenderState.dimensions || - currentRenderState.isMobile !== prevRenderState.isMobile - ) { + if (!dotsRef.current || currentRenderState.dimensions !== prevRenderState.dimensions) { dotsRef.current = createDots(dimensions, isMobile) updateCanvas(canvas, ctx, dimensions.width, dimensions.height) // Draw dots immediately after creating or updating them // This draw call ensure dots are already visible when canvas scrolls into view - drawDots(ctx, dotsRef.current, mousePosition, isMobile) - } else if (currentRenderState.mousePosition !== prevRenderState.mousePosition) { - drawDots(ctx, dotsRef.current, mousePosition, isMobile) + drawDots(ctx, dotsRef.current) } prevRenderStateRef.current = currentRenderState animationFrameId.current = requestAnimationFrame(renderFrame) - }, [dimensions, mousePosition, isMobile]) + }, [dimensions, isMobile]) useEffect(() => { renderFrame() diff --git a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts index f0d02f9c..14de42e5 100644 --- a/src/components/DataRoom/IndustryComparison/utils/drawDots.ts +++ b/src/components/DataRoom/IndustryComparison/utils/drawDots.ts @@ -1,52 +1,12 @@ type Position = { x: number; y: number } -const MAX_SCALE_DISTANCE = 15 -const MOBILE_MAX_SCALE = 8 -const DESKTOP_MAX_SCALE = 12 const DOT_COLOR = '#121312' -let lastUpdatedArea: { x: number; y: number; radius: number } | null = null - -export const drawDots = ( - ctx: CanvasRenderingContext2D, - dots: Position[], - mousePosition: Position, - isMobile: boolean, -) => { - const maxScale = isMobile ? MOBILE_MAX_SCALE : DESKTOP_MAX_SCALE - const updatedRadius = MAX_SCALE_DISTANCE * maxScale - - // Clear the previously updated area - if (lastUpdatedArea) { - ctx.clearRect( - lastUpdatedArea.x - lastUpdatedArea.radius, - lastUpdatedArea.y - lastUpdatedArea.radius, - lastUpdatedArea.radius * 2, - lastUpdatedArea.radius * 2, - ) - } - // Clear the new (to be updated) area - ctx.clearRect(mousePosition.x - updatedRadius, mousePosition.y - updatedRadius, updatedRadius * 2, updatedRadius * 2) - +export const drawDots = (ctx: CanvasRenderingContext2D, dots: Position[]) => { ctx.fillStyle = DOT_COLOR - dots.forEach((dot) => { - const dx = mousePosition.x - dot.x - const dy = mousePosition.y - dot.y - const distance = Math.sqrt(dx * dx + dy * dy) - - if (distance <= updatedRadius) { - const scale = Math.max(1, maxScale - distance / MAX_SCALE_DISTANCE) - ctx.beginPath() - ctx.arc(dot.x, dot.y, 1 * scale, 0, 2 * Math.PI) - ctx.fill() - } else { - // Draw unupdated dots with normal size - ctx.beginPath() - ctx.arc(dot.x, dot.y, 1, 0, 2 * Math.PI) - ctx.fill() - } + ctx.beginPath() + ctx.arc(dot.x, dot.y, 1, 0, 2 * Math.PI) + ctx.fill() }) - - lastUpdatedArea = { x: mousePosition.x, y: mousePosition.y, radius: updatedRadius } } diff --git a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts b/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts deleted file mode 100644 index 5ce684bc..00000000 --- a/src/components/DataRoom/IndustryComparison/utils/useMousePosition.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useState, useEffect } from 'react' -import type { MotionValue } from 'framer-motion' -import { useIsMediumScreen } from '@/hooks/useMaxWidth' - -/** - * Custom hook to track mouse position or simulate it based on scroll progress. - * @param canvasRef - Reference to the canvas element. - * @param dimensions - Object containing width and height of the container. - * @param scrollYProgress - MotionValue for scroll progress, used on mobile devices. - * @returns An object with x and y coordinates representing either: - * - Actual mouse position relative to the canvas (on desktop) - * - Simulated position based on scroll progress (on mobile) - */ -export default function useMousePosition( - canvasRef: React.RefObject, - dimensions: { width: number; height: number }, - scrollYProgress?: MotionValue, -) { - const isMobile = useIsMediumScreen() - const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) - - useEffect(() => { - if (isMobile && scrollYProgress) { - const updatePositionMobile = () => { - const progress = scrollYProgress.get() - setMousePosition({ x: dimensions.width - dimensions.width / 8, y: progress * dimensions.height }) - } - return scrollYProgress.on('change', updatePositionMobile) - } else { - const canvas = canvasRef.current - const updatePositionDesktop = (e: MouseEvent) => { - const rect = canvas?.getBoundingClientRect() - if (rect) { - setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) - } - } - canvas?.addEventListener('mousemove', updatePositionDesktop) - return () => canvas?.removeEventListener('mousemove', updatePositionDesktop) - } - }, [canvasRef, isMobile, scrollYProgress, dimensions]) - - return mousePosition -}