dataDynamicEffectHandler.ts 6.3 KB
import type { App } from 'vue'
import type { SubscriptionUpdateMsg } from '../type/message'
import type { CommandSource, LightboxModeWebsocketService } from '.'
import { getMeetTheConditionsRange } from '@/core/Library/utils'
import { ActRangListItemTypeEnum, ActTypeEnum } from '@/enums/datasource'
import { getAppInstanceId } from '@/utils/draw'
import type { RenderComponentExposeType } from '@/core/Library/types'
import type { DisplayActDataType, DynamicActDataType, FlashActDataType, RotateActDataType } from '@/api/node/model'
import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'

enum ActAnimationName {
  /**
   * @description 闪烁
   */
  FLASH = 'act-flash',

  /**
   * @description 旋转
   */
  ROTATE = 'act-spin',

  /**
   * @description 显示
   */
  VISIBLE = 'act-visible',

  /**
   * @description 隐藏
   */
  HIDDEN = 'act-hidden',

  /**
   * @description 流动
   */
  FLOW = 'water-flow-animation',
}

export class DataDynamicEffectHandler {
  constructor(public service: LightboxModeWebsocketService) {
  }

  get nodeUtils() {
    return this.service.nodeUtils
  }

  setNodeAnimation(node: SVGAElement, animationName: string, remove = false) {
    const dataSetSaveFields = 'actMultipleAnimationList'
    const animationNameListString = node.dataset?.[dataSetSaveFields] || ''
    const animationNameSet = new Set(animationNameListString.split(','))
    remove ? animationNameSet.delete(animationName) : animationNameSet.add(animationName)
    const animation = Array.from(animationNameSet.values()).filter(Boolean).join(',')
    node.setAttribute(`data-${dataSetSaveFields}`, animation)
    node.style.animation = animation
  }

  [ActTypeEnum.FLASH](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node, data } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const { attr, enable } = data as FlashActDataType
    if (!enable) return
    const { latestValue } = useLatestMessageValue(message.data, attr!)
    const { flag } = getMeetTheConditionsRange((data as FlashActDataType).rangeList, latestValue)

    const nodeEl = this.nodeUtils.getNodesForCells<SVGAElement>([cell])

    const FLASH_ANIMATION = `${ActAnimationName.FLASH} infinite 1.5s`
    nodeEl.forEach((item: SVGAElement) => {
      if (flag)
        this.setNodeAnimation(item, FLASH_ANIMATION)
      else
        this.setNodeAnimation(item, FLASH_ANIMATION, true)
    })
  }

  [ActTypeEnum.DISPLAY](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node, data } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const { attr, enable } = data as DisplayActDataType
    if (!enable) return
    const { latestValue } = useLatestMessageValue(message.data, attr!)
    const { flag, record } = getMeetTheConditionsRange((data as DisplayActDataType).rangeList, latestValue)
    if (flag) {
      const nodeEl = this.nodeUtils.getNodesForCells([cell])
      const { type } = record!

      if (type === ActRangListItemTypeEnum.SHOW) {
        nodeEl.forEach((node) => {
          node.classList.add(ActAnimationName.VISIBLE)
          node.classList.remove(ActAnimationName.HIDDEN)
        })
        this.nodeUtils.updateCellValue(cell, record?.title ? record.title : latestValue)
      }
      else if (type === ActRangListItemTypeEnum.HIDDEN) {
        nodeEl.forEach((node) => {
          node.classList.remove(ActAnimationName.VISIBLE)
          node.classList.add(ActAnimationName.HIDDEN)
        })
      }
    }
  }

  [ActTypeEnum.ROTATE](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node, data } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const { attr, enable } = data as RotateActDataType
    if (!enable) return
    const { latestValue } = useLatestMessageValue(message.data, attr!)
    const { flag } = getMeetTheConditionsRange((data as RotateActDataType).rangeList, latestValue)
    const nodeEl = this.nodeUtils.getNodesForCells<SVGAElement>([cell])

    const ROTATE_ANIMATION = `${ActAnimationName.ROTATE} infinite 2s`
    const cellState = this.nodeUtils.getCellState(cell)
    const { x, y, width, height } = cellState.cellBounds
    nodeEl.forEach((item: SVGAElement) => {
      if (flag) {
        item.style.transformOrigin = `${x + width / 2}px ${y + height / 2}px`
        this.setNodeAnimation(item, ROTATE_ANIMATION)
      }
      else {
        this.setNodeAnimation(item, ROTATE_ANIMATION, true)
      }
    })
  }

  [ActTypeEnum.DYNAMIC](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node, data } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const { attr, enable } = data as DynamicActDataType
    if (!enable) return
    const { latestValue } = useLatestMessageValue(message.data, attr!)
    const { flag, record } = getMeetTheConditionsRange((data as DynamicActDataType).rangeList, Number(latestValue))
    const nodeEl = this.nodeUtils.getNodeForCell<SVGAElement>(cell)
    if (flag) {
      const { type } = record!
      if (type === ActRangListItemTypeEnum.RUN) {
        const element = (nodeEl?.querySelectorAll('path')?.[1] as SVGPathElement)
        element.classList.add(ActAnimationName.FLOW)
        element.style.animationDuration = `${(record?.flowSpeed || 10) * 0.1}s`
      }
      else { (nodeEl?.querySelectorAll('path')?.[1] as SVGPathElement).classList.remove(ActAnimationName.FLOW) }
    }
  }

  [ActTypeEnum.STATUS_SETTING](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const instanceId = getAppInstanceId(cell)
    const instance = window?.VueInstanceMap?.[instanceId] as App
    if (instance)
      (instance._instance?.exposed as RenderComponentExposeType)?.onMessage?.(commandSource, message)
  }

  [ActTypeEnum.VARIABLE_IMAGE](commandSource: CommandSource, message: SubscriptionUpdateMsg) {
    const { node } = commandSource
    const cell = this.nodeUtils.getCellById(node)
    if (!cell) return
    const instanceId = getAppInstanceId(cell)
    const instance = window?.VueInstanceMap?.[instanceId] as App
    if (instance)
      (instance._instance?.exposed as RenderComponentExposeType)?.onMessage?.(commandSource, message)
  }
}