import { Base } from '@components/canvas/Base' ; import { MiBrushAttrs , MiBrushAttrsConfig , Tools } from '@components/canvas/Tools' ; import { Utils } from '@components/canvas/Utils' ; import { Canvas } from '@components/canvas/Canvas' ; import { Marker } from '@components/canvas/tools/Marker' ; import { Selection } from '@components/canvas/tools/Selection' ; import { Layer , MiLayerData } from '@components/canvas/Layer' ; import { Cursor } from '@components/canvas/Cursor' ; import { Drag } from '@components/canvas/tools/Drag' ; import { Rect } from '@components/canvas/tools/Rect' ; import { Circle } from '@components/canvas/tools/Circle' ; import { Line } from '@components/canvas/tools/Line' ; import { Eraser } from '@components/canvas/tools/Eraser' ; import { Text } from '@components/canvas/tools/Text' ; import { Arrow } from '@components/canvas/tools/Arrow' ; import { Laser } from '@components/canvas/tools/Laser' ; import { Screenshot } from '@components/canvas/tools/Screenshot' ; import { MiRectConfig } from '@components/canvas/shapes/Rect' ; import { MiPointConfig } from '@components/canvas/Point' ; declare global { interface Window { PointerEvent : typeof PointerEvent ; } } /** 记录 stages */ export const stages = { } ; /** 初始化画布所支持属性 */ export interface MiStageConfig { container : string | HTMLDivElement ; width ? : number ; height ? : number ; buffer ? : boolean ; reset ? : boolean ; background ? : string ; } /** 操作踪迹 */ export interface MiStageTracesConfig { operation ? : string ; index ? : number ; indexes ? : any [ ] ; move ? : MiPointConfig ; active ? : { attrs : MiBrushAttrs ; rect : MiRectConfig ; } ; old ? : { attrs : MiBrushAttrs ; rect : MiRectConfig ; } ; } /** 集成 Base 基类 */ export abstract class MiStage extends Base { protected abstract register ( tools ) ; } export class Stage extends MiStage { id : string ; index = 0 ; container : HTMLDivElement ; width : number ; height : number ; background = '#f6f6f6' ; transparent = false ; tool = 'selection' ; tools : { [ index ] : any ; } = { } ; canvas : Canvas ; cursor ! : Cursor ; layer ! : Layer ; layers : { [ index ] : Layer ; } = { } ; traces : any [ ] = [ ] ; recoveries : any [ ] = [ ] ; protected events : any [ ] = [ ] ; param config param index constructor ( config , index ? ) { super ( ) ; this . id = Utils . uid ( ) ; this . container = ( config . container instanceof HTMLDivElement ? config . container : document . getElementById ( config . container as string ) ) as HTMLDivElement ; this . width = config . width ?? this . container . offsetWidth ?? 0 ; this . height = config . height ?? this . container . offsetHeight ?? 0 ; /** index */ this . index = 1 ; if ( index ) { this . index = index ; } else { const keys = Object . keys ( stages ) ; if ( keys . length > 0 ) { if ( config . buffer ) this . index = Tools . getStage ( ) . index ; else this . index = stages [ keys [ keys . length - 1 ] ] . index + 1 ; } } /** canvas */ this . canvas = new Canvas ( { width : this . width , height : this . height , background : config . background ?? this . background } ) ; this . container . appendChild ( this . canvas . getCanvas ( ) ) ; /** 清空 stages */ if ( config . reset ) this . reset ( ) ; /** 离屏渲染画布 (不加入画布列表, 避免重复注册事件) */ if ( ! config . buffer ) { /** 注册工具 */ this . registerTools ( ) ; /** 注册事件 */ this . registerEvents ( ) ; /** 光标 */ this . cursor = new Cursor ( { container : this . container } ) ; /** 记录 */ stages [ this . id ] = this ; /** 设置选中画布 */ Tools . setStage ( this ) ; } } param id get ( id ? ) { return id ? stages [ id ] : this ; } param layer add ( layer ) { this . layer = layer ; this . layers [ layer . id ] = layer ; /** 记录当前所属图层 */ Tools . setLayer ( this . layer ) ; } param id remove ( id ) { delete this . layers [ id ] ; this . draw ( true ) ; } param id state ( id ) { if ( this . layers [ id ] ) this . layers [ id ] . hidden = ! this . layers [ id ] . hidden ; this . draw ( true ) ; } param reset param ctx draw ( reset = false , ctx ? ) { this . canvas . clear ( ) ; const keys = Object . keys ( this . layers ) , len = keys . length ; for ( let i = 0 ; i < len ; i ++ ) { const cur = this . layers [ keys [ i ] ] as Layer ; if ( reset ) cur . index = null ; cur . draw ( ctx ) ; } } param selection delete ( selection ) { const ids = [ ] ; if ( Array . isArray ( selection ) ) { for ( let i = 0 , len = selection . length ; i < len ; i ++ ) { if ( stages [ selection [ i ] ] ) { ids . push ( stages [ selection [ i ] ] . canvas . id ) ; delete stages [ selection [ i ] ] ; } } } else { ids . push ( stages [ selection ] . canvas . id ) ; delete stages [ selection ] ; } return ids ; } param tools protected register ( tools ) { tools . register ( this . container ) ; this . tools [ tools . name ] = tools ; } param event param callback on ( event , callback ? ) { if ( this . container && this . container instanceof HTMLDivElement ) { let binding = false ; for ( let i = 0 , len = this . events . length ; i < len ; i ++ ) { const cur = this . events [ i ] ; if ( cur . event === event && cur . element === this . container ) { binding = true ; break ; } } if ( ! binding ) { const ev = { element : this . container , event , func : callback } ; this . events . push ( ev ) ; Utils . on ( ev . element , event , ( evt ) => { const active = this . getActiveTool ( ) , tool = this . tools [ active ] as Tools ; if ( Tools . getStage ( ) . id === this . id && tool ) { if ( tool . name === active ) { /** 触发工具事件 */ tool . fireEvent ( event , evt ) ; } else if ( callback ) { /** 注册新工具&事件 */ tool . registerEvent ( event , callback ) ; tool . register ( ev . element ) ; } } } ) ; } } } param event param callback off ( event , callback ? ) { for ( let i = 0 , len = this . events . length ; i < len ; i ++ ) { const cur = this . events [ i ] ; if ( cur . event === event && cur . element === this . container ) { Utils . off ( cur . element , cur . event , cur . func ) ; delete this . events [ i ] ; } } } return protected reset ( ) { for ( const i in stages ) { delete stages [ i ] ; } } return thumbnail ( ) { const image = this . canvas . toDataURL ( ) , element = document . getElementById ( this . id ) ; if ( element ) { element . style . backgroundSize = 'cover' ; element . style . backgroundPosition = 'no-repeat' ; element . style . backgroundImage = `url(${ image })` ; if ( this . transparent ) element . style . backgroundColor = 'transparent' ; else if ( this . background ) element . style . backgroundColor = this . background ; } } return getActiveTool ( ) { return this . tool ; } param tool setActiveTool ( tool ) { if ( ! tool ) return ; this . tool = tool . toLowerCase ( ) ; } param attrs setActiveToolAttrs ( attrs ) { const tool = this . tools [ this . tool ] as Tools ; if ( tool ) { for ( const i in attrs ) { if ( Object . prototype . hasOwnProperty . call ( attrs , i ) ) { ( tool as any ) [ i ] = ( attrs as any ) [ i ] ; } } } } return protected registerTools ( ) { this . register ( new Drag ( this . canvas ) ) ; this . register ( new Selection ( this . canvas ) ) ; this . register ( new Marker ( this . canvas ) ) ; this . register ( new Laser ( this . canvas ) ) ; this . register ( new Line ( this . canvas ) ) ; this . register ( new Arrow ( this . canvas ) ) ; this . register ( new Circle ( this . canvas ) ) ; this . register ( new Rect ( this . canvas ) ) ; this . register ( new Text ( this . canvas ) ) ; this . register ( new Eraser ( this . canvas ) ) ; this . register ( new Screenshot ( this . canvas ) ) ; } param index getStage ( index ) { if ( stages [ index ] ) return stages [ index ] ; else { const keys = Object . keys ( stages ) ; return stages [ keys [ index as number ] ] ; } } return getStages ( ) { const ids = Object . keys ( stages ) , data = [ ] ; for ( let i = 0 , len = ids . length ; i < len ; i ++ ) { const stage = stages [ ids [ i ] ] ; data . push ( { id : stage . id , background : stage . background } ) ; } return data ; } param x param y param repaint updateTransformOrigin ( x , y , repaint = false ) { const keys = Object . keys ( this . layers ) , len = keys . length ; if ( repaint ) this . canvas . clear ( ) ; for ( let i = 0 ; i < len ; i ++ ) { const cur = this . layers [ keys [ i ] ] as Layer ; for ( let k = 0 , l = cur . data . length ; k < l ; k ++ ) { const active = cur . data [ k ] as MiLayerData ; active . origin . x += x ; active . origin . y += y ; if ( repaint && active . visible ) { active . draw ( { rect : active . rect , move : active . move , scale : active . scale , rotate : active . rotate , origin : active . origin , ctx : this . canvas . getContext ( ) } ) ; } } } } param color updateBackgroundColor ( color ) { const canvas = this . canvas . getCanvas ( ) ; if ( color === null ) { this . transparent = true ; canvas . style . backgroundColor = 'white' ; canvas . style . backgroundImage = 'linear-gradient(45deg, #e8eaed 26%, transparent 25%, transparent 75%, #e8eaed 75%, #e8eaed), linear-gradient(45deg, #e8eaed 26%, transparent 25%, transparent 75%, #e8eaed 75%, #e8eaed)' ; canvas . style . backgroundPosition = '-6px -6px, 10px 10px' ; canvas . style . backgroundSize = '32px 32px' ; canvas . style . boxSizing = 'border-box' ; } else { this . transparent = false ; this . background = color ; canvas . style . background = color ; } this . thumbnail ( ) ; } return protected registerEvents ( ) { if ( window . PointerEvent ) { this . pointerEvents ( ) ; } else { this . mouseEvents ( ) ; if ( 'ontouchstart' in window ) this . touchEvents ( ) ; } } return protected pointerEvents ( ) { this . on ( 'pointerdown' ) ; this . on ( 'pointermove' ) ; this . on ( 'pointerup' ) ; this . on ( 'pointerenter' ) ; this . on ( 'pointerleave' ) ; } return protected mouseEvents ( ) { this . on ( 'mousedown' ) ; this . on ( 'mousemove' ) ; this . on ( 'mouseup' ) ; this . on ( 'mouseenter' ) ; this . on ( 'mouseleave' ) ; } return protected touchEvents ( ) { this . on ( 'touchstart' ) ; this . on ( 'touchmove' ) ; this . on ( 'touchend' ) ; } }