Hi everyone, I think the project I’ve been working on in my spare time for the past three months is ready for a public release!
TLDR: A photoblog mostly written in Elm, uses the partition function to guarantee layout—no ragged edges. An interactive world map displays the location of each image.
Details
I’ve seen a lot of ways people choose to display photos in a gallery, and there’s usually some form of trade-off every time. Either you must conform to some thumbnail size and aspect ratio (example), or you just hope that no-one ever scrolls to the bottom of the page to see a miss-aligned ragged edge (example), or even no chance of previewing the images: just keep clicking and hope that it’s worth it (example).
The general issue here can be considered as a partition problem, which falls into the NP-Complete category of annoying things most people don’t want to deal with.
Luckily this is something I’ve created a package for: elm-partition. There are a number of algorithms which tackle this problem, I’ve got three in the package at the moment, and am working on an anytime implementation. Well, I was until I abandoned finishing it to work on Odyssey. I’ll have time to get back to it soon now I guess.
The perfect layout
So, we can take a list of images, grab their aspect ratios, figure out how many rows we’ll need to make the display look nice based on the current viewport size, then use these two sets of info to create a partition of images that will fit snugly, row by row, right to the end.
To make this responsive was a bit of a chore. Initially I thought there may be some bugs in the way getViewport
was functioning, so asked a roundabout question to that end. However, my problem here was that I needed to know the viewport width after the partition was rendered, to see if a scrollbar was needed. Calling getViewport
before doing so cannot predict the future obviously. To overcome this I have a port that sends me the browser’s scrollbar width and I have a lot of educated guesses and checks to figure out how large the result will be before rendering it. 98% of the time this is working. This was quite a pain point during the design, but I can’t see any way Elm would help the situation at all. I think the Viewport system is really quite great—my problem is outside its scope.
Dynamic backdrop
So you’re going to want to have the ability to put your images in focus in your gallery. With different image sizes & aspect ratios, displayed on screens with different resolutions and orientations; a backdrop to fill in the gaps when the image if fullscreen is a must. Most solutions to this problem is just a black crop box.
To me, this looks much better:
Each image kind of spills out of its bounds, but still remains crisp within its border. This is simply a thumbnail sized version Gaussian blurred with a small grain put on top, but looks far better than a swath of black.
Follow me
The world map in the navigation is something I really love. It lists everywhere I’ve been to in teal. Hover over an image and its location will display on the menu panel, and the globe will rotate to highlight its location.
Select a filter from the list and the selection will be highlighted. A trip will show geodesics of where I went, filtering by country highlights it:
You’re welcome to grab the globe and rotate it however you want, zoom in with the mouse wheel, etc.
Automating the build process
There are a heap of things to do to get this running. The map needs the coordinates of each location, each location needs to be able to find the images that were taken there, as well as the name of the place in the local language. Descriptions are put on some but not all images, trips need information about time and order of travel and a whole lot more.
I’ve distilled it all down to a convention on directory structure: gallery/2017/01/Sweden/Stockholm
encodes date and location, and one config file: odyssey.yaml which lists places and their local name along with trip information. The backend is written in Rust, so I’ll brush over the details since this is an Elm post, but it generates thumbnails, creates the world.json
file needed to build the map, and creates a Manifest.elm
file including all the details needed for the gallery, with coordinates for locations automatically pulled from Nominatim.
Security
Something I’ve been acutely aware of in the past is XSS attacks. I talked about this here a little bit. So from the start I wanted this site to be locked down tight. For this to happen this meant no inline styles, js, anything. An A+ rating (115/100) on Mozilla Observatory shows this is something completely possible and something I think we can all strive for in our apps moving forward.
The future
Please let me know of any problems you see if you come across them. I’ve tested on Android (using chrome), Firefox and Chrome, but I’m not a Mac person, so Safari may not like things. If so, please create an issue on Github.
I’m also happy to hear from you on how I could improve my Elm, any suggestions you have there would be great. One thing I currently don’t use but would like to is Html.Lazy
—I still don’t follow how best to test its use, so would welcome some guidance there.
For the moment, the globe is not native Elm, I’m porting out the drawing of that to D3.
I’ve started work in remedying this situation: elm-topojson is a functioning TopoJSON parser. But I’d like to hear from everyone about the next steps here, particularly those working on elm-visualisation and other svg based libraries. I don’t think just strait porting d3-geo and the like is the best way forward. So if others are interested in designing a good mapping package with me please get in contact.
Anyhow, this has become quite a long post, if you’d like to know more about implementation, let me know below; or if you have suggestions etc, I’d love to hear them.