import Base from '@src/common/base' ; import { doublePi , regExp } from '@src/common/constants' ; import { CommonConfig } from '@src/types/common-config' ; import { ValueOf } from '@src/types/utility-types' ; import { isArray , isString , loadImage } from '@src/utils' ; const validShapeTypes = [ 'circle' , 'triangle' , 'star' , 'image' ] as const ; export type NormalShapeType = Exclude < ValueOf < typeof validShapeTypes > , 'image' > ; export type ShapeType = NormalShapeType | string | CanvasImageSource ; export interface ShapeData { type : ValueOf < typeof validShapeTypes > ; source ? : CanvasImageSource ; isImageLoaded ? : boolean ; sides ? : number ; dent ? : number ; } param shape function generateShapeData ( shape ) { if ( isString ( shape ) ) { if ( shape === 'circle' || shape === 'triangle' ) { return { type : shape } ; } if ( regExp . shapeStar . test ( shape as string ) ) { const result = ( shape as string ) . split ( ':' ) ; return { type : 'star' , sides : Number ( result [ 1 ] ) || 5 , dent : Number ( result [ 2 ] ) || 0.5 , } ; } if ( regExp . http . test ( shape as string ) || regExp . imageBase64 . test ( shape as string ) ) { const dataRef = { type : 'image' , isImageLoaded : false , } ; loadImage ( shape as string , ( image ) => { dataRef . isImageLoaded = true ; dataRef . source = image ; } ) ; return dataRef ; } console . warn ( `Shape value of ${ shape } is invalid.` ) ; } try { if ( shape instanceof HTMLImageElement || shape instanceof SVGImageElement || shape instanceof HTMLVideoElement || shape instanceof HTMLCanvasElement || shape instanceof ImageBitmap || shape instanceof OffscreenCanvas ) { return { type : 'image' , isImageLoaded : true , source : shape , } ; } } catch ( _err ) { console . warn ( 'Your browser does not support [CanvasImageSource](https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource), please upgrade it.' ) ; } return { type : 'image' , isImageLoaded : false , } ; } param ctx param x param y param r param p param m function drawStar ( ctx , x , y , r , p , m ) { ctx . translate ( x , y ) ; ctx . moveTo ( 0 , 0 - r ) ; for ( let i = 0 ; i < p ; i ++ ) { ctx . rotate ( Math . PI / p ) ; ctx . lineTo ( 0 , 0 - r * m ) ; ctx . rotate ( Math . PI / p ) ; ctx . lineTo ( 0 , 0 - r ) ; } } export default abstract class Shape < Options > extends Base < Options > { protected readonly options ! : Options & CommonConfig & { shape ? : ShapeType | ShapeType [ ] ; } ; /**
     * 获取形状数据
     */ protected getShapeData ( ) { const { shape } = this . options ; const defaultShapeData = { type : 'circle' } ; if ( ! shape ) { return defaultShapeData ; } if ( isArray ( shape ) ) { const length = ( shape as ShapeType [ ] ) . length ; if ( length ) { const value = ( shape as ShapeType [ ] ) [ Math . floor ( Math . random ( ) * length ) ] ; return generateShapeData ( value ) ; } return defaultShapeData ; } return generateShapeData ( shape as ShapeType ) ; } /**
     * 绘制形状
     */ protected drawShape ( data ) { const { type , isImageLoaded , source , sides , dent } = data . shape ; if ( validShapeTypes . indexOf ( type ) === - 1 ) { return ; } this . ctx . save ( ) ; if ( type === 'image' ) { if ( isImageLoaded ) { const width = data . r * 2 ; this . ctx . drawImage ( source ! , 0 , 0 , ( source ?. width as number ) || width , ( source ?. height as number ) || width , data . x - data . r , data . y - data . r , width , width ) ; } } else { this . ctx . beginPath ( ) ; switch ( data . shape . type ) { case 'circle' : this . ctx . arc ( data . x , data . y , data . r , 0 , doublePi ) ; break ; case 'triangle' : drawStar ( this . ctx , data . x , data . y , data . r , 3 , 0.5 ) ; break ; case 'star' : drawStar ( this . ctx , data . x , data . y , data . r , sides ! , dent ! ) ; break ; } this . ctx . fillStyle = data . color ; this . ctx . fill ( ) ; } this . ctx . restore ( ) ; } }