Creating a Rotating Globe Visualization in Svelte
Making it spin!
This lesson preview is part of the Better Data Visualizations with Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Better Data Visualizations with Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:11] Hey y'all, in this lesson we're going to be adding rotation to our globe because after all the lesson is called a rotating globe, not just a static one. So we're going to be adding rotation on two fronts.
[00:12 - 00:22] First is auto rotation. So if the web page is resting, if there's no interaction whatsoever, we want the globe to rotate in a circle slowly so that, you know, it has that visual effect, which is really nice.
[00:23 - 00:43] Users can see that it does comprise the entire globe and you can see other countries than just the hemisphere that is currently visible as the globe rotates. And the second thing that we'll be doing is adding user interaction and allowing the user to pan and drag the globe on the horizontal and the vertical dimension with their cursor.
[00:44 - 00:50] Right? So that's the second thing we're going to be doing is user directed interaction and rotation.
[00:51 - 01:02] So let's start off with auto rotation because it's the easier of the two. And like we've done in previous modules, let's go ahead and describe in kind of human language, what it is we want to do before diving into the code.
[01:03 - 01:21] My take on what we want to achieve is effectively we want to rotate the globe according to some rotation variable and have that update our projection. How often do we want to do this as often as is possible without it having a performance implication?
[01:22 - 01:39] What I mean by that is we want to access kind of the internals of the web and every time that there's what we call an animation frame, this is kind of a tech term, but every time that there would be a frame of animation, we want to rotate the globe. And the reason that we want to do this as often as is possible is just for this smooth effect, right?
[01:40 - 01:47] We don't want this to be choppy. We don't want to carry out an interval every one second, for example, because that would be very jittery.
[01:48 - 02:01] We want to smoothly rotate on a constant interval, small amounts incrementally so that the globe looks like it's rotating automatically. So with all that said, some things that we know we need are a rotation variable .
[02:02 - 02:21] Like I said, we need to keep track of the rotation of our globe at any given time. We also need a variable that corresponds to like the autocomplete suggests speed or maybe a more representative variable name would be degrees per frame.
[02:22 - 02:23] Okay. And what do we want that to be?
[02:24 - 02:31] Let's do 0.5 degrees per frame of animation. Now the question is, how do we access animation frames?
[02:32 - 02:44] How do we carry out an animation on an interval using spelt or D3 or vanilla JavaScript? And the answer for this, what you're most likely going to find is going to be D 3.timer.
[02:45 - 02:59] So I'll go ahead and pull up kind of the documentation for D3 timer on a browser and then I'll bring it over and show you. As you can see here, D3 timer is another D3 module with its own documentation on GitHub.
[03:00 - 03:11] And here you can see after installation some of the API reference that we could use. So D3.now returns the current time, which we don't really need.
[03:12 - 03:26] D3.timer schedules a new timer invoking a callback repeatedly until the timer is stopped. And then there's timer.restart, timer.stop, timer flush, timeout and interval.
[03:27 - 03:48] So the ones that stick out to me is what we might want to use are D3.interval because we can specify basically carry out a given action at an interval, right? So every delay milliseconds carry it out or D3.timer, which ignores that delay and just says carry this out infinitely until you are told to stop and do it on every animation frame, right?
[03:49 - 03:54] So invoke the specified callback repeatedly until stopped. You could delay it if you want.
[03:55 - 04:04] If not, it will default to zero and then just continue to carry out within the timer. So this seems most akin to what we are trying to achieve.
[04:05 - 04:08] So I'm going to copy this bit of code. I'm going to go back to my code and I'm going to paste this in.
[04:09 - 04:12] Now by default, you're going to see a couple of issues. Everything disappears.
[04:13 - 04:22] And the reason for this is because we're not using the global D3 namespace, right? We want to import timer directly from its parent module.
[04:23 - 04:42] So just like our other imports, we're going to write import the name of the new method or function and then from D3 timer. We're going to hit save and then we're going to now update the internal of this timer, this callback function is what we're calling it, to basically update our rotation.
[04:43 - 04:53] Before we do that, we'll go ahead and remove this delay because we want to start the rotation immediately. You'll notice that the only kind of parameter within the timer function is how long has been elapsed within the timer.
[04:54 - 05:09] So if I went into my console right now and I refreshed, you would see it consoles a few times up until it hits right above 200 and then it stops. So this is effectively every animation frame, as my understanding, is when this is constantly.
[05:10 - 05:12] So we don't want to console long elapsed. In fact, we don't actually care about elapsed.
[05:13 - 05:19] So you could remove this, but we'll leave it just for the sake of keeping it there. What we want to do is update rotation.
[05:20 - 05:35] So effectively, we want to set rotation to equal, sorry, to equal its current value plus degrees per frame. So this is kind of a shorthand syntax for doing exactly that.
[05:36 - 05:46] And this is like JavaScript, vanilla JavaScript, nothing's spelty, nothing D3 about this. This line of code is equivalent to rotation equals rotation plus degrees per frame.
[05:47 - 05:54] So just so you know, this is just a more verbose way of writing this. So we'll keep this succinct way of writing and we'll hit save.
[05:55 - 06:02] And then let's make sure that this is working by console dot logging rotation. Now you'll see the rotation consistently updates frame by frame by frame.
[06:03 - 06:14] And you're seeing its values increase and it will continue to increase until we stop the timer or we return within the callback. So now we have a rotation variable, but we're not doing anything with it, right ?
[06:15 - 06:32] And so how do we account for this rotation in our globe? Well, you may remember way up here that we actually have a rotation, you know, method or a line of code within our orthographic geo projection where we can account for X, Y and Z rotation.
[06:33 - 06:44] And so right now, let's just say this accounts for this X rotation and let's replace this first zero with rotation and hit save. Now you'll see the globe updates piece by piece by piece one by one.
[06:45 - 06:49] Maybe you think this is too fast. So you could definitely change this to like point one, see how that feels.
[06:50 - 06:59] You could change this to something faster like one, whatever you prefer here, I 'm going to do point five. Then I'm going to remove the console log because this is just clogging up our console.
[07:00 - 07:21] I'm going to hit save and automatically already we have an auto rotating globe that's working quite nicely. So we have an auto rotating globe and that was the first part of this lesson because we're able to leverage D3 specific methods within D3 timer and just vanilla JavaScript to increment rotation over and over again.
[07:22 - 07:40] We have a nice auto rotating globe. The second thing we're going to address in this lesson is user interaction, which admittedly is a bit more difficult because basically we want to account for dragging and panning and moving on the globe itself.
[07:41 - 07:59] And before we write some code for this, I want to describe why this is in fact tricky. You'll recall that in earlier lessons, we talk about D3 and Svelte being used in combination as kind of this perfect pair because one component D3 handles a lot of what would happen in your scripts tag, right?
[08:00 - 08:15] A lot of data manipulation, a lot of D3 scaling, a lot of path generation, whatever else, right? Everything here really is either D3 or vanilla JavaScript except these, you know, few reactive dollar labels which help simplify things as well.
[08:16 - 08:39] But basically the key benefit to using D3 and Svelte in combination is the hand off between you know, D3 for the data and Svelte for the DOM. But unfortunately, whenever it comes to something more complex like D3 dragging or panning or zooming, we don't find that Svelte is the most useful framework for that job.
[08:40 - 09:09] Now we could build out what's called like an action within Svelte where basically you would write use and then the name of the action, maybe drag or pan or whatever else and write some custom JavaScript. But I found that just for the sake of, you know, rolling up the easiest solutions that leverage existing tools, existing patterns, it's actually easier to just rely on D3 for most of our more complex user interactions, for example, dragging.
[09:10 - 09:16] So what do I mean by that? Let's go ahead and pull up some writing and some documentation for D3 drag.
[09:17 - 09:33] So this is going to be the D3 drag module, which you can read the documentation within GitHub just like last time. But as you can see, you know, you might be able to do drag and drop or just drag any type of interaction that requires mouse behavior.
[09:34 - 09:39] There's also D3 zoom that does the same thing for zooming. Basically this handles a lot of pointer events.
[09:40 - 09:51] Okay. And what I wanted to rely on in order to carry out this interaction within our lesson was an existing exam because there's no reason to roll up everything from scratch if existing examples are out there.
[09:52 - 10:04] And so I found this example on observable, which remember, observable is basically like a runtime for JavaScript. And oftentimes you'll find this kind of plain vanilla D3 all throughout in its own cells.
[10:05 - 10:07] So here I'll zoom in so that you can definitely read it. What's happening here?
[10:08 - 10:12] This is an auto rotating globe just like ours. You can also move the globe like so.
[10:13 - 10:22] And this is exactly the behavior we want to achieve. So the question is how did this author, Michael Keith, carry this out?
[10:23 - 10:32] And you know, you could read through all of this and try to find it, but possibly the easiest way is just to find the code that looks like it's doing dragging. And that's what we find right here.
[10:33 - 10:43] So we find this bit of code basically targets the SVG somewhere SVG is being instantiated as a variable right here. And that's basically the selection object.
[10:44 - 10:56] Okay. So we're selecting the SVG object and then we're calling the following function , D3.drag, which takes in the following argument afterward on drag carry out this callback .
[10:57 - 11:00] Okay. So this is effectively what we want to do.
[11:01 - 11:03] We don't want to do the D3 zoom. There's no reason for that.
[11:04 - 11:08] We already have the timer that's doing our auto rotation. So this is what we want to target.
[11:09 - 11:15] So I'm going to copy this and I'm going to go back and I'm going to paste it here. But by default, this isn't going to work right out of the box.
[11:16 - 11:21] And there are a few reasons for this, right? You know, SVG does not exist within our code.
[11:22 - 11:26] So if I click save, what's going to happen? We're going to see a whole bunch of errors.
[11:27 - 11:36] SVG is not defined being the first and foremost one. So in the D3 example that we just saw in observable, the way that they created SVG was something like this.
[11:37 - 11:46] SVG equals select and then the name of the ID and then some properties. So remember we are not doing this vanilla D3 selection method.
[11:47 - 11:59] Instead, what we're trying to do is use spelt built in internals to grab DOM elements in an easier way. So what we're actually going to do is we're going to instantiate a variable.
[12:00 - 12:08] You could call it SVG, but I actually prefer to call it to name it something more representative of what it's actually referencing. So I'm going to call it globe.
[12:09 - 12:17] And instead of SVG call, we're going to do globe call or something similar, but we'll save that for later. For now, let's just get the binding down.
[12:18 - 12:29] So globe right now is undefined. But what I'm going to do is bind the actual element in spelt by using a very simple command, bind this to globe.
[12:30 - 12:37] And what this is saying, this is effectively the same thing as the D3 dot select, but it's in reverse. Let me make this one more visible for you.
[12:38 - 12:51] So we're in D3, you would say, let globe equal D3 dot select SVG. Definitely not that select SVG in spelt, you can bind it directly within the markup itself.
[12:52 - 12:59] So here we're saying bind this to the variable that you created called globe. Okay, so that is very important as this kind of object binding.
[13:00 - 13:09] Now let's confirm that this worked by console dot logging, globe and hit save. Now what you see is that at first globe is undefined.
[13:10 - 13:33] And the reason for that is because it is instantiated as such on line 51, there is no value. But then once the binding does in fact take place, you see that we now have a representation of the DOM element in our console, meaning we successfully bound binded this SVG element and everything within basically everything you would see within the inspector tab.
[13:34 - 13:39] That is now accessible in our script tag. So that's what this bind colon this does.
[13:40 - 13:55] Okay, so now that we have our globe element, that's basically the first part getting this SVG, what they call SVG, what we call globe, right? But now we need to carry out this drag and pan logic within our script tag.
[13:56 - 14:00] And the way that we do that, it's going to be slightly different than in D3. And the reason for that.
[14:01 - 14:12] So let's go ahead and show why this won't work. If we replaced globe here and then carried out some function, what would happen ?
[14:13 - 14:19] Well by default, globe at first is not going to exist. So this entire thing that lives within it wouldn't even get there.
[14:20 - 14:27] There would be a bunch of errors because globe does not exist. As we remembered, globe was undefined at page load.
[14:28 - 14:40] So what we actually need to do is put this in what we call an on mount tag. And I can't recall if we've used on mount in spell before, but basically on mount says once the page loads carry out whatever is within this block.
[14:41 - 14:50] So we'll write on mount, which is going to require us to import it. So we'll do import on mount from spelled.
[14:51 - 14:54] Very simple. And then on mount, we want to carry out something.
[14:55 - 14:58] Let's say page has loaded. And then so we can actually see this.
[14:59 - 15:04] I'm going to comment this out again. And then we're going to go into the console.
[15:05 - 15:08] Page has loaded. And then you see the SVG appears below.
[15:09 - 15:11] So this is a really important lesson. By default, it's undefined.
[15:12 - 15:16] The page has loaded. And then we see globe does in fact exist.
[15:17 - 15:23] That's this important kind of life cycle flow of the spell internals. So what do we want to do?
[15:24 - 15:37] Well, once the page loads, we basically want to create this drag handler on the SVG element on the globe itself. So what I'm actually going to do is bring this entire bit of code up into the on mount.
[15:38 - 15:41] Now at first, if I save this, it's still not going to work. There are probably going to be errors.
[15:42 - 15:45] D3 is not defined. The reason for that is right here.
[15:46 - 15:48] D3.drag does not exist. D3.event does not exist.
[15:49 - 15:56] D3.geopath does not exist. We were importing these from their direct namespaces rather than from D3 globally.
[15:57 - 16:01] So let's keep ours a lot leaner. Let's just make sure that this is in fact working.
[16:02 - 16:09] And in order to do that, let's go ahead and just console.log hello or no, let's make it. Let's make it a bit more descriptive.
[16:10 - 16:16] I am being dragged. And now that we have this console, we're still going to need to get rid of this namespace.
[16:17 - 16:24] So the question is, where do we import drag from? And the answer is D3.drag.
[16:25 - 16:29] We already saw that on GitHub earlier. If you can spell, that will be helpful.
[16:30 - 16:32] And then we can remove D3 now. It's save.
[16:33 - 16:39] And you might notice this kind of dependency issue saying this file does not exist. This module does not exist.
[16:40 - 16:47] And what that means is you need to reopen your console. You can either end your current console using Control C or open a new one using this tab.
[16:48 - 16:53] That's what I prefer. You need an npm install D3.drag.
[16:54 - 16:58] So it's going to take some time. And then once it's done, you can delete that console, refresh your page.
[16:59 - 17:03] And now you'll see D3.drag is in fact accessible. You don't see that same namespace error anymore.
[17:04 - 17:10] Now, the final issue we're getting is this uncaught type error. globe.call is not a function.
[17:11 - 17:20] Okay. And the reason for this is the way that D3 works, it can't call on these vanilla, these kind of native HTML elements like SVG.
[17:21 - 17:25] It needs to be called on a D3 selection object. So what do I mean by that?
[17:26 - 17:33] I'll go ahead and open up a new tab here and type D3 selection object. See what documentation shows up.
[17:34 - 17:42] And maybe what I should also Google is D3 call and see kind of how this works. So I like D3 in depth a lot.
[17:43 - 17:53] And I'm going to trust what they write about the D3 expertise here. The call method allows a function to be called into which the selection itself is passed as the first argument.
[17:54 - 17:57] Okay. And so calls useful where you want a reusable function that operates on a selection.
[17:58 - 18:00] What is the keyword here? Selection selection selection.
[18:01 - 18:11] We're seeing selection a lot. And so remember that D3 select is kind of how D3 transforms, accesses and manip ulates the DOM using its own syntax.
[18:12 - 18:21] As I've said, we like to avoid this whenever we can, but sometimes it's unavoidable. And that's why this lesson is a good introduction into using D3 selection whenever you need to.
[18:22 - 18:29] And so here you see that it's literally the syntax dot select, dot select all, etc. That creates a selection object.
[18:30 - 18:41] So with that, we know that we can't call dot call on the globe element itself. We need to use D3 to select that DOM element first and then call the drag handler.
[18:42 - 18:44] Let's go ahead and do that and see what happens. Okay.
[18:45 - 19:04] What we're going to do is we are going to import select from D3 selection as the auto complete knows. And then we're going to say we're going to create a new variable and call it element, which is equal to the selection of globe.
[19:05 - 19:10] Now let's see how element differs from globe. So online 61.
[19:11 - 19:15] So online 50. Let's get rid of this because this is going to cause some errors for now.
[19:16 - 19:20] Oh, oh my comments are messing up. Here we go.
[19:21 - 19:29] So this is really important, right? Glow is what we see in this object right here, an SVG element with its own attributes, etc.
[19:30 - 19:37] But element, what you see right here is very different. It is an object that has groups and parents.
[19:38 - 19:44] And what you might notice is within the groups is the SVG element that we are looking at. Okay.
[19:45 - 20:00] So what does this really mean? It means that by writing element equals select globe, we're basically taking this SVG, the element itself, and we're moving it up into a broader selection object that includes both groups and parents.
[20:01 - 20:19] And that is what we need to operate on. If we're going to use D three drag handlers, kind of anything that we call a function on call a certain method on that is going to be needed to be called on the element itself, the selection element, rather than the SVG element, rather than this native HTML.
[20:20 - 20:23] So we're almost there. Let's remove globe here.
[20:24 - 20:28] And instead call the drag on element. Let's go ahead and hit save.
[20:29 - 20:31] Let's get rid of these consoles. Hit save.
[20:32 - 20:41] And then let's see what happens if and when I drag. Notice how this is occurring over and over and over again, and move my big head .
[20:42 - 20:51] And notice how I am being dragged is updating 107 times 112 times. It is happening every time I am dragging on a frame within our globe.
[20:52 - 20:55] So great. We know that we can access the drag here.
[20:56 - 21:04] And now the question is how do we rotate the globe accordingly? Well, we want to use this as inspiration, but we don't need all of this.
[21:05 - 21:10] Specifically, we don't need line 66 through 71. And you might be asking why is that?
[21:11 - 21:19] Well, the answer is this assumes that the projection itself was instantiated with something like let or const. And then we need to update it accordingly.
[21:20 - 21:41] But remember that our projection all the way up here was created with the dollar label, meaning it's reactive, so if rotation or these two rotation variables changes, it will update automatically. So what we can do is actually go ahead and delete line 66 through 71 and uncom ment 64.
[21:42 - 21:47] And then let's see what's happening here. We are accessing rotation or rotate from the projections rotate.
[21:48 - 22:00] And then we're creating a new K variable, which is basically representative of sensitivity divided by projection scale. And then what they were doing is basically incrementing rotation according to that K value.
[22:01 - 22:15] So we're going to do something similar, but because we already have a rotation value right here, online 44, we're going to update that instead. So we're going to do is say rotation equals rotation plus.
[22:16 - 22:18] And then what do we want? We could do degrees per frame because we have that.
[22:19 - 22:30] No, we want to account for however hard or however far the user is in fact dragging. And so in order to access that, we need to pass the parameter event into our drag handler.
[22:31 - 22:37] Specifically, we'll do event dot DX. And then let's go ahead and delete this K value actually.
[22:38 - 22:42] Save, see what happens. So you notice the rotation does in fact work.
[22:43 - 22:50] The problem is it's just very, very sensitive. Notice how like just one little thing will take it all the way to the other side of the globe.
[22:51 - 22:56] That's probably too sensitive. So the easiest way to do this is to add some magic number here like .5.
[22:57 - 23:01] So maybe you want it half as fast. Okay, .5 would make it half as fast.
[23:02 - 23:03] Right. And now that looks slightly better.
[23:04 - 23:14] Maybe you want it even slower .25 whatever else. But if you're going to use a magic number here, let's say it is .5, it's probably a good idea to remind yourself in code what that number represents.
[23:15 - 23:34] So you could add a comment here that says .5 is making the drag more sensitive. Or what if we did something smarter where we made this into its own variable and we said const drag sensitivity and we made it .5.
[23:35 - 23:46] And then we reference that drag sensitivity right here. And that way we know immediately looking at this code, what the purpose is and we can just change this value up here if and when we want it to.
[23:47 - 23:56] So now we see that it's slightly slower, it feels more natural, feels a lot better. So I think that's looking a lot better already.
[23:57 - 24:06] And you might notice also that there's this problem where the drag fights with the auto rotation. So even though I'm still holding down and trying to drag, the globe is trying to auto rotate instead.
[24:07 - 24:10] So there's that bit of a dependency issue, right? There's auto rotation and my rotation.
[24:11 - 24:12] Which one should win out? Good question.
[24:13 - 24:22] Well, thankfully we can actually just go ahead and pause our auto rotation if and when the user is dragging. Now how do we do that?
[24:23 - 24:28] The answer is we keep track of whether the user is dragging. So let's say let dragging, that's going to be a variable.
[24:29 - 24:35] Let's make it false. And then within here, if the user is dragging, set dragging equal to true.
[24:36 - 24:49] And then if and when they finish dragging, so on end, that's the other type of like element that you can access within the drag handler on end set dragging equal to false. Close that.
[24:50 - 24:57] And now that we have dragging as a value, what do we want to do in the timer? Let's just say if dragging don't do anything.
[24:58 - 25:03] So if dragging don't do anything. So by default, that is not what we want.
[25:04 - 25:09] We want if dragging return. So now by default, the globe is rotating over and over and over again.
[25:10 - 25:16] But if I start to drag, it freezes. So until I release my cursor, it is going to not rotate.
[25:17 - 25:20] The timer is not going to do anything. As soon as I let go, it resumes.
[25:21 - 25:23] Drag resume. Drag pause resume.
[25:24 - 25:36] It's a really easy way for us to kind of make sure that there's no fighting between the auto rotation and the user directed rotation. So now that we can pause the globe, if we're dragging it, we're almost there.
[25:37 - 25:44] The problem right now is that this is only draggable on the x-axis. So let's go ahead and make it draggable on the y-axis as well.
[25:45 - 25:52] And the first thing that we're going to need to do is update our existing calls here to not just be rotation. We want to name it correctly.
[25:53 - 26:03] So we're going to rename all of rotation to x-rotation. So let's go ahead and change the first instantiation of it, on line 44, to x- rotation.
[26:04 - 26:12] And then what you could do is control F and look up every instance of rotation and make sure you select this guy right here. So it says match whole word.
[26:13 - 26:22] So it's only going to look for rotation as a standalone word. You could also match case to make sure you're targeting the variable and paste in x-rotation.
[26:23 - 26:28] Hit save and you should notice nothing changes. Something changes, undo it because you broke something.
[26:29 - 26:31] Per my direction, you broke something. But that looks like it did work for me.
[26:32 - 26:39] And now that I have x-rotation, I'm just going to copy the exact same logic and do it for y-rotation as well. So I'm going to say y-rotation.
[26:40 - 26:43] I think for this, you might want to do like 30. I can't recall exactly.
[26:44 - 26:49] I think negative 30. And that's going to basically be where we want this to live by default.
[26:50 - 26:54] To confirm, to verify, you can go ahead and add that right here. Make sure it looks okay.
[26:55 - 26:56] Looks good. Maybe you want it to be zero.
[26:57 - 27:01] Maybe you want it to be positive 30. I don't totally recall it.
[27:02 - 27:04] Definitely not positive 30. So maybe you want it to be zero.
[27:05 - 27:06] Let's just do zero. Sounds good.
[27:07 - 27:13] And then now what do we want to do? We basically want to mimic any x-rotation logic and do it for y as well.
[27:14 - 27:30] So y-rotation should be equal to the existing y-rotation plus event dy times drag sensitivity. But because our y-axis is inverted, you're going to notice some issues here where it's basically the opposite of what we want to see.
[27:31 - 27:33] So if I drag up, it goes down. If I drag down, it goes up.
[27:34 - 27:40] That's because the y-axis is kind of inverted here, similar to all of our SVG problems. You want to flip things.
[27:41 - 27:51] So we're going to subtract the event delta rather than add it. And now it follows our cursor one to one and it rotates nicely.
[27:52 - 27:59] So everything is looking great. So we have x-rotation, we have y-rotation, and the rotation stops if and when I 'm dragging.
[28:00 - 28:13] The final thing I want to do is add some momentum, some inertia. If you were actually spinning a real globe, maybe you have one of those globes on your desk and it looks really cool and you can spin it, that doesn't stop spinning the second that you release your hand.
[28:14 - 28:24] That would be very robotic and not native, not how the world really works. To make this look more organic, we want to make it kind of spin beyond our initial drag.
[28:25 - 28:31] What do I mean by that? If I drag and let go, if I was going really fast, I want to continue going and spin accordingly.
[28:32 - 28:39] So we're going to add what's called inertia. And the way that we're going to do that is with Svelte's built-in spring module .
[28:40 - 28:47] So remember that Svelte has a lot of built-in modules. We make great use of Svelte transition, for example, for native DOM transitions .
[28:48 - 29:00] And another handy little toolkit is going to be Svelte's motion modules. Now within motion, there is the spring and the tweened methods.
[29:01 - 29:09] So I'll go ahead and pull up some documentation for that here. I'm going to Google Svelte motion and see what shows up.
[29:10 - 29:20] This Svelte tutorial gives a great overview of spring and tweened. Spring works better than tweened when values are frequently changing, as is the case in our example.
[29:21 - 29:33] So here you'll notice, if you click show me and then play around with it, notice how it kind of follows my cursor and bounces around and lags and whatever else. This is effectively what we want to achieve, just with a drag instead of a cursor move.
[29:34 - 29:40] So we know we're going to be using Svelte's spring function. Now let's break down what's inside of the spring function.
[29:41 - 29:45] Looks like it takes two arguments or two to inputs. The first is the initial values.
[29:46 - 30:00] And the second are some configuration options, including stiffness and damping, which control springiness. So basically, if I were to increase stiffness all the way, you notice it really freaks out, so we probably don't want that.
[30:01 - 30:10] If I were to increase damping all the way, you would notice how much more it l ags behind my cursor. So those are the two variables that we can play around with, those are the two configuration options.
[30:11 - 30:28] And then to update these values, you might notice that we combine the value or you could set it manually in code, like so, you could say, cord, sorry, chords dot x equals and the value, for example. So that's how you could update the spring values.
[30:29 - 30:41] But what we know is that we want our X rotation and our Y rotation to be spring variables. So let's go up to where we first instantiate X rotation and Y rotation.
[30:42 - 30:48] And let's update these to be spring values. So first I need to import spring from spell motion.
[30:49 - 30:58] So obviously I can't springify if I don't have the module. And then let's recall, I'll comment this out, but what should the spring value look like?
[30:59 - 31:05] Well, the first argument is the initial value. So in our case, the initial value of X rotation is zero.
[31:06 - 31:07] And then what is the stiffness and damping? You could use these default values.
[31:08 - 31:18] That's going to work. I found that these numbers point zero eight and a damping of point four tend to work best for the X rotation.
[31:19 - 31:22] That's just what I found for the Y rotation. We can do the same thing.
[31:23 - 31:34] You can set negative 30 is what some people do. I'm going to do zero just zero zero and then I'm going to make a stiffness of point one seven and a damping of point seven.
[31:35 - 31:40] Now I'm going to delete this comment and hit save. And it won't work by default.
[31:41 - 31:43] And this is important. The question is why doesn't it work?
[31:44 - 31:55] Fortunately, the console is not giving us any good information here. And this is kind of a maybe this will be updated in future versions, but it would be helpful if spell it was kind of yelling at us and telling us why this isn't working.
[31:56 - 32:02] If I wanted to see why this isn't working, I could console.log X rotation and hit save. What you're going to notice is it's not a value.
[32:03 - 32:12] Okay, this is important. It's an object with all of these little things within, okay, damping, precision , set, stiffness, subscribe, update.
[32:13 - 32:24] And the reason for this is because springs return objects, okay. And to make this clearer, right, this object has multiple different methods on it, like set.
[32:25 - 32:32] So I could run X rotation dot set and then change it to 100, okay. And that's a method within the object.
[32:33 - 32:43] If I want to access the value itself, what I need to do is prefix X rotation with a dollar sign. I know we love dollar signs and felt we're making all the money.
[32:44 - 32:50] Congratulations. We want to use dollar sign, expectation to access the variable itself.
[32:51 - 32:57] So now everywhere that I'm accessing X rotation, I'm going to replace it with a dollar label X rotation. So you could do this manually.
[32:58 - 33:10] You could do the control F thing I was doing earlier, whatever you want. But then the important thing is where we're actually rotating the projection up here, this definitely needs to be prefixed with a dollar label.
[33:11 - 33:19] So now if I save, notice how X rotation is updating piece by piece. And if I drag, you also notice this wonderful inertia, right?
[33:20 - 33:27] It actually feels like it's kind of natively moving as you would expect with a real globe. So let's just do the same thing with our Y rotation.
[33:28 - 33:36] Basically, just look up every instance of Y rotation. And if you want to make this easier on yourself, just do it in the find and replace hit enter.
[33:37 - 33:43] Bam. Except for the first time you reference it right here, because this is where you're declaring the variable.
[33:44 - 33:48] So that's important. Save that and now Y rotation also has this inertia.
[33:49 - 33:55] Notice that there's kind of this springiness. So maybe the reason for that is the stiffness is too high.
[33:56 - 34:00] So maybe we can change it to 0.1 instead. Now you notice less springiness.
[34:01 - 34:03] So maybe that's preferable. Again, you can play around with these variables.
[34:04 - 34:13] You'll notice we're never updating the Z rotation, which is this Z value. So you could just delete that and it would be effectively the same just to not confuse yourself.
[34:14 - 34:20] So what do we have? We have a beautiful rotatable globe.
[34:21 - 34:28] You can play around with other parameters. Now that we have the base of this down, for example, you could change the drag sensitivity so it is a bit more dynamic.
[34:29 - 34:35] Maybe not three, maybe one or slightly higher than one. Just play around with whatever values work.
[34:36 - 34:45] But we have a rotatable globe that has momentum built in, which I think is beautiful. Probably the final thing that we need, just a slight stylistic fix, styling the cursor.
[34:46 - 34:57] So as I drag, let's add a little grabbing cursor to show the user that they are in fact grabbing. Or maybe one of the resize cursors to show that they can move in any direction.
[34:58 - 35:06] There are those kind of plus cursors. If you're curious about which cursors you can use, you could look up CSS curs ors and kind of find this list.
[35:07 - 35:21] So here MDN Docs, as always, has a really good reference for this. But they're going to show you all of the potential cursor options if they're site loads, where basically you could play around with any of these cursors.
[35:22 - 35:36] And so here if I hover, here you see context menu, help, pointer, progress, wait, sell, crosshair. So what makes sense for ours is either move, so you could do move, or grabbing.
[35:37 - 35:47] And grabbing looks like this, where it literally has the fist holding the globe . Either of those works, but I personally think I'm going to do grabbing.
[35:48 - 36:00] So in order to do that, we need to basically only show it if and when the user is dragging. And the really cool thing is here, we can actually, we have a class, or we have a variable called dragging.
[36:01 - 36:13] So let's just do add a class of dragging if the user is dragging. And because those are the same, it will simplify the shorthand into one and say , okay, this class of dragging will only be applied if the user is dragging.
[36:14 - 36:20] To verify you can look at your inspect element, see what happens as you drag. You see it adds a class.
[36:21 - 36:22] If it is visible for you. Yeah, there you go.
[36:23 - 36:24] CC dragging is added. Remove it.
[36:25 - 36:31] It's no longer visible. So what we want to say is if the user is dragging, add a cursor of grabbing.
[36:32 - 36:33] It's safe. See what happens.
[36:34 - 36:42] Now the user sees their in total control over the drag. If you change that to move, if you like that more, whatever you prefer, this is personal preference.
[36:43 - 36:48] But there we have it. We have a rotatable globe that rotates on its own and when the user wants to rotate it.
[36:49 - 36:52] So I had a lot of fun with this lesson. I think it really brings this globe to the next level.
[36:53 - 36:55] It gives it some life. Hope you liked it too.
[36:56 - 37:02] Reach out with any questions. But otherwise I'll see you in the next lesson where we're going to be adding a tooltip to our globe.
[37:03 - 37:03] Thanks so much. See you there.