Last active
September 14, 2024 14:14
-
-
Save rerrahkr/b0f58fad33e088f0a26ad878b72128f7 to your computer and use it in GitHub Desktop.
親要素の大きさに合わせてサイズを自動調整するReactキャンバスコンポーネント
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { type SerializedStyles, css } from "@emotion/react"; | |
| import { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; | |
| /** | |
| * Props type which is used in custom component to append style props. | |
| */ | |
| export type CustomComponentStyleProps = { | |
| /** | |
| * Static style for container element. | |
| */ | |
| staticCss?: SerializedStyles | undefined; | |
| /** | |
| * Dynamic style for container element. | |
| */ | |
| style?: React.CSSProperties | undefined; | |
| }; | |
| export type AutoResizedCanvasBaseProps = { | |
| /** | |
| * Event handler called when the canvas is resized. | |
| * @param canvas Canvas element. | |
| */ | |
| onResize?: (() => void) | undefined; | |
| }; | |
| type AutoResizedCanvasProps = AutoResizedCanvasBaseProps & | |
| CustomComponentStyleProps; | |
| const componentCss = { | |
| container: css` | |
| position: relative; | |
| `, | |
| canvas: css` | |
| display: block; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| `, | |
| }; | |
| /** | |
| * Canvas component whose size is stretched to its parent rectangle. | |
| */ | |
| export const AutoResizedCanvas = forwardRef< | |
| HTMLCanvasElement, | |
| AutoResizedCanvasProps | |
| >((props, ref) => { | |
| const { | |
| staticCss: containerCustomCss, | |
| style: containerCustomStyle, | |
| onResize, | |
| } = props; | |
| const canvasRef = useRef<HTMLCanvasElement>(null); | |
| useImperativeHandle<HTMLCanvasElement | null, HTMLCanvasElement | null>( | |
| ref, | |
| () => canvasRef.current, | |
| [] | |
| ); | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| useEffect(() => { | |
| const container = containerRef.current; | |
| if (!container) { | |
| return; | |
| } | |
| const observer = new ResizeObserver(([entry]) => { | |
| const canvas = canvasRef.current; | |
| if (!canvas) { | |
| return; | |
| } | |
| const directionIsHorizontal = | |
| getComputedStyle(canvas).writingMode.startsWith("horizontal"); | |
| const size = entry.devicePixelContentBoxSize[0]; | |
| canvas.height = directionIsHorizontal ? size.blockSize : size.inlineSize; | |
| canvas.width = directionIsHorizontal ? size.inlineSize : size.blockSize; | |
| onResize?.(); | |
| }); | |
| observer.observe(container, { box: "device-pixel-content-box" }); | |
| return () => { | |
| return observer.disconnect(); | |
| }; | |
| }, [onResize]); | |
| return ( | |
| <div | |
| ref={containerRef} | |
| css={[componentCss.container, containerCustomCss]} | |
| style={containerCustomStyle} | |
| > | |
| <canvas ref={canvasRef} css={componentCss.canvas}> | |
| Canvas is not supported in this browser. | |
| </canvas> | |
| </div> | |
| ); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment