Svelte is a component framework for the web that works at build time, as opposed to frameworks like React that work at runtime. This post will demonstrate how to set up a basic CesiumJS application built on Svelte.
Setup
First, we’ll clone the Svelte startup template. We will be enabling Typescript in the project to take advantage of Cesium’s TypeScript support.
# Set up svelte template
npx degit sveltejs/template cesium-svelte-app
cd cesium-svelte-app
# Enable TypeScript
node scripts/setupTypeScript.js
# Install Svelte dependencies
npm install
# Add cesium as a dependency
npm install cesium
Configuring Rollup
This Svelte template uses Rollup, so we’ll need to perform some additional configuration to allow Cesium’s static assets to be imported. So, first, we’ll install the required Rollup dependencies:
# Install Rollup plugins
npm install --save-dev rollup-plugin-copy
npm install --save-dev rollup-plugin-postcss
Then, let’s make sure the static assets are copied in the build process in rollup.config.js
import postcss from 'rollup-plugin-postcss';
import copy from 'rollup-plugin-copy';
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';
import path from 'path';
const production = !process.env.ROLLUP_WATCH;
const cesiumBuildPath = 'node_modules/cesium/Build/Cesium'
export default {
input: 'src/main.ts',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
dev: !production,
css: css => {
css.write('public/build/bundle.css');
},
preprocess: sveltePreprocess(),
}),
resolve({
browser: true,
dedupe: ['svelte']
}),
postcss({
extensions: [ '.css' ]
}),
copy({
targets: [
{ src: path.join(cesiumBuildPath, 'Assets'), dest: 'public/build/' },
{ src: path.join(cesiumBuildPath, 'ThirdParty'), dest: 'public/build/' },
{ src: path.join(cesiumBuildPath, 'Widgets'), dest: 'public/build/' },
{ src: path.join(cesiumBuildPath, 'Workers'), dest: 'public/build/' },
]
}),
commonjs(),
typescript({ sourceMap: !production }),
!production && serve(),
!production && livereload('public'),
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
Adding the Cesium Viewer
Inside App.svelte
, let’s add the Cesium viewer:
<script lang="ts">
import { onMount } from 'svelte';
import { Viewer } from 'cesium';
import '../node_modules/cesium/Build/Cesium/Widgets/widgets.css'
window.CESIUM_BASE_URL = './build';
let viewer: Viewer;
onMount(async () => {
viewer = new Viewer('cesiumContainer');
});
</script>
<div id="cesiumContainer">
</div>
To ensure that the Cesium container takes up the full screen, add the following CSS snippet to global.css
:
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
Now, we’re ready to run:
npm run dev
And we should have a Cesium Viewer up and running with Svelte!
Creating a simple Infobox component
Finally, let’s add a simple Infobox component to display information about the currently selected entity. Create a new file called Infobox.svelte
in the src
directory.
<script lang="ts">
import type { Entity } from "cesium";
export let entity: Entity = undefined;
</script>
<style>
#cesiumInfobox {
position: absolute;
z-index: 2;
right: 10px;
top: 10px;
color: white;
background-color: gray;
width: 10%;
height: 5%;
font-family: 'Courier New', Courier, monospace;
font-size: large;
border-radius: 5px;
display: flex;
align-items: center;
text-align: center;
}
#entityNameLabel {
width: 100%;
flex: 1;
}
</style>
<div id="cesiumInfobox">
<label id="entityNameLabel">{entity.name}</label>
</div>
To display this new component, we need to add it to our main App.svelte
component:
<script lang="ts">
window.CESIUM_BASE_URL = './build';
import { onMount }from 'svelte';
import { Viewer, defined, Cartesian3, Color, Entity } from 'cesium';
import Infobox from './Infobox.svelte';
import '../node_modules/cesium/Build/Cesium/Widgets/widgets.css'
let viewer: Viewer;
let entity: Entity;
onMount(async () => {
viewer = new Viewer('cesiumContainer', {
baseLayerPicker : false,
infoBox: false,
geocoder: false,
homeButton: false,
navigationHelpButton: false,
sceneModePicker: false
});
// Add sample entity to select.
var blueBox = viewer.entities.add({
name: "Blue box",
position: Cartesian3.fromDegrees(-114.0, 40.0, 300000.0),
box: {
dimensions: new Cartesian3(400000.0, 300000.0, 500000.0),
material: Color.BLUE,
},
});
// Update entity object on selection/deselection.
viewer.selectedEntityChanged.addEventListener(() => {
entity = viewer.selectedEntity;
});
});
</script>
<div id="cesiumContainer">
<!--Only show Infobox, if entity is defined-->
{#if defined(entity)}
<Infobox entity={entity} />
{/if}
</div>
Now, when we click on our entity, we should have an infobox appear with its name!