UI: Add css animations to SCADA symbol API.
This commit is contained in:
parent
f80a62ad11
commit
5be11f439b
@ -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, SVG, Svg, Text, Timeline } from '@svgdotjs/svg.js';
|
||||
import '@svgdotjs/svg.panzoom.js';
|
||||
import {
|
||||
DataToValueType,
|
||||
@ -58,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 {
|
||||
@ -621,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: {},
|
||||
@ -1008,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user