Skip to content
Snippets Groups Projects
PanZoomContainer.vue 6.03 KiB
<template>
  <div
    v-if="mode == 'move'"
    ref="container"
    class="outer"
    v-on:wheel.capture="onWheel"
    v-on:touchstart.passive="onTouchStart"
    v-on:mousedown="onMouseDown"
    v-on:touchmove="onTouchMove"
    v-on:mousemove="onMouseMove"
    v-on:touchend.passive.capture="onTouchEnd"
    v-on:mouseup.passive.capture="onMouseUp"
    v-on:click.capture="handleEventCapture"
    v-on:touchend.capture="handleEventCapture"
  >
    <div
      ref="innerContainer"
      v-bind:class="{ inner: true, active: true }"
      v-bind:style="{
        width: `${width}px`,
        height: `${height}px`,
        transform: `translate(${translation.x}px, ${translation.y}px) scale(${scale})`,
      }"
    >
      <!-- {{ JSON.stringify(interaction) }} -->
      <!-- view paramaters not being utilised ? -->
      <!-- {{ mode }} -->
      <slot />
    </div>
  </div>

  <div v-else ref="container" class="outer">
    <!-- <div
      ref="innerContainer"
      v-bind:class="{ inner: true, active: true }"
      v-bind:style="{
        width: `${width}px`,
        height: `${height}px`,
        transform: `translate(${translation.x}px, ${translation.y}px) scale(${scale})`,
      }"
    > -->

    <div
      ref="innerContainer"
      v-bind:class="{ inner: true, active: true }"
      v-bind:style="{
        width: `${width}px`,
        height: `${height}px`,
        transform: `translate(${translation.x}px, ${translation.y}px) scale(${scale})`,
      }"
    >
      <slot />
    </div>
  </div>
</template>
<script>
import { mapState } from 'vuex'
import { constrainTranslation } from '@/experimental/utils/numbers'
import {
 // getNormalisedInteraction,
  changeViewFromWheelEvent,
  changeViewFromMultiTouchEvent,
} from '@/experimental/utils/view'

export default {
  name: 'map-interaction',
  data() {
    return {
      shouldPreventTouchEndDefault: false,
    }
  },
  computed: {
    ...mapState({
      interaction: (state) => state.ui.interaction,
      mode: (state) => state.ui.mode,
    }),
  },
  props: {
    translationBounds: {
      type: Object,
      default() {
        return { xMin: -500, xMax: 500, yMin: -500, yMax: 500 }
      },
    },
    translation: Object,
    scale: Number,
    width: Number,
    height: Number,
    minScale: {
      type: Number,
      default: 0.3,
    },
    maxScale: {
      type: Number,
      default: 2.0,
    },
  },
  methods: {
    handleEventCapture(e) {
      if (this.shouldPreventTouchEndDefault) {
        e.preventDefault()
        this.shouldPreventTouchEndDefault = false
      }

      // const { relativePoint, boardPoint } = getNormalisedInteraction(
      //   this.$refs.container,
      //   e,
      //   this.translation,
      //   this.scale
      // )
      // console.log(relativePoint, boardPoint)
    },
    reset() {},
    onMouseDown(e) {
      e.preventDefault()
      this.setPointerState([e])
    },
    onMouseMove(e) {
      if (!this.interaction.origin) {
        return
      }
      e.preventDefault()
      this.handleDrag(e)
    },
    onMouseUp() {
      this.setPointerState([])
    },
    onTouchStart(e) {
      e.preventDefault()
      this.setPointerState(e.touches)
    },
    onTouchMove(e) {
      if (!this.interaction.origin) {
        return
      }
      //console.log(e.touches.length)
      e.preventDefault()
      const isPinchAction =
        e.touches.length == 2 && this.interaction.origin.points.length > 1

      if (isPinchAction) {
        this.handleMultiTouch(e)
      } else if (e.touches.length === 1 && this.interaction.origin) {
        this.handleDrag(e.touches[0])
      }
    },
    onTouchEnd(e) {
      this.setPointerState(e.touches)
    },
    onWheel(e) {
      e.preventDefault()
      e.stopPropagation()

      //  // console.log(e)

      this.handleWheel(e)
    },

    handleDrag(pointer) {
      const { translation, points } = this.interaction.origin
      const startPointer = points[0]
      const dragX = pointer.clientX - startPointer.clientX
      const dragY = pointer.clientY - startPointer.clientY
      const newTranslation = {
        x: translation.x + dragX,
        y: translation.y + dragY,
      }

      //// console.log(dragX, dragY)

      this.$store.commit(
        'ui/setTranslation',
        constrainTranslation(newTranslation, this.translationBounds)
      )
      this.shouldPreventTouchEndDefault = true
    },
    setPointerState(points) {
      if (!points || points.length === 0) {
        this.$store.commit('ui/resetOrigin')
        return
      }

      this.$store.commit('ui/setOrigin', {
        points,
        scale: this.scale,
        translation: this.translation,
      })
    },
    handleWheel(e) {
      const [newScale, newTranslation] = changeViewFromWheelEvent(
        e,
        this.$refs.container,
        this
      )
      this.$store.commit('ui/setScale', newScale)
      this.$store.commit(
        'ui/setTranslation',
        constrainTranslation(newTranslation, this.translationBounds)
      )
    },

    // FIXME: I dont think this is working
    handleMultiTouch(e) {
      const [newTranslation, newScale] = changeViewFromMultiTouchEvent(
        this.interaction.origin.points,
        e.touches,
        this.$refs.container,
        this
      )
      this.$store.commit('ui/setScale', newScale)
      this.$store.commit(
        'ui/setTranslation',
        constrainTranslation(newTranslation, this.translationBounds)
      )
    },
  },
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.outer {
  height: 100%;
  width: 100%;
  position: relative;
  overflow: hidden;
  background-color: rgb(245, 245, 245);
}
.inner {
  width: 2000px;
  height: 2000px;
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  background-size: 40px 40px;
  background-color: rgb(245, 245, 245);
  /* border: 1px solid rgba(0, 0, 0, 0.25); */
  background-image: radial-gradient(
    circle,
    rgba(0, 0, 0, 0.5) 1px,
    rgba(0, 0, 0, 0) 1px
  );
}
.inner.active {
  touch-action: none;
  -ms-touch-action: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
</style>