2019-07-07 00:14:19 -07:00
|
|
|
<div class="draggable-area {draggableClass}"
|
|
|
|
on:pointermove="onPointerMove(event)"
|
|
|
|
on:pointerleave="onPointerLeave(event)"
|
|
|
|
on:click="onClick(event)"
|
|
|
|
ref:area
|
|
|
|
>
|
|
|
|
<div class="draggable-indicator {indicatorClass}"
|
|
|
|
style={indicatorStyle}
|
|
|
|
on:pointerdown="onPointerDown(event)"
|
|
|
|
on:pointerup="onPointerUp(event)"
|
|
|
|
ref:indicator
|
|
|
|
>
|
|
|
|
<div class="draggable-indicator-inner">
|
|
|
|
<slot></slot>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<style>
|
|
|
|
.draggable-area {
|
|
|
|
position: relative;
|
|
|
|
touch-action: none;
|
|
|
|
}
|
|
|
|
.draggable-indicator {
|
|
|
|
position: absolute;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
.draggable-indicator-inner {
|
|
|
|
pointer-events: none;
|
|
|
|
display: flex;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<script>
|
|
|
|
import { throttleRaf } from '../_utils/throttleRaf'
|
|
|
|
|
|
|
|
const clamp = x => Math.max(0, Math.min(1, x))
|
|
|
|
const throttledRaf = throttleRaf()
|
|
|
|
|
|
|
|
export default {
|
|
|
|
data: () => ({
|
|
|
|
draggableClass: '',
|
|
|
|
indicatorClass: '',
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
indicatorWidth: 0,
|
|
|
|
indicatorHeight: 0
|
|
|
|
}),
|
|
|
|
computed: {
|
|
|
|
indicatorStyle: ({ x, y, indicatorWidth, indicatorHeight }) => (
|
|
|
|
`left: calc(${x * 100}% - ${indicatorWidth / 2}px); top: calc(${y * 100}% - ${indicatorHeight / 2}px);`
|
|
|
|
)
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
onPointerDown (e) {
|
|
|
|
e.preventDefault()
|
|
|
|
e.stopPropagation()
|
2019-08-03 13:49:37 -07:00
|
|
|
const rect = this.refs.indicator.getBoundingClientRect()
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({
|
|
|
|
dragging: true,
|
|
|
|
dragOffsetX: e.clientX - rect.left,
|
|
|
|
dragOffsetY: e.clientY - rect.top
|
|
|
|
})
|
|
|
|
},
|
|
|
|
onPointerMove (e) {
|
|
|
|
if (this.get().dragging) {
|
|
|
|
e.preventDefault()
|
|
|
|
e.stopPropagation()
|
2019-08-03 13:49:37 -07:00
|
|
|
const { indicatorWidth, indicatorHeight, dragOffsetX, dragOffsetY } = this.get()
|
2019-07-07 00:14:19 -07:00
|
|
|
throttledRaf(() => {
|
2019-08-03 13:49:37 -07:00
|
|
|
const rect = this.refs.area.getBoundingClientRect()
|
|
|
|
const offsetX = dragOffsetX - (indicatorWidth / 2)
|
|
|
|
const offsetY = dragOffsetY - (indicatorHeight / 2)
|
|
|
|
const x = clamp((e.clientX - rect.left - offsetX) / rect.width)
|
|
|
|
const y = clamp((e.clientY - rect.top - offsetY) / rect.height)
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({ x, y })
|
|
|
|
this.fire('change', { x, y })
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onPointerUp (e) {
|
|
|
|
e.preventDefault()
|
|
|
|
e.stopPropagation()
|
|
|
|
this.set({ dragging: false })
|
|
|
|
},
|
|
|
|
onPointerLeave (e) {
|
|
|
|
e.preventDefault()
|
|
|
|
e.stopPropagation()
|
|
|
|
this.set({ dragging: false })
|
|
|
|
},
|
|
|
|
onClick (e) {
|
|
|
|
if (!e.target.classList.contains('draggable-indicator')) {
|
|
|
|
e.preventDefault()
|
|
|
|
e.stopPropagation()
|
2019-08-03 13:49:37 -07:00
|
|
|
const rect = this.refs.area.getBoundingClientRect()
|
|
|
|
const x = clamp((e.clientX - rect.left) / rect.width)
|
|
|
|
const y = clamp((e.clientY - rect.top) / rect.height)
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({ x, y })
|
|
|
|
this.fire('change', { x, y })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|