feat: support image zoom (#355)

This commit is contained in:
Innei 2023-04-17 07:57:32 +08:00 committed by GitHub
parent f1fd5f5468
commit e7ee565aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 21 deletions

View File

@ -77,6 +77,7 @@
"lottie-react": "^2.4.0",
"mdast-util-to-string": "3.2.0",
"mdast-util-toc": "6.1.1",
"medium-zoom": "1.0.8",
"mermaid": "10.1.0",
"nanoid": "4.0.2",
"next": "13.2.4",

View File

@ -154,6 +154,9 @@ dependencies:
mdast-util-toc:
specifier: 6.1.1
version: 6.1.1
medium-zoom:
specifier: 1.0.8
version: 1.0.8
mermaid:
specifier: 10.1.0
version: 10.1.0(react-dom@18.2.0)(react@18.2.0)
@ -10170,6 +10173,10 @@ packages:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
dev: true
/medium-zoom@1.0.8:
resolution: {integrity: sha512-CjFVuFq/IfrdqesAXfg+hzlDKu6A2n80ZIq0Kl9kWjoHh9j1N9Uvk5X0/MmN0hOfm5F9YBswlClhcwnmtwz7gA==}
dev: false
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true

View File

@ -1,10 +1,8 @@
import { useEffect, useRef, useState } from "react"
import useOnClickOutside from "use-onclickoutside"
import type { NoteMetadata } from "crossbell.js"
import { PageContent } from "../common/PageContent"
import { useState } from "react"
import { useDate } from "~/hooks/useDate"
import { PageContent } from "../common/PageContent"
import { Button, ButtonGroup } from "../ui/Button"
import { useTranslation } from "next-i18next"
export const ImportPreview: React.FC<{

View File

@ -9,7 +9,7 @@ export const Avatar: React.FC<
name?: string | null
size?: number
rounded?: boolean
imageRef?: React.Ref<HTMLImageElement>
imageRef?: React.MutableRefObject<HTMLImageElement>
} & React.HTMLAttributes<HTMLSpanElement>
> = ({ images, size, name, className, rounded, imageRef, ...props }) => {
size = size || 60

View File

@ -1,22 +1,70 @@
import React from "react"
import React, { useEffect } from "react"
import { default as NextImage, ImageProps } from "next/image"
import { toGateway, toIPFS } from "~/lib/ipfs-parser"
import { useMobileLayout } from "~/hooks/useMobileLayout"
import { useGetState } from "~/hooks/useGetState"
export const Image: React.FC<
{
className?: string
src?: string
width?: number | string
height?: number | string
"original-src"?: string
imageRef?: React.Ref<HTMLImageElement>
} & React.HTMLAttributes<HTMLImageElement> &
ImageProps
> = ({ fill, className, alt, src, width, height, imageRef, ...props }) => {
type TImageProps = {
className?: string
src?: string
width?: number | string
height?: number | string
"original-src"?: string
imageRef?: React.MutableRefObject<HTMLImageElement>
zoom?: boolean
} & React.HTMLAttributes<HTMLImageElement> &
ImageProps
export const Image: React.FC<TImageProps> = ({
fill,
className,
alt,
src,
width,
height,
imageRef,
zoom,
...props
}) => {
src = toIPFS(src)
const [paddingTop, setPaddingTop] = React.useState("0")
const [autoWidth, setAutoWidth] = React.useState(0)
const noOptimization = className?.includes("no-optimization")
const imageRefInternal = React.useRef<HTMLImageElement>(null)
const isMobileLayout = useMobileLayout()
const getSrc = useGetState(src)
useEffect(() => {
if (!imageRef) return
if (!imageRefInternal.current) return
if (typeof imageRef === "object") {
imageRef.current = imageRefInternal.current
}
}, [imageRef])
useEffect(() => {
const $image = imageRefInternal.current
if (!$image) return
if (isMobileLayout) {
const clickHandler = () => {
window.open(getSrc(), "_blank")
}
$image.addEventListener("click", clickHandler)
return () => {
$image.removeEventListener("click", clickHandler)
}
}
if (zoom) {
import("medium-zoom").then(({ default: mediumZoom }) => {
mediumZoom($image, {
margin: 10,
background: "rgb(var(--tw-colors-i-white))",
scrollOffset: 0,
})
})
}
}, [zoom, isMobileLayout])
if (!src) {
return null
@ -57,7 +105,7 @@ export const Image: React.FC<
setAutoWidth(naturalWidth)
}
}}
ref={imageRef}
ref={imageRefInternal}
/>
) : (
<span
@ -89,9 +137,18 @@ export const Image: React.FC<
setAutoWidth(naturalWidth)
}
}}
ref={imageRef}
ref={imageRefInternal}
/>
</span>
</span>
)
}
export const ZoomedImage: React.FC<TImageProps> = (props) => {
return (
<span className="text-center block">
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<Image {...props} zoom />
</span>
)
}

View File

@ -16,7 +16,7 @@ import { rehypeWrapCode } from "./rehype-wrap-code"
import jsYaml from "js-yaml"
import rehypeReact from "rehype-react"
import { createElement, ReactElement } from "react"
import { Image } from "~/components/ui/Image"
import { ZoomedImage } from "~/components/ui/Image"
import remarkDirective from "remark-directive"
import remarkDirectiveRehype from "remark-directive-rehype"
import { remarkYoutube } from "./remark-youtube"
@ -177,7 +177,7 @@ export const renderPageContent = (
.use(html ? () => (tree: any) => {} : rehypeReact, {
createElement: createElement,
components: {
img: Image,
img: ZoomedImage,
anchor: Element,
mention: Mention,
mermaid: Mermaid,