import React , { useCallback , useEffect , useRef , useState } from 'react' ; import classnames from 'classnames' ; import { useResize } from '@/hooks' ; import { IStoreState } from '@/store' ; import { useSelector } from 'react-redux' ; import Bubble from './bubble' ; import './style.less' ; type Props = { width ? : number ; height ? : number ; cover ? : string ; className ? : string ; isBlur ? : boolean ; direction ? : 'vertical' | 'horizontal' ; interval ? : number [ ] ; radius ? : boolean ; shadow ? : boolean ; refs ? : any ; } ; interface BubbleCover { ( props ) : JSX . Element | null ; } const BubbleCover = ( { cover , className , isBlur = true , direction = 'vertical' , interval , radius = true , shadow = true , refs } ) => { const containerRef = useRef < HTMLDivElement | null > ( ) ; const ref = useRef < HTMLCanvasElement > ( null ) ; const app = useSelector ( ( state ) => state . app ) ; const [ ctx , setCtx ] = useState < CanvasRenderingContext2D > ( ) ; const [ bubbles , setBubbles ] = useState < Bubble [ ] > ( [ ] ) ; const [ total , setTotal ] = useState ( 0 ) ; const [ canvas , setCanvas ] = useState ( { width : 0 , height : 0 } ) ; useEffect ( ( ) => { setCtx ( ref . current ?. getContext ( '2d' ) as CanvasRenderingContext2D ) ; } , [ ] ) ; const setCanvasSize = useCallback ( ( ) => { const set = ( ) => { if ( containerRef . current ) { const o = containerRef . current . getBoundingClientRect ( ) ; setCanvas ( { width : o . width , height : o . height } ) ; } } ; if ( app . layoutSize === 'small' ) { setTimeout ( ( ) => { set ( ) ; } , 500 ) ; } else { set ( ) ; } } , [ app . layoutSize ] ) ; useEffect ( ( ) => { if ( refs ) { refs . current . setCanvasSize = setCanvasSize ; } } , [ refs , setCanvasSize ] ) ; useResize ( setCanvasSize ) ; useEffect ( ( ) => { setCanvasSize ( ) ; } , [ setCanvasSize ] ) ; useEffect ( ( ) => { if ( canvas . width > 0 ) { setTotal ( Math . ceil ( canvas . width * 0.04 ) ) ; } } , [ canvas ] ) ; useEffect ( ( ) => { const arr = [ ] ; for ( let i = 0 ; i < total ; i ++ ) { const b = new Bubble ( { width : canvas . width , height : canvas . height , direction , interval } ) ; arr . push ( b ) ; } setBubbles ( arr ) ; } , [ canvas , total , direction , interval ] ) ; const render = useCallback ( ( ) => { if ( bubbles . length === 0 || ! ctx ) { return ; } ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ; bubbles . forEach ( ( bubble ) => { bubble . draw ( ctx ) ; } ) ; requestAnimationFrame ( render ) ; } , [ ctx , bubbles , canvas ] ) ; useEffect ( ( ) => { render ( ) ; } , [ render ] ) ; return ( < div className = { classnames ( [ 'bubble-cover' , className , { 'bubble-cover__radius' : radius , 'bubble-cover__shadow' : shadow } ] ) } ref = { ( node ) => ( containerRef . current = node ) } > < div className = { classnames ( 'bubble-cover__bg' , { 'bubble-cover__bg-blur' : isBlur } ) } > { cover && ( < div className = "bubble-cover__bg-img" style = { { backgroundImage : `url(${ cover })` } } > < /div>)} < /div> < canvas className = "bubble-cover__canvas" ref = { ref } width = { canvas . width } height = { canvas . height } > < /canvas> < /div>); } ; export default BubbleCover ;