Drag Animation with Svelte Custom Actions

Ross MacDonald
4 min readDec 19, 2020

--

I’ve recently been broadening my horizons beyond the world of React, which led me down quite the rabbit-hole of tinkering with Svelte. For those unfamiliar with Svelte, it is essentially another framework for building fast web apps, like React or Vue, but with a few crucial differences. In their own words:

“ Svelte converts your app into ideal JavaScript at build time, rather than interpreting your application code at run time. This means you don’t pay the performance cost of the framework’s abstractions, and you don’t incur a penalty when your app first loads.”

There is also less boilerplate code, and in my opinion, a smaller barrier to entry when starting up a new project. Svelte allows you to write code that directly manipulates the DOM, which in turn forces you to brush up on your core browser APIs and vanilla JavaScript.

Enough preamble (ramble), let’s get started. To get a Svelte app up and running, we’ll use option 2 from Svelte’s Quick Start Guide, use degit.

npx degit sveltejs/template svelte-custom-drag 
cd svelte-custom-drag
npm install
npm run dev

Head to your browser at http://localhost:5000 and you should see the Svelte Hello World page. If not, maybe it's a sign and Svelte just isn’t for you. I’m not going to worry too much about file architecture for this tutorial, so we’ll be working mainly with App.svelte in the src file.

Replace the boilerplate code in App.svelte with the following:

This should give you a lovely blue square centered on your screen. Next, create a file in your src directory called drag.js. This is where we’ll write the code for our custom action. Custom actions in Svelte allow us to create our own event handlers and easily use them across different components in our app, among other useful things. If you’d like to read up a bit more on actions, head to Svelte’s docs.

In your drag.js file add the following:

export function drag(node, params){}

Actions take 2 parameters: the DOM node, and custom params that can be setup within our drag function. Now in App.svelte, add this line within your script tags to import the drag action.

import { drag } from './drag'

Still in App.svelte, we simply add use:drag as a property on our div, which should now look like this:

<div class="box" use:drag />

Although we still have plenty to do in order to have a functional drag action, that is truly the only setup that we need to do. Let’s get started on the drag functionality. In order to create our own custom drag event, we’ll need to be able to listen to when the mouse clicks down, moves, and then when the click is released. To start, let's add these three functions into our drag.js main function, so it looks like this (I’ve also added an event listener before our first function — after adding this code and saving, click on the blue box in your browser and you should see the node logged to your dev tools console.):

Next, we’ll dive into the most complicated part of the code. We need to be able to get and keep track of the coordinates of our element so that it can be accurately reflected in the DOM. In order to grab the initial coordinates, we’ll first initialize x and y variables at the beginning of our drag function and then assign these variables to event.clientX and event.clientY within the handleMouseDown function. We’ll also add two more event listeners within our handleMouseDown & handleMouseUp functions that will listen for the mouse move and mouse up events.

Now we’re going to add a feature built into svelte called ‘spring’ — this essentially acts as a store for our values as the node is being dragged, as well as assists with animation between the changes in x and y coordinates. We’ll now initiate a variable coordinates and then subscribe to the changes in the coordinates. Within the subscribe function we use a css transform

Stiffness, damping and the inner workings of spring are a bit beyond the scope of this post, but if you want to read into it you can check out the docs here.

Now we move into the handleMouseMove function to calculate the change from the original position to the current position. To do so, we calculate the difference between the current positions and the original positions (x and y). Then after initializing deltaX and deltaY, we update the x and y values. We also use the spring update method which returns an object with the current value of coordinates added to the deltaX or deltaY. handleMouseMove now looks like this:

Head to your browser, and tada! You should now be able to drag your box. Just to tidy things up we’ll add a return statement to the bottom of our function to remove the event listeners. Your final drag.js file should look like this:

The full repo can be found on my GitHub. While you’re at it, check out my personal portfolio site here.

--

--