2019-08-04 13:31:51 -07:00
|
|
|
<div class="draggable-area {draggableClassAfterRaf}"
|
|
|
|
on:pointerMove="onPointerMove(event)"
|
|
|
|
on:pointerLeave="onPointerLeave(event)"
|
|
|
|
on:pointerUp="onPointerUp(event)"
|
2019-07-07 00:14:19 -07:00
|
|
|
on:click="onClick(event)"
|
|
|
|
ref:area
|
|
|
|
>
|
2019-08-04 13:31:51 -07:00
|
|
|
<div class="draggable-indicator {indicatorClassAfterRaf}"
|
|
|
|
style={indicatorStyleAfterRaf}
|
|
|
|
on:pointerDown="onPointerDown(event)"
|
2019-07-07 00:14:19 -07:00
|
|
|
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>
|
2019-08-04 13:31:51 -07:00
|
|
|
import { observe } from 'svelte-extras'
|
2019-08-07 20:38:01 -07:00
|
|
|
import { throttleTimer } from '../_utils/throttleTimer'
|
2019-08-04 13:31:51 -07:00
|
|
|
import { pointerUp, pointerDown, pointerLeave, pointerMove } from '../_utils/pointerEvents'
|
2019-08-07 20:38:01 -07:00
|
|
|
import { requestPostAnimationFrame } from '../_utils/requestPostAnimationFrame'
|
2019-08-04 13:31:51 -07:00
|
|
|
|
|
|
|
// ensure DOM writes only happen once after a rAF
|
2019-08-07 20:38:01 -07:00
|
|
|
const updateIndicatorStyle = throttleTimer(requestAnimationFrame)
|
|
|
|
const updateIndicatorClass = throttleTimer(requestAnimationFrame)
|
|
|
|
const updateDraggableClass = throttleTimer(requestAnimationFrame)
|
2019-08-04 13:31:51 -07:00
|
|
|
|
|
|
|
// ensure DOM reads only happen once after a rPAF
|
2019-08-07 20:38:01 -07:00
|
|
|
const calculateGBCR = throttleTimer(requestPostAnimationFrame)
|
2019-07-07 00:14:19 -07:00
|
|
|
|
|
|
|
const clamp = x => Math.max(0, Math.min(1, x))
|
|
|
|
|
|
|
|
export default {
|
2019-08-04 13:31:51 -07:00
|
|
|
oncreate () {
|
|
|
|
this.observe('dragging', dragging => {
|
|
|
|
if (dragging) {
|
|
|
|
this.fire('dragStart')
|
|
|
|
} else {
|
2019-08-07 20:38:01 -07:00
|
|
|
const { x, y } = this.get()
|
2019-08-04 13:31:51 -07:00
|
|
|
this.fire('dragEnd')
|
2019-08-07 20:38:01 -07:00
|
|
|
this.fire('change', { x, y })
|
2019-08-04 13:31:51 -07:00
|
|
|
}
|
|
|
|
}, { init: false })
|
|
|
|
this.observe('indicatorStyle', indicatorStyle => {
|
|
|
|
console.log('Draggable indicatorStyle', indicatorStyle)
|
|
|
|
updateIndicatorStyle(() => {
|
|
|
|
this.set({ indicatorStyleAfterRaf: indicatorStyle })
|
|
|
|
})
|
|
|
|
})
|
|
|
|
this.observe('indicatorClass', indicatorClass => {
|
|
|
|
updateIndicatorClass(() => {
|
|
|
|
this.set(({ indicatorClassAfterRaf: indicatorClass }))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
this.observe('draggableClass', draggableClass => {
|
|
|
|
updateDraggableClass(() => {
|
|
|
|
this.set({ draggableClassAfterRaf: draggableClass })
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
2019-07-07 00:14:19 -07:00
|
|
|
data: () => ({
|
2019-08-04 13:31:51 -07:00
|
|
|
dragging: false,
|
2019-07-07 00:14:19 -07:00
|
|
|
draggableClass: '',
|
2019-08-04 13:31:51 -07:00
|
|
|
draggableClassAfterRaf: '',
|
2019-07-07 00:14:19 -07:00
|
|
|
indicatorClass: '',
|
2019-08-04 13:31:51 -07:00
|
|
|
indicatorClassAfterRaf: '',
|
2019-07-07 00:14:19 -07:00
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
indicatorWidth: 0,
|
2019-08-04 13:31:51 -07:00
|
|
|
indicatorHeight: 0,
|
|
|
|
indicatorStyleAfterRaf: ''
|
2019-07-07 00:14:19 -07:00
|
|
|
}),
|
|
|
|
computed: {
|
|
|
|
indicatorStyle: ({ x, y, indicatorWidth, indicatorHeight }) => (
|
|
|
|
`left: calc(${x * 100}% - ${indicatorWidth / 2}px); top: calc(${y * 100}% - ${indicatorHeight / 2}px);`
|
|
|
|
)
|
|
|
|
},
|
|
|
|
methods: {
|
2019-08-04 13:31:51 -07:00
|
|
|
observe,
|
2019-07-07 00:14:19 -07:00
|
|
|
onPointerDown (e) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onPointerDown')
|
2019-08-03 13:49:37 -07:00
|
|
|
const rect = this.refs.indicator.getBoundingClientRect()
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: e.clientX', e.clientX)
|
|
|
|
console.log('Draggable: e.clientY', e.clientY)
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({
|
|
|
|
dragging: true,
|
|
|
|
dragOffsetX: e.clientX - rect.left,
|
|
|
|
dragOffsetY: e.clientY - rect.top
|
|
|
|
})
|
|
|
|
},
|
|
|
|
onPointerMove (e) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onPointerMove')
|
|
|
|
const { dragging, indicatorWidth, indicatorHeight, dragOffsetX, dragOffsetY } = this.get()
|
|
|
|
if (dragging) {
|
|
|
|
console.log('Draggable: dragging')
|
|
|
|
calculateGBCR(() => {
|
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 })
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onPointerUp (e) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onPointerUp')
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({ dragging: false })
|
|
|
|
},
|
|
|
|
onPointerLeave (e) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onPointerLeave')
|
2019-07-07 00:14:19 -07:00
|
|
|
this.set({ dragging: false })
|
|
|
|
},
|
|
|
|
onClick (e) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onClick')
|
|
|
|
console.log('Draggable: target classList', e.target.classList)
|
|
|
|
console.log('Draggable: currentTarget classList', e.currentTarget.classList)
|
2019-07-07 00:14:19 -07:00
|
|
|
if (!e.target.classList.contains('draggable-indicator')) {
|
2019-08-04 13:31:51 -07:00
|
|
|
console.log('Draggable: onClick handled')
|
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 })
|
|
|
|
}
|
|
|
|
}
|
2019-08-04 13:31:51 -07:00
|
|
|
},
|
|
|
|
events: {
|
|
|
|
pointerUp,
|
|
|
|
pointerDown,
|
|
|
|
pointerLeave,
|
|
|
|
pointerMove
|
2019-07-07 00:14:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|