Skip to content

Conversation

@zjffun
Copy link
Contributor

@zjffun zjffun commented Jun 20, 2020

Only a complish a simple demo now, there are still a long way to go.

This PR implements or fixes... (explain your changes)

#393

This PR closes the following issues... (if applicable)

#393

Does this PR require the Docs to be updated?

Yes

Does this PR require new tests?

Yes

This branch been tested on... (click all that apply / add new items)

Browsers:

  • Chrome version
  • Firefox version
  • Safari version
  • IE / Edge version
  • iOS Browser version
  • Android Browser version

@zjffun
Copy link
Contributor Author

zjffun commented Aug 3, 2020

This function need sensorEvent provide pageX and pageY, so it will go ahead after #423 solved.

@PaulleDemon
Copy link

Hi @zjffun I am trying my own implementation of this. Does your implementation change the source position? When I tried creating my own plugin. The mirror snaps to the grid, but the source elements has a bit of offset.

Thanks for your help.

below is my implementation btw, also, it seems the drag:stop event doesn't seem to affect the source on drop

class SnapPlugin {

    static enabled = false // keep it static variable so it's share across all instances of SnapPlugin
    static gridSize = 20

    constructor(draggable, { enabled = false } = {}) {
        this.draggable = draggable
        this.snappable = enabled // this one checks if the data-grid-snappable is enabled on widget basis

        this.cursorOffset = { x: 0, y: 0 }
        this.lastSnapped = { screen: { x: 0, y: 0 }, relative: { x: 0, y: 0 } }

        this.setGrid = this.setGrid.bind(this)
        this.setEnabled = this.setEnabled.bind(this)
        this.onDragStart = this.onDragStart.bind(this)
        this.onMirrorMove = this.onMirrorMove.bind(this)
        this.onDragStop = this.onDragStop.bind(this)

        this.snapCoords = this.snapCoords.bind(this)

        this.attach = this.attach.bind(this)
        this.detach = this.detach.bind(this)
    }

    attach() {
        this.draggable.on('drag:start', this.onDragStart)
        this.draggable.on('mirror:move', this.onMirrorMove)
        this.draggable.on('mirror:destroy', this.onDragStop)
        // this.enabled = true
    }

    detach() {
        this.draggable.off('drag:start', this.onDragStart)
        this.draggable.off('mirror:move', this.onMirrorMove)
        this.draggable.off('mirror:destroy', this.onDragStop)
        this.enabled = false
    }

    setGrid(size) {
        SnapPlugin.gridSize = size
    }

    setEnabled(state) {
        SnapPlugin.enabled = state
        if (!state) {
            this.snappable = false
        }
    }

    snapCoords(clientX, clientY) {
        const containerRect = this.draggable.containers[0].getBoundingClientRect()

        // Cursor relative to container
        const relX = clientX - containerRect.left
        const relY = clientY - containerRect.top

        // Snap cursor itself to grid
        const snappedCursorX = Math.round(relX / SnapPlugin.gridSize) * SnapPlugin.gridSize
        const snappedCursorY = Math.round(relY / SnapPlugin.gridSize) * SnapPlugin.gridSize

        return { x: snappedCursorX, y: snappedCursorY, containerRect }
    }


    onDragStart(event) {
        if (!SnapPlugin.enabled) {
            this.snappable = false
            return
        }

        const { sensorEvent, source } = event
        const { clientX, clientY } = sensorEvent

        const rect = source.getBoundingClientRect()

        this.snappable = SnapPlugin.enabled && source.dataset.gridSnappable === "true" // check if the widget allows snapping
        // Where inside the element did the user grab?
        this.cursorOffset = {
            x: clientX - rect.left,
            y: clientY - rect.top
        }
    }

    onMirrorMove(event) {
        if (!this.snappable) return
        event.cancel()

        const { sensorEvent, mirror, source } = event
        const { clientX, clientY } = sensorEvent

        const { containerRect } = this.snapCoords(clientX, clientY)

        // Top-left position of element relative to container
        const relX = clientX - containerRect.left - this.cursorOffset.x
        const relY = clientY - containerRect.top - this.cursorOffset.y

        // Snap the element’s top-left instead of the cursor
        const snappedX = Math.round(relX / SnapPlugin.gridSize) * SnapPlugin.gridSize
        const snappedY = Math.round(relY / SnapPlugin.gridSize) * SnapPlugin.gridSize

        // Translate to screen coords for mirror
        const mirrorX = containerRect.left + snappedX
        const mirrorY = containerRect.top + snappedY

        mirror.style.transform = `translate(${mirrorX}px, ${mirrorY}px)`
        // source.style.transform = `translate(${snappedX}px, ${snappedY}px)`
        console.log("soruce: ", event)
        this.lastSnapped = {
            screen: { x: mirrorX, y: mirrorY },
            relative: { x: snappedX, y: snappedY },
            containerRect
        }

    }

    onDragStop(event) {
        const { source } = event
        const { x: snappedX, y: snappedY } = this.lastSnapped.relative
        source.style.transform = `translate(${snappedX}px, ${snappedY}px)`
    }

}

export default SnapPlugin

@PaulleDemon
Copy link

Sorry, I figured it out. It wasn't problem with the GridSnap or the library, but how i hadnled the positioning after. Thanks again

@zjffun
Copy link
Contributor Author

zjffun commented Sep 6, 2025

@PaulleDemon Sorry, It's been so long, I've forgotten my previous thoughts. Maybe you cloud try to clone the mirror and setTimeout for a while, then get the position and set to the source element.

@PaulleDemon
Copy link

@zjffun thanks for replying, I actually figured it out, this code was correct, it was a different problem on how I was positioning my elements. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants