Commit 251d7c0b authored by Justin Domingue's avatar Justin Domingue Committed by GitHub

Fix NFT SVG performance issue in browser (#1509)

* only animate NFT SVG on hover by using a canvas

* handle high dpis

* animation transition between canvas and img

* set start state to not animated

* removed animations that were causing issues on Firefox

* simplify code

* remove debugger statement

* remove useEffect in favor of an event handler

* hide canvas without unmounting to avoid blinking

* fix lint error

* fix flicker on hover by leaving canvas always visible

* add comment about z-index
Co-authored-by: default avatarJustin Domingue <domingue.justin@gmail.com>
parent 285e4f28
import React, { useCallback, useMemo, useState } from 'react' import React, { SyntheticEvent, useCallback, useMemo, useRef, useState } from 'react'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { PoolState, usePool } from 'hooks/usePools' import { PoolState, usePool } from 'hooks/usePools'
...@@ -126,6 +126,23 @@ const ResponsiveButtonPrimary = styled(ButtonPrimary)` ...@@ -126,6 +126,23 @@ const ResponsiveButtonPrimary = styled(ButtonPrimary)`
`}; `};
` `
const NFTGrid = styled.div`
display: grid;
grid-template: 'overlap';
min-height: 400px;
`
const NFTCanvas = styled.canvas`
grid-area: overlap;
`
const NFTImage = styled.img`
grid-area: overlap;
height: 400px;
/* Ensures SVG appears on top of canvas. */
z-index: 1;
`
function CurrentPriceCard({ function CurrentPriceCard({
inverted, inverted,
pool, pool,
...@@ -183,6 +200,50 @@ function getRatio( ...@@ -183,6 +200,50 @@ function getRatio(
} }
} }
function NFT({ image, height: targetHeight }: { image: string; height: number }) {
const [animate, setAnimate] = useState(false)
const canvasRef = useRef<HTMLCanvasElement>()
const imageRef = useRef<HTMLImageElement>()
const getSnapshot = (src: HTMLImageElement) => {
if (!canvasRef.current) return
const { current: canvas } = canvasRef
const context = canvas.getContext('2d')
if (!context) return
let { width, height } = src
// src may be hidden and not have the target dimensions
const ratio = width / height
height = targetHeight
width = Math.round(ratio * targetHeight)
// Ensure crispness at high DPIs
canvas.width = width * devicePixelRatio
canvas.height = height * devicePixelRatio
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
context.scale(devicePixelRatio, devicePixelRatio)
context.clearRect(0, 0, width, height)
context.drawImage(src, 0, 0, width, height)
}
const onLoad = (e: SyntheticEvent<HTMLImageElement>) => {
getSnapshot(e.target as HTMLImageElement)
}
return (
<NFTGrid onMouseEnter={() => setAnimate(true)} onMouseLeave={() => setAnimate(false)}>
<NFTCanvas ref={canvasRef as any} />
<NFTImage src={image} hidden={!animate} onLoad={onLoad} ref={imageRef as any} />
</NFTGrid>
)
}
export function PositionPage({ export function PositionPage({
match: { match: {
params: { tokenId: tokenIdFromUrl }, params: { tokenId: tokenIdFromUrl },
...@@ -461,7 +522,7 @@ export function PositionPage({ ...@@ -461,7 +522,7 @@ export function PositionPage({
}} }}
> >
<div style={{ marginRight: 12 }}> <div style={{ marginRight: 12 }}>
<img height="400px" src={metadata.result.image} /> <NFT image={metadata.result.image} height={400} />
</div> </div>
{typeof chainId === 'number' && owner && !ownsNFT ? ( {typeof chainId === 'number' && owner && !ownsNFT ? (
<ExternalLink href={getEtherscanLink(chainId, owner, 'address')}>Owner</ExternalLink> <ExternalLink href={getEtherscanLink(chainId, owner, 'address')}>Owner</ExternalLink>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment