import { createElement , Size } from '@canvas-ui/core' ; import React , { CSSProperties , memo , ReactNode , useEffect , useLayoutEffect , useMemo , useRef , useState } from 'react' ; import useMeasure , { RectReadOnly } from 'react-use-measure' ; import type { ViewProps } from '../elements' ; import { useBinding , UseBindingOptions } from './binding' ; import { DprObserver } from './dpr-observer' ; const measureNodeStyle = { position : 'relative' , width : '100%' , height : '100%' , overflow : 'hidden' , } ; const canvasStyle = { width : '100%' , height : '100%' , letterSpacing : 'normal' , position : 'absolute' , userSelect : 'none' , WebkitFontSmoothing : 'subpixel-antialiased' , } ; /**
 * <Canvas> 的子节点是 glui 节点
 */ export type CanvasProps = ViewProps & Pick < BindingProps , 'onReady' > & { children : ReactNode ; } ; export const Canvas = memo ( function Canvas ( { children , onReady , } ) { const [ bindMeasureNode , rect ] = useMeasure ( { scroll : true , debounce : { scroll : 50 , resize : 1 / 60 } } ) ; const canvasElRef = useRef < HTMLCanvasElement | null > ( null ) ; const ready = useReady ( rect ) ; const binding = ready && canvasElRef . current && ( < Binding el = { canvasElRef . current } top = { rect . top } left = { rect . left } width = { rect . width } height = { rect . height } onReady = { onReady } > { children } < /Binding>); return ( < div ref = { bindMeasureNode } style = { measureNodeStyle } className = "glui-canvas" > < canvas ref = { canvasElRef } style = { canvasStyle } / > { binding } < /div>); } ) ; function useReady ( rect ) { const flag = useRef ( false ) ; const ready = useMemo ( ( ) => { return flag . current = ( flag . current || ( rect . width > 0 && rect . height > 0 ) ) ; } , [ rect ] ) ; return ready ; } function useDprObserver ( ) { const [ dpr , setDpr ] = useState ( ( ) => { return self . devicePixelRatio ; } ) ; useEffect ( ( ) => { const observer = new DprObserver ( setDpr ) ; return ( ) => { observer . disconnect ( ) ; } ; } , [ ] ) ; return dpr ; } type BindingProps = Omit < UseBindingOptions , 'binding' > & { el : HTMLCanvasElement ; } ; function Binding ( { el , left , top , width , height , children , } ) { const [ binding ] = useState ( ( ) => { const binding = createElement ( 'Canvas' ) ; binding . prepareInitialFrame ( ) ; return binding ; } ) ; const dpr = useDprObserver ( ) ; useLayoutEffect ( ( ) => { binding . el = el ; binding . dpr = dpr ; binding . size = Size . fromWH ( width , height ) ; } , [ binding , el , width , height , dpr , ] ) ; useBinding ( { binding , left , top , width , height , children , } ) ; return null ; }