utils/particle/WaterGunEffect.js

import { Cesium } from '../../../namespace'
import {
  calculateAngle,
  changeHeight,
  transformCartesianToWGS84,
} from '../Coordinate'
import { getStaticFile, mergedObj } from '../Generate'
import { getDistanceTwo } from '../Measure'
/**
 * 水枪粒子
 * @class
 */
class WaterGunEffect {
  /**
   * 构造函数
   * @param {Object} viewer 地图场景对象
   * @param {Cartesian3} sourcePos 起始点位置,笛卡尔坐标
   * @param {Cartesian3} targetPos 目标点位置,笛卡尔坐标
   * @param {WaterGunOption} options 粒子参数
   */
  constructor(viewer, sourcePos, targetPos, options = {}) {
    this.viewer = viewer
    const pos0 = transformCartesianToWGS84(sourcePos)
    const pos1 = transformCartesianToWGS84(targetPos)
    const angleX = calculateAngle(pos0, pos1)
    const dis = getDistanceTwo(
      { x: pos0.x, y: pos0.y },
      { x: pos1.x, y: pos1.y }
    )
    const DEF_WATER_GUN = {
      angle: 30,
      gravity: (-2.0 * dis) / (pos0.z - pos1.z),
      speed: Math.ceil(dis / 5),
    }
    this.options = mergedObj(DEF_WATER_GUN, options)

    this.viewModel = {
      startScale: 1,
      endScale: 5,
      minimumParticleLife: 25,
      maximumParticleLife: 30,
      minimumSpeed: this.options.speed,
      maximumSpeed: this.options.speed + 1,
      emissionRate: 150,
    }

    this.emitterModelMatrix = new Cesium.Matrix4()
    this.translation = new Cesium.Cartesian3()
    this.rotation = new Cesium.Quaternion()
    this.hpr = new Cesium.HeadingPitchRoll()
    this.trs = new Cesium.TranslationRotationScale()
    this.scene = this.viewer.scene
    this.particleSystem = ''
    let center = new Cesium.Cartesian3()
    Cesium.Cartesian3.midpoint(sourcePos, targetPos, center)
    this.position = changeHeight(center, dis * 5)
    this.entity = this.viewer.entities.add({
      //选择粒子放置的坐标
      position: sourcePos,
    })

    this.angleX = angleX + 90
    this._gravity = options.gravity
    this.angleY = options.angle
    this.init()
  }

  /**
   * 发射角度
   * @type {Number}
   */
  get angle() {
    return this.angleY
  }
  set angle(val) {
    this.angleY = val
  }

  /**
   * 重力因子
   * @type {Number}
   */
  get gravity() {
    return this._gravity
  }
  set gravity(val) {
    this._gravity = val
  }

  init() {
    const _this = this
    this.viewer.clock.shouldAnimate = true
    var particleSystem = this.scene.primitives.add(
      new Cesium.ParticleSystem({
        image: getStaticFile('water1.png'), //生成所需粒子的图片路径
        //粒子在生命周期开始时的颜色
        startColor: new Cesium.Color(1, 1, 1, 0.6),
        //粒子在生命周期结束时的颜色
        endColor: new Cesium.Color(0.8, 0.86, 1, 0.4),
        //粒子在生命周期开始时初始比例
        startScale: _this.viewModel.startScale,
        //粒子在生命周期结束时比例
        endScale: _this.viewModel.endScale,
        //粒子发射的最小速度
        minimumParticleLife: _this.viewModel.minimumParticleLife,
        //粒子发射的最大速度
        maximumParticleLife: _this.viewModel.maximumParticleLife,
        //粒子质量的最小界限
        minimumSpeed: _this.viewModel.minimumSpeed,
        //粒子质量的最大界限
        maximumSpeed: _this.viewModel.maximumSpeed,
        //以像素为单位缩放粒子图像尺寸
        imageSize: new Cesium.Cartesian2(10, 10),
        //每秒发射的粒子数
        emissionRate: _this.viewModel.emissionRate,
        //粒子系统发射粒子的时间(秒)
        lifetime: 16.0,
        //设置粒子的大小是否以米或像素为单位
        sizeInMeters: true,
        //系统的粒子发射器
        emitter: new Cesium.CircleEmitter(2.0), //BoxEmitter 盒形发射器,ConeEmitter 锥形发射器,SphereEmitter 球形发射器,CircleEmitter圆形发射器
        //回调函数,实现各种喷泉、烟雾效果
        updateCallback: (p, dt) => {
          return this.applyGravity(p, dt)
        },
      })
    )
    this.particleSystem = particleSystem
    this.preUpdateEvent()
  }

  //场景渲染事件
  preUpdateEvent() {
    let _this = this
    this.viewer.scene.preUpdate.addEventListener(function (scene, time) {
      _this.particleSystem.modelMatrix = _this.computeModelMatrix(
        _this.entity,
        time
      )
      _this.particleSystem.emitterModelMatrix =
        _this.computeEmitterModelMatrix()
    })
  }

  computeModelMatrix(entity, time) {
    return entity.computeModelMatrix(time, new Cesium.Matrix4())
  }

  computeEmitterModelMatrix() {
    this.hpr = Cesium.HeadingPitchRoll.fromDegrees(
      this.angleX,
      this.angleY,
      0.0,
      this.hpr
    ) // 倾斜角度
    this.trs.translation = Cesium.Cartesian3.fromElements(
      0,
      0,
      0.1,
      this.translation
    ) // 发射高度
    this.trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(
      this.hpr,
      this.rotation
    )
    return Cesium.Matrix4.fromTranslationRotationScale(
      this.trs,
      this.emitterModelMatrix
    )
  }

  applyGravity(p, dt) {
    var gravityScratch = new Cesium.Cartesian3()
    var position = p.position
    Cesium.Cartesian3.normalize(position, gravityScratch)
    Cesium.Cartesian3.multiplyByScalar(
      gravityScratch,
      this.gravity * dt,
      gravityScratch
    )
    p.velocity = Cesium.Cartesian3.add(p.velocity, gravityScratch, p.velocity)
  }

  removeEvent() {
    this.viewer.scene.preUpdate.removeEventListener(this.preUpdateEvent, this)
    this.emitterModelMatrix = undefined
    this.translation = undefined
    this.rotation = undefined
    this.hpr = undefined
    this.trs = undefined
  }

  /**
   * 移除水枪粒子
   */
  remove() {
    ;() => {
      return this.removeEvent()
    } //清除事件
    this.viewer.scene.primitives.remove(this.particleSystem) //删除粒子对象
    this.viewer.entities.remove(this.entity) //删除entity
  }
}
export default WaterGunEffect