Renderless North Pole distance
NorthPoleDistance
is a renderless component, meaning it only performs some logic and passes
props to its slot. The consumer of the component can read those props inside the slot (with let:prop)
and decide what to do with them.
Example 1
Waiting for location data...
Example 2
Waiting for location data...
Challenge
Source code
+page.svelte
<script>
import NorthPoleDistance from './NorthPoleDistance.svelte';
</script>
<h1>Renderless North Pole distance</h1>
<p>
<code>NorthPoleDistance</code> is a renderless component, meaning it only performs some logic and passes
props to its slot. The consumer of the component can read those props inside the slot (with let:prop)
and decide what to do with them.
</p>
<div class="flow container">
<h2>Example 1</h2>
<NorthPoleDistance let:distance let:toggleUnit let:unit>
<p>You are currently: {distance} {unit}s away from the North Pole.</p>
<button class="button-green" on:click={toggleUnit}>Toggle</button>
</NorthPoleDistance>
<h2>Example 2</h2>
<NorthPoleDistance let:distance let:toggleUnit let:unit>
<div class="example-2 flow">
<p>Current distance from the North Pole</p>
<p class="distance">{distance} {unit}</p>
<button on:click={toggleUnit} class="button-red">
Switch to {unit === 'km' ? 'miles' : 'km'}
</button>
</div>
</NorthPoleDistance>
</div>
<style>
.container {
width: 100%;
max-width: var(--size-content-2);
}
button {
appearance: none;
border: none;
background: none;
cursor: pointer;
transition: transform 0.2s ease;
color: white;
padding: 0.5rem 1rem;
border-radius: var(--radius-2);
}
.button-green {
background: var(--green-7);
}
.button-red {
background: var(--red-6);
}
button:hover {
filter: brightness(0.95);
}
button:active {
transform: scale(0.95);
}
.example-2 {
text-align: center;
border: 2px solid black;
border-radius: var(--radius-3);
padding: 1rem;
}
.distance {
font-size: var(--font-size-4);
}
</style>
NorthPoleDistance.svelte
<script lang="ts">
import { getDistanceKm, getDistanceMiles } from './distance';
import { geolocation } from './geolocation';
$: distance =
unit === 'km'
? getDistanceKm($geolocation.latitude, $geolocation.longitude)
: getDistanceMiles($geolocation.latitude, $geolocation.longitude);
let unit: 'km' | 'mile' = 'mile';
function toggleUnit() {
if (unit === 'km') {
unit = 'mile';
} else {
unit = 'km';
}
}
</script>
{#if isNaN(distance)}
<p>Waiting for location data...</p>
{:else}
<slot {distance} {toggleUnit} {unit} />
{/if}
distance.ts
// this file has helpers to get the distance from a given latitude-longtitude to the north pole
// source: https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula
export function getDistanceKm(lat: number, lon: number) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(90 - lat); // deg2rad below
var dLon = deg2rad(135 - lon);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat)) * Math.cos(deg2rad(90)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return Math.round(d);
}
export function getDistanceMiles(lat: number, lon: number) {
return Math.round(getDistanceKm(lat, lon) * 0.621371);
}
export function deg2rad(deg: number) {
return deg * (Math.PI / 180);
}
geolocation.ts
import { browser } from '$app/environment';
import { readable } from 'svelte/store';
export const geolocation = readable<GeolocationCoordinates>(
{
accuracy: 0,
latitude: Infinity,
longitude: Infinity,
altitude: null,
altitudeAccuracy: null,
heading: null,
speed: null
},
(set) => {
if (browser) {
let watcher = navigator.geolocation.watchPosition((pos) => set(pos.coords));
return () => {
navigator.geolocation.clearWatch(watcher);
};
}
}
);