From 7c48058cd353942613145a1ff289596d45c86082 Mon Sep 17 00:00:00 2001 From: jaredvu Date: Tue, 28 Nov 2023 15:44:01 -0800 Subject: [PATCH] Animation attempt --- src/hooks/orderbook/useDrawOrderbook.ts | 52 ++++++++++++-- src/views/Orderbook.tsx | 92 ++++++++++++++++--------- 2 files changed, 107 insertions(+), 37 deletions(-) diff --git a/src/hooks/orderbook/useDrawOrderbook.ts b/src/hooks/orderbook/useDrawOrderbook.ts index 4de0827..6191ce6 100644 --- a/src/hooks/orderbook/useDrawOrderbook.ts +++ b/src/hooks/orderbook/useDrawOrderbook.ts @@ -168,6 +168,7 @@ export const useDrawOrderbook = ({ size, price, mine, + animationType, }: { ctx: CanvasRenderingContext2D; canvasWidth: number; @@ -175,9 +176,39 @@ export const useDrawOrderbook = ({ size?: Nullable; price?: Nullable; mine?: Nullable; + animationType?: 'remove' | 'add'; }) => { const y = getYFromIndex(idx, 'text'); - const textColor = Colors[theme].text2; + const rectY = getYFromIndex(idx, 'rect'); + + let startTime: number; + let textColor: string = Colors[theme].text2; + const animateColor = (timestamp: number) => { + if (!startTime) startTime = timestamp; + const animationTime = timestamp - startTime; + + // Linear transition for demonstration purposes + textColor = animationType + ? { + add: Colors[theme].green, + remove: Colors[theme].red, + }[animationType] + : Colors[theme].text2; + + if (animationTime < 500) { + // Continue the animation for 500ms (adjust as needed) + requestAnimationFrame(animateColor); + } else { + textColor = Colors[theme].text2; + } + + if (animationType) { + // TODO: Clear rect and redraw text + // ctx.clearRect(0, rectY, canvasWidth, rectY + ROW_HEIGHT); + } + }; + + requestAnimationFrame(animateColor); // Size text if (size) { @@ -201,7 +232,7 @@ export const useDrawOrderbook = ({ // Mine text if (mine) { - ctx.fillStyle = textColor; + ctx.fillStyle = Colors[theme].text2; ctx.fillText( MustBigNumber(mine).toFormat(stepSizeDecimals ?? TOKEN_DECIMALS, formatOptions), getXByColumn({ canvasWidth, colIdx: 2 }) - ROW_PADDING_RIGHT, @@ -258,9 +289,7 @@ export const useDrawOrderbook = ({ return canvas.width / ratio; }, [canvas, hoverCanvas]); - /** - * Handle Row Hover - */ + //Handle Row Hover const lastHoveredRowRef = useRef(); const lastHoveredRow = lastHoveredRowRef.current; useEffect(() => { @@ -280,7 +309,12 @@ export const useDrawOrderbook = ({ } }, [lastHoveredRow, hoveredRow]); - // Update histograms and row contents on data change + // Row Removal Animation state + const previousDataRef = useRef<{ + [key: string]: number; + }>(Object.fromEntries(data.map((row: RowData) => [row.price, row.size]))); + + //Update histograms and row contents on data change useEffect(() => { const ctx = canvas?.getContext('2d'); @@ -316,6 +350,9 @@ export const useDrawOrderbook = ({ histogramSide, }); + const isNew = !previousDataRef.current[price]; + const wasRemoved = previousDataRef.current[price] && !data.find((row) => row.price === price); + // Size, Price, Mine drawText({ ctx, @@ -324,8 +361,11 @@ export const useDrawOrderbook = ({ size, price, mine, + animationType: isNew ? 'add' : wasRemoved ? 'remove' : undefined, }); }); + + previousDataRef.current = Object.fromEntries(data.map((row: RowData) => [row.price, row.size])); }, [ canvasWidth, data, diff --git a/src/views/Orderbook.tsx b/src/views/Orderbook.tsx index b72d2fd..6dedd3f 100644 --- a/src/views/Orderbook.tsx +++ b/src/views/Orderbook.tsx @@ -25,6 +25,7 @@ import { Tag } from '@/components/Tag'; import { ROW_HEIGHT, Row, type RowData } from './Orderbook/OrderbookRow'; import { SpreadRow } from './Orderbook/SpreadRow'; +import { Output, OutputType } from '@/components/Output'; const ORDERBOOK_MAX_ROWS_PER_SIDE = 35; @@ -166,21 +167,32 @@ export const Orderbook = ({ side="ask" onMouseLeave={() => setHoveredRow(undefined)} > - <$HistogramCanvas ref={asksCanvasRef} width="100%" height={numRows * ROW_HEIGHT} /> - <$HistogramCanvas ref={asksHoverRef} width="100%" height={numRows * ROW_HEIGHT} /> - - {asksSlice.map((row: RowData, idx) => - row.price ? ( - <$Row - key={idx} - title={`${row.price}`} - onClick={() => onRowAction(row.price)} - onMouseOver={(e) => setHoveredRow({ idx, side: 'ask' })} - /> - ) : ( - <$Row key={idx} onMouseOver={(e) => setHoveredRow(undefined)} /> - ) - )} + <$OrderbookCanvas ref={asksCanvasRef} width="100%" height={numRows * ROW_HEIGHT} /> + <$HoverCanvas ref={asksHoverRef} width="100%" height={numRows * ROW_HEIGHT} /> + <$HoverRows> + {asksSlice.map((row: RowData, idx) => + row.price ? ( + <$Row + key={idx} + title={`${row.price}`} + onClick={() => onRowAction(row.price)} + onMouseOver={(e) => setHoveredRow({ idx, side: 'ask' })} + > + + + + + + + ) : ( + <$Row key={idx} onMouseOver={(e) => setHoveredRow(undefined)} /> + ) + )} + setHoveredRow(undefined)} > - <$HistogramCanvas ref={bidsCanvasRef} width="100%" height={numRows * ROW_HEIGHT} /> - <$HistogramCanvas ref={bidsHoverRef} width="100%" height={numRows * ROW_HEIGHT} /> - - {bidsSlice.map((row: RowData, idx) => - row.price ? ( - <$Row - key={idx} - title={`${row.price}`} - onClick={() => onRowAction(row.price)} - onMouseOver={(e) => setHoveredRow({ idx, side: 'bid' })} - /> - ) : ( - <$Row key={idx} onMouseOver={(e) => setHoveredRow(undefined)} /> - ) - )} + <$OrderbookCanvas ref={bidsCanvasRef} width="100%" height={numRows * ROW_HEIGHT} /> + <$HoverCanvas ref={bidsHoverRef} width="100%" height={numRows * ROW_HEIGHT} /> + <$HoverRows> + {bidsSlice.map((row: RowData, idx) => + row.price ? ( + <$Row + key={idx} + title={`${row.price}`} + onClick={() => onRowAction(row.price)} + onMouseOver={(e) => setHoveredRow({ idx, side: 'bid' })} + > + + + + + + + ) : ( + <$Row key={idx} onMouseOver={(e) => setHoveredRow(undefined)} /> + ) + )} + )} @@ -267,7 +290,7 @@ const $OrderbookSideContainer = styled.div<{ numRows: number; side: 'bid' | 'ask position: relative; `; -const $HistogramCanvas = styled(Canvas)` +const $OrderbookCanvas = styled(Canvas)` width: 100%; height: 100%; @@ -278,6 +301,13 @@ const $HistogramCanvas = styled(Canvas)` font-feature-settings: var(--fontFeature-monoNumbers); `; +const $HoverCanvas = styled($OrderbookCanvas)``; + +const $HoverRows = styled.div` + position: absolute; + width: 100%; +`; + const $Row = styled(Row)<{ onClick?: () => void }>` ${({ onClick }) => (onClick ? 'cursor: pointer;' : 'cursor: default;')} `;