Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/frontend/vue/components/AmplitudeDisk/AmplitudeArrow.vue
3375 views
<template>
  <div
    v-if="isInteractive || internalAmplitude.magnitude > 0.01"
    class="amplitude-arrow"
    :style="`--magnitude: ${internalAmplitude.magnitude}; --phase: ${internalAmplitude.phase}`"
  >
    <div ref="origin" class="amplitude-arrow__origin-reference" />
    <div class="amplitude-arrow__line" />
    <div
      class="amplitude-arrow__head"
      :class="{
        'amplitude-arrow__head__grabbable': isInteractive,
        'amplitude-arrow__head__grabbing': isGrabbing
      }"
      @pointerdown="startGrabbingArrow"
      @pointerup="stopGrabbingArrow"
      @pointermove="grabArrow"
    />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue-demi'
import { Point } from '@mathigon/euclid'
import { Amplitude } from './amplitude'

export default defineComponent({
  name: 'AmplitudeArrow',
  components: {
  },
  props: {
    amplitude: {
      type: Object,
      required: false,
      default: () => {
        return { phase: 30, magnitude: 1 }
      }
    },
    isInteractive: {
      type: Boolean,
      required: false,
      default: false
    }
  },
  data () {
    return {
      isGrabbing: false,
      center: new Point(0, 0),
      internalAmplitude: { phase: 30, magnitude: 1 }
    }
  },
  watch: {
    amplitude (newVal) {
      this.internalAmplitude = newVal
    }
  },
  mounted () {
    this.internalAmplitude = this.amplitude as Amplitude
  },
  methods: {
    startGrabbingArrow () {
      this.isGrabbing = this.isInteractive
    },
    stopGrabbingArrow () {
      this.isGrabbing = false
    },
    grabArrow (evt: any) {
      if (this.isGrabbing) {
        const origin = this.$refs.origin as HTMLDivElement
        const originClientRect = origin.getBoundingClientRect()
        this.center = new Point(originClientRect.x, originClientRect.y)

        const dragPosition = new Point(evt.x, evt.y)
        const vector = dragPosition.subtract(this.center)
        const vectorUnit = vector.unitVector

        this.internalAmplitude.phase = (Math.atan2(vectorUnit.x, vectorUnit.y) * 180 / Math.PI) - 90
        if (this.internalAmplitude.phase < 0) {
          this.internalAmplitude.phase += 360
        }
        this.internalAmplitude.magnitude = Math.max(Math.min(vector.length / 50, 1), 0)
        this.$emit('updateAmplitude', this.internalAmplitude)
      }
    }
  }
})
</script>

<style lang="scss" scoped>
@import 'carbon-components/scss/globals/scss/typography';
@import '~/../scss/variables/colors.scss';

.amplitude-arrow {
  transform: translate(-50%, -50%) rotate(calc(var(--phase, 30) * -1deg)) translate(50%, 0);
  width: calc(var(--magnitude, 1) * 50%);
  height: 0px;
  pointer-events: none;

  &__line {
    position: absolute;
    width: calc(100% - 5px);
    height: calc(var(--arrow-thickness, 1) * 1px);
    top: calc(var(--arrow-thickness, 1) * -0.5px);
    background-color: $block-border-color;
    transition: all 0.2s ease-in;
  }
  &__head {
    position: absolute;
    right: -1.5rem;
    top: 50%;
    width: 3rem;
    height: 3rem;
    transform: translate(0%, -50%);

    &::after {
      content: "";
      display: block;
      position: absolute;
      transform: translate(0%, -2.5px);
      top: 50%;
      right: 50%;
      width: 0;
      height: 0;

      transition: all 0.2s ease-in;
      border-top: 2.5px solid transparent;
      border-bottom: 2.5px solid transparent;

      border-left: 6px solid $block-border-color;
    }

    &__grabbable {
      cursor: grab;
      pointer-events: auto;
      touch-action: none;

      &:hover::after {
        transform: translate(4px, -6px);
        border-top-width: 6.5px;
        border-bottom-width: 6.5px;

        border-left-width: 14px;
      }
    }

    &__grabbing {
      cursor: grabbing;
    }
  }
  &__origin-reference {
    width: 0;
    height: 0;
    position: absolute;
    display: block;
    left: 0;
    top: 50%;
  }
}
</style>