Explanation
I was under the wrong impression that If I passed a selector into the hitTest then it would do the hit test against every element that fit the selector. In which case I dont neccesarily have the element when I execute the hitTest() I just have a selector which might fit many diffirent elements and I want to return the element of that selector that was actually hit. Anyway, I realize now that this is not actually how the hitTest() works and Ive finished my solution. Thanks for your help. Your suggestions where very helpfull for much of my experimentation. Issue Resolved
I experimented with a lot of diffirent ways of doing it using the liveSnap property. But nothing quite did all the things I wanted with the flexibility I was looking for. Instead I ended up bringing in the flip plugin and just using that to create my own snapping to complement the draggable plugin. Which turned out to be the perfect solution. I have a ton of flexibility now. Record
For the record, in case anyone else is trying to do something similar ill post another codepen with the approach I came up with for my solution. https://codepen.io/carelesscourage/pen/VwXbLRx The Code
My real source code is doing all this in Nuxt 3 which has do do some extra juggling because of SSR. Heres how I did it in Nuxt 3 if anyone is spesifically dealing with that approach. Also, for the record, the below code will only work in Nuxt 3 if you transpile GSAP. Otherwise you have to import the gsap dependecies using require() inside the process.client check. Heres the Nuxt 3 docs to explain how to transpile dependencies: docs
<script setup>
import { gsap } from "gsap"
import { Flip } from "gsap/Flip";
import { Draggable } from "gsap/Draggable";
const fragElement = ref(null)
let zones = []
let fragRect = null
let draggable = null
onMounted(() => {
if(process.client) {
gsap.registerPlugin(Flip, Draggable);
zones = document.querySelectorAll('[data-dropzone]')
fragRect = fragElement.value.getBoundingClientRect()
draggable = new Draggable(fragElement.value, {
onDrag: onDrag,
onRelease: onRelease,
})
}
})
function zoneHit(el, {hit, mis = () => {}, dud = () => {}}) {
let noHit = true
function onHit(zone) {
noHit = false
hit(zone, el)
}
zones.forEach((zone) => {
el.hitTest(zone)
? onHit(zone)
: mis(zone, el)
})
noHit && dud(el)
}
function landHit(zone, el) {
zone.classList.remove("drag-hit");
zone.classList.add("land-hit");
Flip.fit(el.target, zone, {
duration: 0.1,
});
}
function landMis(zone, el) {
zone.classList.remove("drag-hit");
zone.classList.remove("land-hit");
}
function onDrag(e) {
fragElement.value.classList.add("drag")
zoneHit(this, {
hit: (zone) => zone.classList.add("drag-hit"),
mis: (zone) => zone.classList.remove("drag-hit")
})
}
function onRelease(e) {
fragElement.value.classList.remove("drag")
zoneHit(this, {
hit: landHit,
mis: landMis,
dud: () => gsap.to(this.target, {duration: 0.5, x:0, y:0})
})
}
</script>
<template>
<div
ref="fragElement"
class="frag u-panel"
>
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
.frag {
background-color: var(--background);
padding: var(--space-familiar);
border: solid 3px red;
transition: all none !important;
transition: max-width 0.4s !important;
max-width: 700px;
}
</style>