fileOverview author dxq613 gmail import { IGroup , IShape , IElement } from '@antv/g-base' ; import { ShapeOptions , ILabelConfig } from '../interface/shape' ; import { IPoint , Item , LabelStyle , ShapeStyle , ModelConfig , EdgeConfig , UpdateType } from '../types' ; import Global from '../global' ; import { ext } from '@antv/matrix-util' ; import { deepMix , each , mix , isBoolean , isPlainObject , clone } from '@antv/util' ; import { cloneBesidesImg } from '../util/graphic' ; const transform = ext . transform ; const CLS_SHAPE_SUFFIX = '-shape' ; const CLS_LABEL_SUFFIX = '-label' ; const ARROWS = [ 'startArrow' , 'endArrow' ] ; const SHAPE_DEFAULT_ATTRS = { lineWidth : 1 , stroke : undefined , fill : undefined , lineAppendWidth : 1 , opacity : undefined , strokeOpacity : undefined , fillOpacity : undefined , x : 0 , y : 0 , r : 10 , width : 20 , height : 20 , shadowColor : undefined , shadowBlur : 0 , shadowOffsetX : 0 , shadowOffsetY : 0 , } ; const PATH_SHAPE_DEFAULT_ATTRS = { lineWidth : 1 , stroke : '#000' , lineDash : undefined , startArrow : false , endArrow : false , opacity : undefined , strokeOpacity : undefined , fillOpacity : undefined , shadowColor : undefined , shadowBlur : 0 , shadowOffsetX : 0 , shadowOffsetY : 0 , } ; const SHAPES_DEFAULT_ATTRS = { edge : PATH_SHAPE_DEFAULT_ATTRS , node : SHAPE_DEFAULT_ATTRS , combo : SHAPE_DEFAULT_ATTRS , } ; export const CLS_LABEL_BG_SUFFIX = '-label-bg' ; export const shapeBase = { options : { labelCfg : { style : { fontFamily : Global . windowFontFamily } , } , descriptionCfg : { style : { fontFamily : Global . windowFontFamily } , } , } , itemType : '' , /**
     * 形状的类型，例如 circle，ellipse，polyline...
     */ type : '' , getCustomConfig ( cfg ) { return { } ; } , getOptions ( cfg , updateType ? ) { if ( updateType === 'move' || updateType ?. includes ( 'bbox' ) ) { return { } ; } return deepMix ( { } , this . options , this . getCustomConfig ( cfg ) || { } , cfg ) ; } , @override
     *  param { Object } cfg param { G . Group } group return { IShape } draw ( cfg , group ) { group [ 'shapeMap' ] = { } ; this . mergeStyle = this . getOptions ( cfg ) ; const shape = this . drawShape ! ( cfg , group ) ; shape . set ( 'className' , this . itemType + CLS_SHAPE_SUFFIX ) ; group [ 'shapeMap' ] [ this . itemType + CLS_SHAPE_SUFFIX ] = shape ; if ( cfg . label ) { const label = this . drawLabel ! ( cfg , group ) ; label . set ( 'className' , this . itemType + CLS_LABEL_SUFFIX ) ; group [ 'shapeMap' ] [ this . itemType + CLS_LABEL_SUFFIX ] = label ; } return shape ; } , param cfg param group param keyShape afterDraw ( cfg ? , group ? , keyShape ? ) { } , drawShape ( cfg ? , group ? ) { return null as any ; } , drawLabel ( cfg , group ) { const { labelCfg : defaultLabelCfg } = this . mergeStyle || this . getOptions ( cfg ) as ModelConfig || { } ; const labelCfg = ( defaultLabelCfg || { } ) as ILabelConfig ; const labelStyle = this . getLabelStyle ! ( cfg , labelCfg , group ) ; const rotate = labelStyle . rotate ; delete labelStyle . rotate ; const label = group . addShape ( 'text' , { attrs : labelStyle , draggable : true , className : 'text-shape' , name : 'text-shape' , labelRelated : true } ) ; group [ 'shapeMap' ] [ 'text-shape' ] = label ; if ( ! isNaN ( rotate ) && rotate !== '' ) { const labelBBox = label . getBBox ( ) ; let labelMatrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; if ( labelStyle . rotateCenter ) { switch ( labelStyle . rotateCenter ) { case 'center' : labelMatrix = transform ( labelMatrix , [ [ 't' , - labelBBox . width / 2 , - labelBBox . height / 2 ] , [ 'r' , rotate ] , [ 't' , labelBBox . width / 2 , labelBBox . height / 2 ] , ] ) ; break ; case 'lefttop' : labelMatrix = transform ( labelMatrix , [ [ 't' , - labelStyle . x ! , - labelStyle . y ! ] , [ 'r' , rotate ] , [ 't' , labelStyle . x , labelStyle . y ] , ] ) ; break ; case 'leftcenter' : labelMatrix = transform ( labelMatrix , [ [ 't' , - labelStyle . x ! , - labelStyle . y ! - labelBBox . height / 2 ] , [ 'r' , rotate ] , [ 't' , labelStyle . x , labelStyle . y ! + labelBBox . height / 2 ] , ] ) ; break ; default : labelMatrix = transform ( labelMatrix , [ [ 't' , - labelBBox . width / 2 , - labelBBox . height / 2 ] , [ 'r' , rotate ] , [ 't' , labelBBox . width / 2 , labelBBox . height / 2 ] , ] ) ; break ; } } else { labelMatrix = transform ( labelMatrix , [ [ 't' , - labelStyle . x ! , - labelStyle . y ! - labelBBox . height / 2 ] , [ 'r' , rotate ] , [ 't' , labelStyle . x , labelStyle . y ! + labelBBox . height / 2 ] , ] ) ; } label . setMatrix ( labelMatrix ) ; } if ( labelStyle . background ) { const rect = this . drawLabelBg ( cfg , group , label ) ; const labelBgClassname = this . itemType + CLS_LABEL_BG_SUFFIX ; rect . set ( 'classname' , labelBgClassname ) ; group [ 'shapeMap' ] [ labelBgClassname ] = rect ; label . toFront ( ) ; } return label ; } , drawLabelBg ( cfg , group , label ) { const { labelCfg : defaultLabelCfg } = this . options as ModelConfig ; const labelCfg = mix ( { } , defaultLabelCfg , cfg . labelCfg ) as ILabelConfig ; const style = this . getLabelBgStyleByPosition ( label , labelCfg ) ; const rect = group . addShape ( 'rect' , { name : 'text-bg-shape' , attrs : style , labelRelated : true } ) ; group [ 'shapeMap' ] [ 'text-bg-shape' ] = rect ; return rect ; } , getLabelStyleByPosition ( cfg , labelCfg ? , group ? ) { return { text : cfg . label as string } ; } , getLabelBgStyleByPosition ( label , labelCfg ? ) { return { } ; } , param cfg param labelCfg param group getLabelStyle ( cfg , labelCfg , group ) { const calculateStyle = this . getLabelStyleByPosition ! ( cfg , labelCfg , group ) ; const attrName = `${ this . itemType }Label` ; const defaultStyle = ( Global as any ) [ attrName ] ? ( Global as any ) [ attrName ] . style : null ; return { ... defaultStyle , ... calculateStyle , ... labelCfg . style } ; } , param cfg getShapeStyle ( cfg ) { return cfg . style ! ; } , @override
     *  param { Object } cfg param { G6 . Item } item update ( cfg , item , updateType ? ) { ( this as any ) . updateShapeStyle ( cfg , item , updateType ) ; ( this as any ) . updateLabel ( cfg , item , updateType ) ; } , updateShapeStyle ( cfg , item , updateType ? ) { const group = item . getContainer ( ) ; const shape = item . getKeyShape ( ) ; const shapeStyle = mix ( { } , shape . attr ( ) , cfg . style ) ; for ( const key in shapeStyle ) { const style = shapeStyle [ key ] ; if ( isPlainObject ( style ) ) { const subShape = group [ 'shapeMap' ] ?. [ key ] || group . find ( ( element ) => element . get ( 'name' ) === key ) ; subShape ?. attr ( style ) ; } else { shape . attr ( { [ key ] : style , } ) ; } } } , updateLabel ( cfg , item , updateType ? ) { if ( cfg . label || cfg . label === '' ) { const group = item . getContainer ( ) ; let { labelCfg = { } } = this . mergeStyle || this . getOptions ( { } , updateType ) as ModelConfig || { } ; const labelClassName = this . itemType + CLS_LABEL_SUFFIX ; const label = group [ 'shapeMap' ] [ labelClassName ] || group . find ( ele => ele . get ( 'className' ) === labelClassName ) ; const labelBgClassname = this . itemType + CLS_LABEL_BG_SUFFIX ; let labelBg = group [ 'shapeMap' ] [ labelBgClassname ] || group . find ( ele => ele . get ( 'className' ) === labelBgClassname ) ; if ( ! label ) { const newLabel = this . drawLabel ! ( cfg , group ) ; newLabel . set ( 'className' , labelClassName ) ; group [ 'shapeMap' ] [ labelClassName ] = newLabel ; } else { if ( ! updateType || updateType === 'bbox|label' || ( this . itemType === 'edge' && updateType !== 'style' ) ) { labelCfg = deepMix ( labelCfg , cfg . labelCfg ) ; } const calculateStyle = ( this as any ) . getLabelStyleByPosition ( cfg , labelCfg , group ) ; const cfgStyle = cfg . labelCfg ?. style ; const labelStyle = { ... calculateStyle , ... cfgStyle } ; const rotate = labelStyle . rotate ; delete labelStyle . rotate ; if ( ! isNaN ( rotate ) && rotate !== '' ) { let rotateMatrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; rotateMatrix = transform ( rotateMatrix , [ [ 't' , - labelStyle . x , - labelStyle . y ] , [ 'r' , rotate ] , [ 't' , labelStyle . x , labelStyle . y ] , ] ) ; labelStyle . matrix = rotateMatrix ; label . attr ( labelStyle ) ; } else { if ( label . getMatrix ( ) ?. [ 4 ] !== 1 ) { label . resetMatrix ( ) ; } label . attr ( labelStyle ) ; } if ( ! labelBg ) { if ( labelStyle . background ) { labelBg = this . drawLabelBg ( cfg , group , label ) ; labelBg . set ( 'classname' , labelBgClassname ) ; group [ 'shapeMap' ] [ labelBgClassname ] = labelBg ; label . toFront ( ) ; } } else if ( labelStyle . background ) { const calculateBgStyle = ( this as any ) . getLabelBgStyleByPosition ( label , labelCfg ) ; labelBg . attr ( calculateBgStyle ) ; } else { group . removeChild ( labelBg ) ; } } } } , afterUpdate ( cfg ? , item ? ) { } , @override
     *  param { String } name param { String | Boolean } value param { G6 . Item } item setState ( name , value , item ) { const shape = item . get ( 'keyShape' ) ; if ( ! shape || shape . destroyed ) return ; const type = item . getType ( ) ; const stateName = isBoolean ( value ) ? name : `${ name }:${ value }` ; const shapeStateStyle = this . getStateStyle ( stateName , item ) ; const itemStateStyle = item . getStateStyle ( stateName ) ; if ( ! itemStateStyle && ! shapeStateStyle ) { return ; } const styles = mix ( { } , itemStateStyle || shapeStateStyle ) ; const group = item . getContainer ( ) ; const keptAttrs = { x : 1 , y : 1 , cx : 1 , cy : 1 , matrix : 1 } ; if ( type === 'combo' ) { keptAttrs . r = 1 ; keptAttrs . width = 1 ; keptAttrs . height = 1 ; } if ( value ) { for ( const key in styles ) { const style = styles [ key ] ; if ( isPlainObject ( style ) && ! ARROWS . includes ( key ) ) { const subShape = group [ 'shapeMap' ] ?. [ key ] || group . find ( ( element ) => element . get ( 'name' ) === key ) ; subShape ?. attr ( style ) ; } else { shape . attr ( { [ key ] : style , } ) ; } } } else { const enableStatesStyle = cloneBesidesImg ( item . getCurrentStatesStyle ( ) ) ; const model = item . getModel ( ) ; const originStyle = mix ( { } , model . style , cloneBesidesImg ( item . getOriginStyle ( ) ) ) ; const keyShapeName = shape . get ( 'name' ) ; const shapeAttrs = shape . attr ( ) ; const keyShapeStyles = { } ; Object . keys ( shapeAttrs ) . forEach ( ( key ) => { if ( key === 'img' ) return ; const attr = shapeAttrs [ key ] ; if ( attr && typeof attr === 'object' ) { keyShapeStyles [ key ] = clone ( attr ) ; } else { keyShapeStyles [ key ] = attr ; } } ) ; const filtetDisableStatesStyle = { } ; for ( const p in styles ) { const style = styles [ p ] ; if ( isPlainObject ( style ) && ! ARROWS . includes ( p ) ) { const subShape = group [ 'shapeMap' ] [ p ] || group . find ( ele => ele . get ( 'name' ) === p ) ; if ( subShape ) { const subShapeStyles = cloneBesidesImg ( subShape . attr ( ) ) ; each ( style , ( v , key ) => { if ( p === keyShapeName && keyShapeStyles [ key ] && ! keptAttrs [ key ] ) { delete keyShapeStyles [ key ] ; const value = originStyle [ p ] [ key ] || SHAPES_DEFAULT_ATTRS [ type ] [ key ] ; shape . attr ( key , value ) ; } else if ( subShapeStyles [ key ] || subShapeStyles [ key ] === 0 ) { delete subShapeStyles [ key ] ; const value = originStyle [ p ] [ key ] || SHAPES_DEFAULT_ATTRS [ type ] [ key ] ; subShape . attr ( key , value ) ; } } ) ; filtetDisableStatesStyle [ p ] = subShapeStyles ; } } else { if ( keyShapeStyles [ p ] && ! keptAttrs [ p ] ) { delete keyShapeStyles [ p ] ; const value = originStyle [ p ] || ( originStyle [ keyShapeName ] ? originStyle [ keyShapeName ] [ p ] : undefined ) || SHAPES_DEFAULT_ATTRS [ type ] [ p ] ; shape . attr ( p , value ) ; } } } if ( ! keyShapeName ) { mix ( filtetDisableStatesStyle , keyShapeStyles ) ; } else { filtetDisableStatesStyle [ keyShapeName ] = keyShapeStyles ; } for ( const key in enableStatesStyle ) { if ( keptAttrs [ key ] ) continue ; const enableStyle = enableStatesStyle [ key ] ; if ( ! isPlainObject ( enableStyle ) || ARROWS . includes ( key ) ) { if ( ! keyShapeName ) { mix ( originStyle , { [ key ] : enableStyle , } ) ; } else { mix ( originStyle [ keyShapeName ] , { [ key ] : enableStyle , } ) ; delete originStyle [ key ] ; } delete enableStatesStyle [ key ] ; } } const originstyles = { } ; deepMix ( originstyles , originStyle , filtetDisableStatesStyle , enableStatesStyle ) ; let keyShapeSetted = false ; for ( const originKey in originstyles ) { const style = originstyles [ originKey ] ; if ( isPlainObject ( style ) && ! ARROWS . includes ( originKey ) ) { const subShape = group [ 'shapeMap' ] [ originKey ] || group . find ( ele => ele . get ( 'name' ) === originKey ) ; if ( subShape ) { if ( subShape . get ( 'type' ) === 'text' || subShape . get ( 'labelRelated' ) ) { delete ( style as any ) . x ; delete ( style as any ) . y ; delete ( style as any ) . matrix ; } if ( originKey === keyShapeName ) { if ( type === 'combo' ) { delete ( style as any ) . r ; delete ( style as any ) . width ; delete ( style as any ) . height ; } keyShapeSetted = true ; } subShape . attr ( style ) ; } } else if ( ! keyShapeSetted ) { const value = style || SHAPES_DEFAULT_ATTRS [ type ] [ originKey ] ; if ( type === 'combo' ) { if ( ! keyShapeName ) { shape . attr ( { [ originKey ] : value , } ) ; } } else { shape . attr ( { [ originKey ] : value , } ) ; } } } } } , param { string } name param { Item } item return { object } getStateStyle ( name , item ) { const model = item . getModel ( ) ; const type = item . getType ( ) ; const { stateStyles , style = { } } = this . getOptions ( model ) ; const modelStateStyle = model . stateStyles ? model . stateStyles [ name ] : stateStyles && stateStyles [ name ] ; if ( type === 'combo' ) { return clone ( modelStateStyle ) ; } return mix ( { } , style , modelStateStyle ) ; } , param { Object } cfg return { Array | null } getControlPoints ( cfg ) { return cfg . controlPoints ; } , param { Object } cfg return { Array | null } getAnchorPoints ( cfg ) { const anchorPoints = cfg ?. anchorPoints || this . getCustomConfig ( cfg ) ?. anchorPoints || this . options ?. anchorPoints ; return anchorPoints ; } , } ;