import Base from '@src/common/base' ; import { CommonConfig } from '@src/types/common-config' ; import { isString , loadImage , upperFirst } from '@src/utils' ; export type modeMethodNames = 'modeNormal' | 'modeGhost' ; export default abstract class Mask < Options > extends Base < Options > { protected maskImage ? : CanvasImageSource ; protected readonly options ! : Options & CommonConfig & { mask ? : string | CanvasImageSource ; maskMode ? : 'normal' | 'ghost' ; } ; private completedMap : { [ key ] : HTMLImageElement ; } = { } ; TODO protected loadMaskImage ( ) { const mask = this . options . mask ; if ( ! mask ) return ; if ( isString ( mask ) ) { if ( this . completedMap [ mask as string ] ) { this . maskImage = this . completedMap [ mask as string ] ; return ; } loadImage ( mask as string , ( image ) => { this . completedMap [ mask as string ] = image ; this . maskImage = image ; } ) ; } else { this . maskImage = mask as CanvasImageSource ; } } protected renderMaskMode ( mainDrawing ) { if ( ! this . maskImage ) { mainDrawing ( ) ; return ; } const modeName = this . options . maskMode || 'normal' ; this . ctx . save ( ) ; this [ `mode${ upperFirst ( modeName ) }` as modeMethodNames ] ( mainDrawing ) ; this . ctx . restore ( ) ; } /**
     * 常规遮罩或无遮罩模式
     */ private modeNormal ( mainDrawing ) { this . drawMaskImage ( ) ; this . ctx . globalCompositeOperation = 'source-atop' ; mainDrawing ( ) ; } /**
     * 幽灵遮罩模式：
     *   1、用遮罩图片生成灰色背景
     *   2、用波纹 clip 出原始遮罩图片
     */ private modeGhost ( mainDrawing ) { this . ctx . save ( ) ; this . ctx . filter = 'grayscale(100%)' ; this . drawMaskImage ( ) ; this . ctx . restore ( ) ; this . ctx . globalCompositeOperation = 'source-atop' ; mainDrawing ( ) ; this . ctx . clip ( ) ; this . drawMaskImage ( ) ; } /**
     * 绘制遮罩图案，遮罩图像填充模式为 contain 且居中
     */ protected drawMaskImage ( ) { if ( ! this . maskImage ) return ; const { ctx , canvasWidth , canvasHeight , maskImage } = this ; const originWidth = maskImage . width as number ; const originHeight = maskImage . height as number ; const imgScale = originWidth / originHeight ; let width = originWidth > canvasWidth ? canvasWidth : originWidth ; let height = originHeight > canvasHeight ? canvasHeight : originHeight ; if ( originWidth > originHeight ) { height = width / imgScale ; } else { width = height * imgScale ; } const x = ( canvasWidth - width ) / 2 ; const y = ( canvasHeight - height ) / 2 ; ctx . drawImage ( maskImage , 0 , 0 , originWidth , originHeight , x , y , width , height ) ; } }