Skip to content

Instantly share code, notes, and snippets.

@mpj
Last active July 20, 2022 19:04
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save mpj/6969ecff1d5b8e5ccb559ca525ee2632 to your computer and use it in GitHub Desktop.
Save mpj/6969ecff1d5b8e5ccb559ca525ee2632 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Intro to XState</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="box"></div>
<script src="https://unpkg.com/xstate@4.8.0/dist/xstate.js"></script>
<script src="index.js"></script>
</body>
</html>
const { createMachine, interpret, assign } = XState;
const dragDropMachine = createMachine({
initial: 'idle',
context: {
// the position of the box
x: 0,
y: 0,
// where you clicked
pointerx: 0,
pointery: 0,
// how far from where you clicked
dx: 0, // how far: x
dy: 0, // how far: y
},
states: {
idle: {
on: {
// event: nextstate
mousedown: {
target: 'dragging',
actions: assign((context, mouseEvent) => {
return {
...context,
pointerx: mouseEvent.clientX,
pointery: mouseEvent.clientY
}
})
}
}
},
dragging: {
on: {
mousemove: {
target: 'dragging',
actions: assign((context, mouseEvent) => {
return {
...context,
dx: mouseEvent.clientX - context.pointerx,
dy: mouseEvent.clientY - context.pointery
}
})
},
mouseup: {
target: 'idle',
actions: assign((context) => {
return {
...context,
x: context.x + context.dx,
y: context.y + context.dy,
dx: 0,
dy: 0
}
})
// change context.x and context.y
}
}
}
}
})
const body = document.body;
const box = document.getElementById('box')
const dragDropService = interpret(dragDropMachine)
.onTransition(state => {
if (state.changed) {
console.log(state.context)
box.style.setProperty('left',
// where the box is + how far the box moved
state.context.x + state.context.dx + 'px')
box.style.setProperty('top',
state.context.y + state.context.dy + 'px')
body.dataset.state = state.toStrings().join(' ')
}
})
.start();
body.addEventListener('mousedown', event => {
// event.clientX
// event.clientY
dragDropService.send(event)
})
body.addEventListener('mouseup', event => {
dragDropService.send(event)
})
body.addEventListener('mousemove', event => {
dragDropService.send(event)
})
@bwaidelich
Copy link

bwaidelich commented Mar 13, 2020

Those were the styles.css:

* {
    position: relative;
    box-sizing: border-box;
}

body,
html {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
}

body::before {
    content: attr(data-state);
    font-size: 2rem;
    font-family: monospace;
    position: absolute;
    padding: 1rem;
    z-index: 99;
    right: 0;
}

body[data-state="dragging"] #box {
    background: red;
}

#box {
    height: 20vmin;
    width: 20vmin;
    background: #99ccff;
    border-radius: 1rem;
}

Also I think it should have been

box.addEventListener('mousedown'...

instead of

body.addEventListener('mousedown'...

only the mouseup listener needs to be on the container

@bwaidelich
Copy link

a simpler version (less math :) and more logic in the state machine) that also works if the box doesn't start at position 0,0: https://gist.github.com/bwaidelich/aa5f33c4937e070dd3c53c6345623167

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