@ -15,7 +15,7 @@
///
import { ValueType } from '@shared/models/constants' ;
import { Box , Element , Runner , SVG , Svg , Text , Timeline } from '@svgdotjs/svg.js' ;
import { Box , Element , Runner , Style , S VG , Svg , Text , Timeline } from '@svgdotjs/svg.js' ;
import '@svgdotjs/svg.panzoom.js' ;
import {
DataToValueType ,
@ -41,7 +41,6 @@ import { ColorProcessor, constantColor, Font } from '@shared/models/widget-setti
import { AttributeScope } from '@shared/models/telemetry/telemetry.models' ;
import { UtilsService } from '@core/services/utils.service' ;
import { WidgetAction , WidgetActionType , widgetActionTypeTranslationMap } from '@shared/models/widget.models' ;
import { ResizeObserver } from '@juggle/resize-observer' ;
import { catchError , map , take , takeUntil } from 'rxjs/operators' ;
import { isSvgIcon , splitIconName } from '@shared/models/icon.models' ;
import { MatIconRegistry } from '@angular/material/icon' ;
@ -59,6 +58,7 @@ export interface ScadaSymbolApi {
enable : ( element : Element | Element [ ] ) = > void ;
callAction : ( event : Event , behaviorId : string , value? : any , observer? : Partial < Observer < void > > ) = > void ;
setValue : ( valueId : string , value : any ) = > void ;
cssAnimate : CssScadaSymbolAnimations ;
}
export interface ScadaSymbolContext {
@ -622,7 +622,8 @@ export class ScadaSymbolObject {
disable : this.disableElement.bind ( this ) ,
enable : this.enableElement.bind ( this ) ,
callAction : this.callAction.bind ( this ) ,
setValue : this.setValue.bind ( this )
setValue : this.setValue.bind ( this ) ,
cssAnimate : new CssScadaSymbolAnimations ( this . svgShape )
} ,
tags : { } ,
properties : { } ,
@ -1009,3 +1010,301 @@ export class ScadaSymbolObject {
}
}
}
const scadaSymbolAnimationId = 'scadaSymbolAnimation' ;
class CssScadaSymbolAnimations {
constructor ( private svgShape : Svg ) { }
public rotate ( element : Element , rotation : number , duration = 1000 , loop = false ) : CssScadaSymbolAnimation {
this . checkOldAnimation ( element ) ;
return this . setupAnimation ( element ,
new RotateCssScadaSymbolAnimation ( this . svgShape , element , loop , rotation || 0 ) ) . duration ( duration ) ;
}
public move ( element : Element , deltaX : number , deltaY : number , duration = 1000 , loop = false ) : CssScadaSymbolAnimation {
this . checkOldAnimation ( element ) ;
return this . setupAnimation ( element ,
new MoveCssScadaSymbolAnimation ( this . svgShape , element , loop , deltaX , deltaY ) . duration ( duration ) ) ;
}
public attr ( element : Element , attrName : string , value : any , duration = 1000 , loop = false ) : CssScadaSymbolAnimation {
this . checkOldAnimation ( element ) ;
return this . setupAnimation ( element ,
new AttrsCssScadaSymbolAnimation ( this . svgShape , element , loop , { [ attrName ] : value } ) . duration ( duration ) ) ;
}
public attrs ( element : Element , attr : any , duration = 1000 , loop = false ) : CssScadaSymbolAnimation {
this . checkOldAnimation ( element ) ;
return this . setupAnimation ( element ,
new AttrsCssScadaSymbolAnimation ( this . svgShape , element , loop , attr ) . duration ( duration ) ) ;
}
public animation ( element : Element ) : CssScadaSymbolAnimation | undefined {
return element . remember ( scadaSymbolAnimationId ) ;
}
private checkOldAnimation ( element : Element ) {
const previousAnimation : CssScadaSymbolAnimation = element . remember ( scadaSymbolAnimationId ) ;
if ( previousAnimation ) {
previousAnimation . destroy ( ) ;
}
}
private setupAnimation ( element : Element , animation : CssScadaSymbolAnimation ) : CssScadaSymbolAnimation {
animation . init ( ) ;
element . remember ( scadaSymbolAnimationId , animation ) ;
return animation ;
}
}
interface ScadaSymbolAnimationKeyframe {
stop : string ;
style : any ;
}
abstract class CssScadaSymbolAnimation {
private _animationName : string ;
private _animationStyle : Style ;
private _running = true ;
private _speed = 1 ;
private _duration = 1000 ;
private _easing = 'linear' ;
protected constructor ( protected svgShape : Svg ,
protected element : Element ,
protected loop : boolean ) {
}
public init() {
this . prepareAnimation ( ) ;
}
public start() {
if ( ! this . _running ) {
this . updateAnimationStyle ( 'animation-play-state' , 'running' ) ;
this . _running = true ;
}
}
public pause() {
if ( this . _running ) {
this . updateAnimationStyle ( 'animation-play-state' , 'paused' ) ;
this . _running = false ;
}
}
public running ( ) : boolean {
return this . _running ;
}
public destroy() {
if ( this . _animationStyle ) {
this . _animationStyle . remove ( ) ;
this . element . removeClass ( this . _animationName ) ;
this . _animationStyle = null ;
this . _animationName = null ;
}
}
public duration ( duration : number ) : CssScadaSymbolAnimation {
this . _duration = duration ;
this . updateAnimationStyle ( this . loop ? 'animation-duration' : 'transition-duration' ,
Math . round ( this . _duration / this . _speed ) + 'ms' ) ;
return this ;
}
public speed ( speed : number ) : CssScadaSymbolAnimation {
this . _speed = speed ;
this . updateAnimationStyle ( this . loop ? 'animation-duration' : 'transition-duration' ,
Math . round ( this . _duration / this . _speed ) + 'ms' ) ;
return this ;
}
public easing ( easing : string ) : CssScadaSymbolAnimation {
this . _easing = easing ;
this . updateAnimationStyle ( this . loop ? 'animation-timing-function' : 'transition-timing-function' , this . _easing ) ;
return this ;
}
private prepareAnimation() {
this . _animationName = 'animation_' + generateElementId ( ) ;
this . _animationStyle = this . svgShape . style ( ) ;
let styles : any ;
if ( this . loop ) {
styles = {
'animation-name' : this . _animationName ,
'animation-duration' : this . _duration + 'ms' ,
'animation-timing-function' : this . _easing ,
'animation-iteration-count' : 'infinite' ,
'animation-fill-mode' : 'forwards' ,
'animation-play-state' : 'running' ,
. . . this . animationStyles ( )
} ;
} else {
styles = {
'transition-property' : this . transitionProperties ( ) ,
'transition-duration' : this . _duration + 'ms' ,
'transition-timing-function' : this . _easing ,
. . . this . animationStyles ( )
} ;
}
this . _animationStyle . rule ( '.' + this . _animationName , styles ) ;
if ( this . loop ) {
const keyframes = this . animationKeyframes ( ) ;
let keyframesCss = ` \ n@keyframes ${ this . _animationName } { \ n ` ;
for ( const keyframe of keyframes ) {
let keyframeCss = ` ${ keyframe . stop } { \ n ` ;
for ( const i of Object . keys ( keyframe . style ) ) {
keyframeCss += ' ' + i + ':' + keyframe . style [ i ] + ';\n' ;
}
keyframeCss += ' }\n' ;
keyframesCss += keyframeCss ;
}
keyframesCss += '}' ;
this . _animationStyle . addText ( keyframesCss ) ;
}
this . element . addClass ( this . _animationName ) ;
if ( ! this . loop ) {
this . doTransform ( ) ;
}
}
private updateAnimationStyle ( attrName : string , value : any ) {
if ( this . _animationStyle ) {
const styleText = this . _animationStyle . node . innerHTML ;
const attrValueRegex = new RegExp ( ` ${ attrName } :([^;]+); ` ) ;
this . _animationStyle . node . innerHTML = styleText . replace ( attrValueRegex , ` ${ attrName } : ${ value } ; ` ) ;
}
}
protected animationStyles ( ) : any {
return { } ;
}
protected abstract animationKeyframes ( ) : ScadaSymbolAnimationKeyframe [ ] ;
protected abstract transitionProperties ( ) : string ;
protected doTransform() {
}
}
class RotateCssScadaSymbolAnimation extends CssScadaSymbolAnimation {
constructor ( protected svgShape : Svg ,
protected element : Element ,
protected loop : boolean ,
private rotation : number ) {
super ( svgShape , element , loop ) ;
}
protected animationStyles ( ) : any {
return {
'transform-origin' : ` ${ this . element . cx ( ) } px ${ this . element . cy ( ) } px `
} ;
}
protected animationKeyframes ( ) : ScadaSymbolAnimationKeyframe [ ] {
const transform = this . element . transform ( ) ;
return [
{
stop : '0%' ,
style : {
transform : ` translate( ${ transform . translateX } px, ${ transform . translateY } px) rotate( ${ transform . rotate } deg) `
}
} ,
{
stop : '100%' ,
style : {
transform : ` translate( ${ transform . translateX } px, ${ transform . translateY } px) rotate( ${ this . rotation } deg) `
}
}
] ;
}
protected transitionProperties ( ) : string {
return 'transform' ;
}
protected doTransform() {
const transform = this . element . transform ( ) ;
this . element . attr ( { transform : ` translate( ${ transform . translateX } ${ transform . translateY } ) rotate( ${ this . rotation } ) ` } ) ;
}
}
class MoveCssScadaSymbolAnimation extends CssScadaSymbolAnimation {
constructor ( protected svgShape : Svg ,
protected element : Element ,
protected loop : boolean ,
private deltaX : number ,
private deltaY : number ) {
super ( svgShape , element , loop ) ;
}
protected animationStyles ( ) : any {
return {
'transform-origin' : ` ${ this . element . cx ( ) } px ${ this . element . cy ( ) } px `
} ;
}
protected animationKeyframes ( ) : ScadaSymbolAnimationKeyframe [ ] {
const transform = this . element . transform ( ) ;
return [
{
stop : '0%' ,
style : {
transform : ` translate( ${ transform . translateX } px, ${ transform . translateY } px) `
}
} ,
{
stop : '100%' ,
style : {
transform : ` translate( ${ transform . translateX + this . deltaX } px, ${ transform . translateY + this . deltaY } px) `
}
}
] ;
}
protected transitionProperties ( ) : string {
return 'transform' ;
}
protected doTransform() {
this . element . relative ( this . deltaX , this . deltaY ) ;
}
}
class AttrsCssScadaSymbolAnimation extends CssScadaSymbolAnimation {
constructor ( protected svgShape : Svg ,
protected element : Element ,
protected loop : boolean ,
private attr : any ) {
super ( svgShape , element , loop ) ;
}
protected animationStyles ( ) : any {
return { } ;
}
protected animationKeyframes ( ) : ScadaSymbolAnimationKeyframe [ ] {
return [ ] ;
}
protected transitionProperties ( ) : string {
return Object . keys ( this . attr ) . join ( ' ' ) ;
}
protected doTransform() {
this . element . attr ( this . attr ) ;
}
}