From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
Today we’d like to share an experimental demo with you. This demo is an interactive map that will animate a map path while scrolling the page. The main idea is to connect the story being told with the path itself. The journey can also contain images that will indicate where they have been taken with a semi-transparent cone. It will appear as soon as the image is in the visible viewport. A little plane icon is used in the last bit of the path in order to indicate that the journey was made on an aircraft.
- ChromeSupported
- FirefoxSupported
- Internet ExplorerNot supported
- SafariSupported
- OperaSupported
The photography is courtesy of Dan Rubin who has travelled to Iceland and has taken some incredible shots that we used in the demo. Find him on Instagram, Twitter, Flickr and VSCO.
To make this map, we chose canvas
for rendering for performance reasons. It could be done purely with SVG, but at the time of writing, SVG rendering performance isn’t quite there yet, while canvas
is hardware accelerated on most platforms.
That’s not to say we won’t use SVG at all, though. On the contrary: to draw on the canvas, we need to get the map graphics from somewhere, and maps are perfectly suited for vector formats like SVG. Moreover, we also need to get trail and point data, and SVG is a great candidate to store that information.
So now let’s see how we can use both together.
SVG + Canvas
First, we need to prepare our map. What we will need from it, for this project:
- The trail path;
- The path of the “camera”;
- Points of interest, with names;
- And of course, the map itself.
Both paths are simple… well, <path>
elements, that can be drawn with any Bézier curve tool or similar.
<path>
elements instead of other primitives such as <polygon>
, <line>
, etc. If you are using Illustrator, create a Compound Path by selecting the object and going to Edit > Compound Path > Make
or by pressing Command/Ctrl+8 to convert the object to <path>
Points of interest can be stored using <circle>
elements, created with the regular ellipse tool. The names of the points can be set as the name of their layers, and you can retrieve them later with getAttribute('id')
.
Speaking of layer names, don’t forget to set the name of the layers of these “special” graphics, because we’ll need them in order to access the layers later by code.
Finally, the map itself can be just any graphic. You can draw anything you want on top of it too, like additional points of interest, terrain, parallels, etc.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
Drawing SVG on canvas
is simple, but comes with a caveat: you can only draw straight from actual .svg files, you can’t modify the SVG with code before drawing. Therefore, you have to hide all the special layers of the SVG (camera path and the like) before saving. Loading the SVG both as a regular <img>
for drawing in canvas and as a <svg>
element for accessing data means that we have to load it two times and hope the browser cache saves us from having to download it twice.
The code could look like this (using ES2015 and Promises):
let mapFile='map.svg'
let mapSVG, mapImage
ajaxGet(mapFile).then(response=>{ // Load the SVG with an Ajax request.
mapSVG=Array.from( // Makes a new array from...
new DOMParser()
.parseFromString(response,'image/svg+xml')
.childNodes // ...the NodeList we get from the parsed SVG file.
) // It returns a handful of nodes, like <!doctype,
.filter(node=>{ // but we only need the svg element, so we filter that
let tag=node.tagName // array looking for any elements ...
if(typeof tag=='undefined')
return false
return tag.toLowerCase()==='svg' // ...that have the tag name svg...
})[0] // ...and we get the first (and usually only) one.
loadImage(mapFile).then(img=>{ // Then we load the image again,
mapImage=img // this time as an `img` element.
...
The performance gains for using canvas
will be nullified, though, if we draw from the SVG on every frame, especially if your map is complex. There are some techniques you can use to avoid that.
The simplest one is creating a hidden canvas and drawing the SVG there once. Then, you draw on the main canvas from that hidden canvas instead of drawing from the SVG: drawing from a bitmap source is much faster.
That might come with some problems though: one of the biggest reasons for using vector graphics is being able to zoom without losing resolution, and that is lost if we draw on a canvas that’s too small. Using a huge canvas for drawing on every frame, though, can get pretty slow.
A technique we can employ to mitigate both issues is keeping a high resolution copy of the map, and using a reasonably sized buffer canvas for moving – and refresh that buffer only when we move or zoom too much.
Doing this allows us to only draw from the high resolution image when we need it, and doesn’t restrict us to using low resolutions.
Paths
We can move through an SVG path using a built-in <path>
function: getPointAtLength
. It returns the position of a point on an arbitrary length along the path. We can get the total length of the path using getTotalLength
.
let path = mapSVG.querySelector('#camera-path path') // Gets the path element
let length = mapSVG.getTotalLength() // Gets the length of the path
// To get, for example, the position of a point halfway through the path
path.getPointAtLength(length*0.5) // returns {x: ..., y: ...}
It would be useful to be able to tell how far along the path each point of interest is located. Unfortunately, there’s no getLengthAtPoint
function (of course, because there’s no guarantee that a point crosses the path at all).
We can approximate that, however, getting a length that is more or less close to the point, using simple trigonometry and some iterations:
We can use the same getPointAtLength
function to draw the trail on the canvas, running the function a hundred of times, storing the points in an array, and then drawing lines from that array. Alternately, we can parse the d
attribute from the <path>
element and use similar canvas
drawing commands.
Hopefully these techniques will give you a bit of insight into how something like this can be achieved. Now, let’s have a look at the results of our implementation.
The Map in Action
At the beginning of the journey, we show the map and when we start scrolling, the path animation begins.
When linking an image in the story with the path, we show a cone that indicates where the picture was taken:
There are points that can be zoomed and paused at, in order to show a gallery or other relevant parts of the story:
For the final bit, we use a little plane icon to indicate that this part of the journey was taken on an aircraft:
We hope you enjoy this experiment and find it inspiring!
GitHub link coming soon!
i love it! but i need your help…
i can’t solve the non-showing-map-problem when i download the project 🙁
Wow! This has to be the the best way of showing a journey that I have seen. I have been thinking about how I could do this on the website I am building for the cruise industry. Just not sure if my talents can stretch to using this method; however one day I will try.
Brilliant!
Awesome idea and nice work. beautiful
This is brilliant! Thanks for sharing!
This is amazing! This gives me a lot of ideas for future projects. Thank you.
nice idea but still cross browser issues found
map not working on safari
the guys at hinderlingvolkart (a swiss based agency) did something similar some time ago: http://seidenstrasse.srf.ch/de/home.html
That’s a nice article. I would like to create such map for an interesting trip – Can anyone who’s good with illustrator help me on creating the required SVG? Has been stuck on that part!! You can reach me out on vamshi4001[at]gmail[dot]com
brilliant.
Great post thanks for posting
Hey ! Any news about the github link ? 🙂
Thanks !
Thank you very much Lucas ! It’s very impressive
I try tu use it for a project, and it seems to be incredibly well-thinked.
I have just a problem : for a reason i ignore, some points are not well detected on the path
Path which is a little bit complicated (with many “loops”…)
Is there a way to improve that ? Do you have best practices for positionning points among the path ?
Thank you very much !
Hi Thibaut,
I have exactly the same problem. Did you found a fix ?
I did many, many tests, and to me the problem has nothing to do with the SVG image, and neither the way you created paths and the circles. I mean not directly.
It appears to me that when points/circles are very close to other points (a very frequent case when you have a complicated path with loops), the script tries to determine the next point on the path, and probably due to the journey path and also to the method used to locate the point (in a trigonometric way without care for the order of the points on the path ?), it returns, not the very next point on the path, but another one, geometically closest…
This is very frustating, as this script is awsome and very well-thinked as you said.
I guess that a tightest control on the order of the the points would solve this problem (for example with the help of a nomenclature for naming the layer of the points, or with a strict order of the layers of the points, or even sometimes partial, assuming that a point can be use only once, it would avoid this trouble for the previous points on the path). But i can not figure out this huge script enought to customize it.
Best regards and special thanks to Lucas !
Thank you Lucas. A very smart and impressive work! For Thibaut and David. The algorithm, as it is, do not allow to run through the same point. What I find as solution is to create a cloned second point (later in the circles “timeline”) moved by some pixels on the x or y (or both axes), a bit before the original point. Then I set its radius at something very small, like 0.1. Beforehand I moved, during creation of the points on canvas, every point on the trail path using a similar algorithm I found on this blog https://bl.ocks.org/mbostock/8027637. It works well. The concept is that the trail must “always” go further to calculate its length. I hope it helps!
Hi
i was creating similar kind with india map and built svg as you suggested
I was getting errors as
app.js:3 Uncaught (in promise) TypeError: Cannot read property ‘getTotalLength’ of null(…)s
app.js:2 Uncaught TypeError: Cannot read property ‘setAttribute’ of null
Could you please help me out on this
Looking forward to your reply
Thanks
I have the map problem also. However i noticed when I set the path to zoom 0; when it got so tiny you could hardly see it the map was revealed to be off the canvas by a mile!
It would seem it is something to do with the way Illustrator deals with SVG.
Is there a way to move vectors in SVG files by using a text editor like Sublime Text? or maybe it is my path that need to be reset?
I found a solution to the map not working.
When I open and saved the map.svg in this editor http://editor.method.ac/ It worked.
There must be issues with Illustrator.
Just to show what is happening to the map on the SVG. Please see the following screenshots of how far the map is off the canvas/viewport… Similarly what ever I drop into the SVG is always that far off the canavas.
In illustrator it looks like this http://prntscr.com/blluwk
On the page it looks like this http://prntscr.com/bllvap
Hi Lucas,
So I have been trying for a few days now to draw my own map in the map.svg. But as soon as I drop anything inside of the file and then save the map and anything that is not a trail path, cam path or point of interest gets thrown out of the view port. As in my previous comments you can see the green square completely out of view only when I zoom out enough.
I cannot find anything to explain this phenomenon in Illustrator using SVG files.
Do you have any idea at all what is causing this?
I have also tried Inkscape, but the SVG layers are not visible.
hello benjamin, from where i can dowload usa svg map similar to iceland map tat lucas used?
I do not have words, just amazing, I’m looking for something like that for a long time, Congrats!!!!
This is impressive. Thanks for sharing!
Hello – in the code you have a TODO that hasn’t been handled – ‘TODO: diff to test if it should render’ in component.js – I’m not sure I get what you were wanting to do? Can you explain quickly.
Lovely work…
Hoi, I’ve been trying to re-work your awesome script, to fit in a more dynamic environment.
My goal was the following: Instead of listening to body`s scroll event, achieve to listen the scroll inside any div.
So far I tried to do the next:
this: document.addEventListener(“scroll” …
into this: document.getElementById(‘about’).addEventListener(“scroll” …
Nothing worked, I would appreciate any help, or at least, what js will let me draw that path, move and zoom the camera,
so I could write it on my own.
Hello. I’m a developer and I try to understand how your map works. It would be very helpful if you could explain me how iterator in path.js works (iterate(lower,upper)).
The problem is that with my svg file integrated the drawn path is not correct. Points are placed not in the needed order.
Do you think something like this could be implemented in an HTML email?
This new tool is unbelievable for me! I tried to use the demo and it`s fantastic. I am sure people who will read any text with the path like it, because it is very easy to understand the sense when you`re watching text, image and even path.
And I have also seen something very similar on this site http://www.olyablack.com/. It`s great when technology is growing up!)
I did a review of my trip to South Korea with it, it works really great !
You can check it here : http://www.ariamondu.com/coree2016
Thank you for the great script !
hello jean, Please tell me from where u downloaded the map fro south korea?
Hello,
Getting Uncaught SyntaxError: Invalid or unexpected token in dist/app.js , and the map is not getting displayed at all. What am I missing here.
Kindly help.
Your idea is a good one now a days these kinds of animations are loved by everyone, Good start to reach all kinds of people.
I love this idea! Very visually appealing! Does anyone know how to draw a jpeg image using canvas as the map vs. an svg.? so far I haven’t been able to figure out which parts of the code are actually drawing the canvas map.
Were you able to figure this out, Cam? I’ve been looking into this same idea but haven’t found a solution yet. I’ll post back here if I do…
Hi guys , how can I instal this plugin in my web site by worpress ?
from where i can dowload usa svg map similar to iceland map tat lucas used?
How can i download all of these animated map path ?
For all the people with problems rendering the map (the whole map doesn’t show up or only the path is visible), the original document was created in Adobe Illustrator CS5. When you open it on a newer version and save it, the original structure will change and somehow the code does not deal well with that. I’ve spent several hours looking for a solution, so hope this info can help you.
Have you found a solution? I’m still trying to figure it out:)
Has anyone been able to figure this out yet?
save your map on this site, it will work. http://editor.method.ac/
Okay I think the issue is the styles. This script doesn’t work with Illustrator’s new method of including rules for fill, etc. The new AI method would produce an SVG with
.st0{fill:none;stroke:#010101;stroke-miterlimit:10;}
.st1{fill:none;stroke:#EC2227;stroke-width:3;stroke-miterlimit:10;}
.st2{fill:#405B54;}
etc etc
but for this script to work as is, you would need to move all that to inline:
<path class="st3" fill="97DAFF" d="blah blah…"
Thank you for sharing this interesting topic about animated map path in interactive story telling.
I want a functional version for this template please, because I am a beginner in javascript
THAT IS GREAT, THANKS SO MUCH
I would love a solution like this wonderful one but without Illustrator as I am a poor person with limited knowledge…
Do you know of any timeline- or itinary-implementations done with other techniques but similar effect?
Awesome, thank you!
Awesome. Grate work.
Is possible to create second line animation from different point, once you reached? Example Once you reached Gullfoss, the flight animation starts from Eyjafjallajökull.
Brother, que projeto bunito! Parabéns mesmo. Uma hora vou investir umas horas pra ver se tenho as skills necessárias para fazer algo (pelo menos perto) dele. Muito Sucesso!
Hi,
Impressive tool. I’m starting to develop a widget allowing people to create their own storytelling.
I could insert a world map and create a fake travel, everything work. The most difficult part will be the UI to pick a road on a map (aka Google map) and transform it as SVG path. But I’m on it 🙂
Hi ! How is doing with your widget? I look forward to could create some great stories with your work and amazing Lucas’ story telling tools.
Hi! Nice work!
I have a question!
How can I put a linked image into de map.svg?
I placed a png into the svg and saved it with linked option selected.
When I open the index file it just dont show the linked image. But If I open the map.svg file in any browser, the linked image shows up!
Is there any way to solve this?
Is it possible for you to include the Illustrator file as part of the source download? I seem to be having issues with the export of the svg file and I am wondering if I have something wrong in the actual file. I tried opening the svg in Illustrator and seeing if anything is different, but it isn’t different so I am wondering if the the actual Illustrator file looks different than an opened svg file. Thanks!
Wow… Thank you for sharing functional animated version of mapping.
Hello, I’m having issues with displaying the map in the left side, is it possible? Thanks..
Ok, the effect is really impressive indeed but… I did my own maps two hundred times according to what you said in this tutto and it never works as it should be. Sometimes the path work in the opposite of what I need. Sometimes nothing appear, other times it’s only the map… Could you tell us what is the exact parameters of the .svg recording.