Skip to content

Commit

Permalink
feat: make peers page first render fast (#808)
Browse files Browse the repository at this point in the history
* feat: make peers page first render fast

The peers page was taking >2s to load on my machine. Turns out the
d3 generated svg of the world map was around 3MiB

This PR makes extracts the world map to a static svg that is used as
a background image. It's ~79KiB / ~20KiB gzippped and cached as a
static resource rather than generated by js.

The peers page now loads faster than all the others. There is still
too much fan spinning caused by the geo look ups, but this is an
improvement.

* fix: don't repeat the map on larger screens
* fix: center peers map on large screens


License: MIT
Signed-off-by: Oli Evans <oli@tableflip.io>
  • Loading branch information
olizilla committed Sep 25, 2018
1 parent 5764ea1 commit 570d17a
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 64 deletions.
2 changes: 1 addition & 1 deletion src/peers/PeersPage.js
Expand Up @@ -12,7 +12,7 @@ const PeersPage = ({ t }) => (
<Helmet>
<title>{t('title')} - IPFS</title>
</Helmet>
<Box className='pa3'>
<Box className='pt3 ph3 pb4'>
<WorldMap />
<PeersTable />
</Box>
Expand Down
4 changes: 2 additions & 2 deletions src/peers/PeersTable/PeersTable.js
Expand Up @@ -45,8 +45,8 @@ export class PeersTable extends React.Component {
const tableHeight = 320

return (
<div className='flex w-100 bg-white-70' style={{ 'height': `${tableHeight}px` }}>
{ peerLocationsForSwarm && <AutoSizer>
<div className='bg-white-70 center' style={{ 'height': `${tableHeight}px`, maxWidth: 1100 }}>
{ peerLocationsForSwarm && <AutoSizer disableHeight>
{({ width }) => (
<Table
className='tl fw4 w-100 f7'
Expand Down
54 changes: 54 additions & 0 deletions src/peers/WorldMap/Map.js
@@ -0,0 +1,54 @@
import * as d3 from 'd3'
import * as topojson from 'topojson'
import ReactFauxDOM from 'react-faux-dom'
import worldData from './world.json'

// Earth! It's complicated, so try not to re-render so much
const Map = ({ width, height, path }) => {
// https://github.com/d3/d3-geo/blob/master/README.md#geoGraticule
const graticule = d3.geoGraticule()

const el = d3.select(ReactFauxDOM.createElement('svg'))
.attr('width', width)
.attr('height', height)

// Fill Pattern
el.append('defs')
.append('pattern')
.attr('id', 'gridpattern')
.attr('x', 0)
.attr('y', 0)
.attr('width', 5)
.attr('height', 5)
.attr('patternUnits', 'userSpaceOnUse')
.append('circle')
.attr('cx', 3)
.attr('cy', 3)
.attr('r', 1)
.attr('fill', '#AAA')
.attr('stroke', '#DDD')

el.append('path')
.datum(graticule)
.attr('class', 'graticule')
.attr('d', path)

el.insert('path', '.graticule')
.datum(topojson.feature(worldData, worldData.objects.land))
.attr('d', path)
.attr('fill', 'url(#gridpattern)')

el.insert('path', '.graticule')
.datum(topojson.mesh(
worldData,
worldData.objects.countries,
(a, b) => a !== b
))
.attr('d', path)
.attr('fill', 'none')
.attr('stroke', 'none')

return el.node().toReact()
}

export default Map
2 changes: 2 additions & 0 deletions src/peers/WorldMap/StaticMap.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 6 additions & 61 deletions src/peers/WorldMap/WorldMap.js
Expand Up @@ -4,23 +4,16 @@ import { connect } from 'redux-bundler-react'
import { AutoSizer } from 'react-virtualized'
import { translate } from 'react-i18next'
import * as d3 from 'd3'
import * as topojson from 'topojson'

import worldData from './world.json'
import staticMapSrc from './StaticMap.svg'

const WorldMap = ({ t }) => {
return (
<div className='flex w-100 mb4' style={{ 'height': '550px' }}>
<AutoSizer>
{ ({ height, width }) => (
<GeoPath width={width} height={height}>
<div className='flex w-100 mb4' style={{ 'height': '550px', background: `transparent url(${staticMapSrc}) center no-repeat` }}>
<AutoSizer disableHeight>
{ ({ width }) => (
<GeoPath width={width} height={550}>
{ ({ path }) => (
<div className='relative'>
<Map height={height} width={width} path={path} />
<div className='absolute top-0'>
<MapPins height={height} width={width} path={path} />
</div>
</div>
<MapPins height={550} width={width} path={path} />
)}
</GeoPath>
)}
Expand All @@ -47,54 +40,6 @@ const GeoPath = ({ width, height, children }) => {
return children({ path })
}

// Earth! It's complicated, so try not to re-render so much
const Map = ({ width, height, path }) => {
// https://github.com/d3/d3-geo/blob/master/README.md#geoGraticule
const graticule = d3.geoGraticule()

const el = d3.select(ReactFauxDOM.createElement('svg'))
.attr('width', width)
.attr('height', height)

// Fill Pattern
el.append('defs')
.append('pattern')
.attr('id', 'gridpattern')
.attr('x', 0)
.attr('y', 0)
.attr('width', 5)
.attr('height', 5)
.attr('patternUnits', 'userSpaceOnUse')
.append('circle')
.attr('cx', 3)
.attr('cy', 3)
.attr('r', 1)
.attr('fill', '#AAA')
.attr('stroke', '#DDD')

el.append('path')
.datum(graticule)
.attr('class', 'graticule')
.attr('d', path)

el.insert('path', '.graticule')
.datum(topojson.feature(worldData, worldData.objects.land))
.attr('d', path)
.attr('fill', 'url(#gridpattern)')

el.insert('path', '.graticule')
.datum(topojson.mesh(
worldData,
worldData.objects.countries,
(a, b) => a !== b
))
.attr('d', path)
.attr('fill', 'none')
.attr('stroke', 'none')

return el.node().toReact()
}

// Just the dots on the map, this gets called a lot.
const MapPins = connect('selectPeerCoordinates', ({ width, height, path, peerCoordinates }) => {
const el = d3.select(ReactFauxDOM.createElement('svg'))
Expand Down

0 comments on commit 570d17a

Please sign in to comment.