14

How can I trigger a mousemove only if the element is clicked first? I'm trying to utilize this for an audio player timeline.

.player__time--bar(@mousedown="setNewCurrentPosition($event)")
    .slider(role="slider" aria-valuemin="0" :aria-valuenow="currentPosition" :aria-valuemax="trackTotalDuration" aria-orientation="horizontal")
        .player__time--bar-current-position(:style="{width:  (100 / (trackTotalDuration / currentPosition)) + '%'}")

The method:

setNewCurrentPosition(e) {
    let tag = e.target
    // if the click is not on 'slider', grab div with class 'slider'
    if (e.target.className === 'player__time--bar') tag = e.target.firstElementChild
    else if (e.target.className === 'player__time--bar-current-position') tag = e.target.parentElement
    const pos = tag.getBoundingClientRect()
    const seekPos = (e.clientX - pos.left) / pos.width
    this.currentPosition = parseInt(this.trackTotalDuration * seekPos)
    // updates the time in the html
    this.$refs.player.currentTime = this.currentPosition
},
Giacomo
  • 1,056
  • 4
  • 16
  • 24
  • What error are you getting? Is something in the code you provided not working? – PatrickSteele Jan 08 '18 at 14:43
  • 2
    Set a variable on mousedown on the time-bar. Unset the variable on mouseup anywhere. Wrap your mousemove in an `if` that checks whether the variable is set. – Roy J Jan 08 '18 at 15:10
  • @RoyJ ok that it’s very doable but how do I call the function mousemove – Giacomo Jan 08 '18 at 16:56
  • @PatrickSteele what I have is working only on click. I want to be able to move the position ben if you click and drag. Basically sort of emulate the input type range – Giacomo Jan 08 '18 at 16:57

2 Answers2

38

You will want to have a mousedown listener on your element that sets a variable to indicate dragging started. Put a listener on the window to catch mouseup anywhere and unset the variable.

You can put mousemove on the element if you are only interested in dragging that happens inside the element. Otherwise you can put the mousemove listener on window so you catch it everywhere.

new Vue({
  el: '#app',
  data: {
    dragging: false,
    x: 'no',
    y: 'no'
  },
  methods: {
    startDrag() {
      this.dragging = true;
      this.x = this.y = 0;
    },
    stopDrag() {
      this.dragging = false;
      this.x = this.y = 'no';
    },
    doDrag(event) {
      if (this.dragging) {
        this.x = event.clientX;
        this.y = event.clientY;
      }
    }
  },
  mounted() {
    window.addEventListener('mouseup', this.stopDrag);
  }
});
.dragstartzone {
  background-color: red;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <div class="dragstartzone" @mousedown="startDrag" @mousemove="doDrag">Start dragging here</div>
  <div>X: {{x}}, Y: {{y}}</div>
</div>
Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Great! Thank you. I readjusted it a bit to fit my need. I will post code snippet below – Giacomo Jan 08 '18 at 22:00
  • There's still an issue... not really working on mobile :/ – Giacomo Jan 08 '18 at 23:37
  • 1
    Right. You'll have to work with [touch events](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events) since there's no mouse button. Should be the same things with touchstart touchend and touchmove. – Roy J Jan 09 '18 at 02:00
  • Not having much luck with it, I don't think Vue supports @touch... by default – Giacomo Jan 09 '18 at 10:20
  • Nevermind, all figured out. Just had to grab some different properties. Thanks again – Giacomo Jan 09 '18 at 10:40
0

I ended up using the code provided by Roy J, refactoring it a bit to fit my need. Here it is

Template:

.player__time--bar(@mousedown="startDrag($event)" @mouseup="stopDrag($event)" @mousemove="doDrag($event)")
    .slider(role="slider" aria-valuemin="0" :aria-valuenow="currentPosition" :aria-valuemax="trackTotalDuration" aria-orientation="horizontal")
        .player__time--bar-current-position(:style="{width:  (100 / (trackTotalDuration / currentPosition)) + '%'}")

Data:

data: () => ({
    currentPosition: 0,
    trackTotalDuration: 0,
    dragging: false
}),

Methods:

startDrag() {
    this.dragging = true
},
stopDrag(event) {
    this.dragging = false
    this.setNewCurrentPosition(event)
},
doDrag(event) {
    if (this.dragging) this.setNewCurrentPosition(event)
},

setNewCurrentPosition(e) {
    let tag = e.target
    // if the click is not on 'slider', grab div with class 'slider'
    if (e.target.className === 'player__time--bar') tag = e.target.firstElementChild
    else if (e.target.className === 'player__time--bar-current-position') tag = e.target.parentElement
    const pos = tag.getBoundingClientRect()
    const seekPos = (e.clientX - pos.left) / pos.width
    this.currentPosition = parseInt(this.trackTotalDuration * seekPos)
    // updates the time in the html
    this.$refs.player.currentTime = this.currentPosition
},
Giacomo
  • 1,056
  • 4
  • 16
  • 24