A first experiment getting to know Svelte 3.
Bootstrapped from Svelte Template.
Strongly considering adding Svelte support to VizHub.
Seeing huge potential for Svelte with interactive data visualization!
<!doctype html>
<html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Svelte app</title>
<link rel='stylesheet' href='global.css'>
</head>
<body>
<script src='bundle.js'></script>
</body>
</html>
<script>
export let width;
export let height;
export let n = 50;
let numbers = [];
const f = (x, t) => Math.sin(x * t + t) * 200;
const setNumbers = t => {
for(let i = 0; i < n; i++){
const x = i / n;
numbers[i] = f(x, t);
}
};
const animate = timestamp => {
setNumbers(timestamp / 1000);
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
</script>
<svg width={width} height={height}>
{#each numbers as y, i}
<circle
cx={i / n * width}
cy={y + height / 2}
r="10"
/>
{#if i < n - 1}
<line
x1={i / n * width}
y1={y + height / 2}
x2={(i + 1) / n * width}
y2={numbers[i + 1] + height / 2}
/>
{/if}
{/each}
</svg>
var app = (function () {
'use strict';
function noop() {}
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i]) iterations[i].d(detaching);
}
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function empty() {
return text('');
}
function attr(node, attribute, value) {
if (value == null) node.removeAttribute(attribute);
else node.setAttribute(attribute, value);
}
function children(element) {
return Array.from(element.childNodes);
}
let current_component;
function set_current_component(component) {
current_component = component;
}
const dirty_components = [];
let update_promise;
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
function schedule_update() {
if (!update_promise) {
update_promise = Promise.resolve();
update_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
function flush() {
const seen_callbacks = new Set();
do {
// first, call beforeUpdate functions
// and update components
while (dirty_components.length) {
const component = dirty_components.shift();
set_current_component(component);
update(component.$$);
}
while (binding_callbacks.length) binding_callbacks.shift()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
while (render_callbacks.length) {
const callback = render_callbacks.pop();
if (!seen_callbacks.has(callback)) {
callback();
// ...so guard against infinite loops
seen_callbacks.add(callback);
}
}
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_promise = null;
}
function update($$) {
if ($$.fragment) {
$$.update($$.dirty);
run_all($$.before_render);
$$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null;
$$.after_render.forEach(add_render_callback);
}
}
function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_render } = component.$$;
fragment.m(target, anchor);
// onMount happens after the initial afterUpdate. Because
// afterUpdate callbacks happen in reverse order (inner first)
// we schedule onMount callbacks before afterUpdate callbacks
add_render_callback(() => {
const new_on_destroy = on_mount.map(run).filter(is_function);
if (on_destroy) {
on_destroy.push(...new_on_destroy);
} else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
after_render.forEach(add_render_callback);
}
function destroy(component, detaching) {
if (component.$$) {
run_all(component.$$.on_destroy);
component.$$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
component.$$.on_destroy = component.$$.fragment = null;
component.$$.ctx = {};
}
}
function make_dirty(component, key) {
if (!component.$$.dirty) {
dirty_components.push(component);
schedule_update();
component.$$.dirty = {};
}
component.$$.dirty[key] = true;
}
function init(component, options, instance, create_fragment, not_equal$$1, prop_names) {
const parent_component = current_component;
set_current_component(component);
const props = options.props || {};
const $$ = component.$$ = {
fragment: null,
ctx: null,
// state
props: prop_names,
update: noop,
not_equal: not_equal$$1,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
before_render: [],
after_render: [],
context: new Map(parent_component ? parent_component.$$.context : []),
// everything else
callbacks: blank_object(),
dirty: null
};
let ready = false;
$$.ctx = instance
? instance(component, props, (key, value) => {
if ($$.ctx && not_equal$$1($$.ctx[key], $$.ctx[key] = value)) {
if ($$.bound[key]) $$.bound[key](value);
if (ready) make_dirty(component, key);
}
})
: props;
$$.update();
ready = true;
run_all($$.before_render);
$$.fragment = create_fragment($$.ctx);
if (options.target) {
if (options.hydrate) {
$$.fragment.l(children(options.target));
} else {
$$.fragment.c();
}
if (options.intro && component.$$.fragment.i) component.$$.fragment.i();
mount_component(component, options.target, options.anchor);
flush();
}
set_current_component(parent_component);
}
class SvelteComponent {
$destroy() {
destroy(this, true);
this.$destroy = noop;
}
$on(type, callback) {
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1) callbacks.splice(index, 1);
};
}
$set() {
// overridden by instance, if it has props
}
}
class SvelteComponentDev extends SvelteComponent {
constructor(options) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`);
}
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console
};
}
}
/* App.svelte generated by Svelte v3.0.0 */
const file = "App.svelte";
function get_each_context(ctx, list, i) {
const child_ctx = Object.create(ctx);
child_ctx.y = list[i];
child_ctx.i = i;
return child_ctx;
}
// (31:4) {#if i < n - 1}
function create_if_block(ctx) {
var line, line_x__value, line_y__value, line_x__value_1, line_y__value_1;
return {
c: function create() {
line = svg_element("line");
attr(line, "x1", line_x__value = ctx.i / ctx.n * ctx.width);
attr(line, "y1", line_y__value = ctx.y + ctx.height / 2);
attr(line, "x2", line_x__value_1 = (ctx.i + 1) / ctx.n * ctx.width);
attr(line, "y2", line_y__value_1 = ctx.numbers[ctx.i + 1] + ctx.height / 2);
add_location(line, file, 31, 6, 587);
},
m: function mount(target, anchor) {
insert(target, line, anchor);
},
p: function update(changed, ctx) {
if ((changed.n || changed.width) && line_x__value !== (line_x__value = ctx.i / ctx.n * ctx.width)) {
attr(line, "x1", line_x__value);
}
if ((changed.numbers || changed.height) && line_y__value !== (line_y__value = ctx.y + ctx.height / 2)) {
attr(line, "y1", line_y__value);
}
if ((changed.n || changed.width) && line_x__value_1 !== (line_x__value_1 = (ctx.i + 1) / ctx.n * ctx.width)) {
attr(line, "x2", line_x__value_1);
}
if ((changed.numbers || changed.height) && line_y__value_1 !== (line_y__value_1 = ctx.numbers[ctx.i + 1] + ctx.height / 2)) {
attr(line, "y2", line_y__value_1);
}
},
d: function destroy(detaching) {
if (detaching) {
detach(line);
}
}
};
}
// (25:2) {#each numbers as y, i}
function create_each_block(ctx) {
var circle, circle_cx_value, circle_cy_value, if_block_anchor;
var if_block = (ctx.i < ctx.n - 1) && create_if_block(ctx);
return {
c: function create() {
circle = svg_element("circle");
if (if_block) if_block.c();
if_block_anchor = empty();
attr(circle, "cx", circle_cx_value = ctx.i / ctx.n * ctx.width);
attr(circle, "cy", circle_cy_value = ctx.y + ctx.height / 2);
attr(circle, "r", "10");
add_location(circle, file, 25, 4, 482);
},
m: function mount(target, anchor) {
insert(target, circle, anchor);
if (if_block) if_block.m(target, anchor);
insert(target, if_block_anchor, anchor);
},
p: function update(changed, ctx) {
if ((changed.n || changed.width) && circle_cx_value !== (circle_cx_value = ctx.i / ctx.n * ctx.width)) {
attr(circle, "cx", circle_cx_value);
}
if ((changed.numbers || changed.height) && circle_cy_value !== (circle_cy_value = ctx.y + ctx.height / 2)) {
attr(circle, "cy", circle_cy_value);
}
if (ctx.i < ctx.n - 1) {
if (if_block) {
if_block.p(changed, ctx);
} else {
if_block = create_if_block(ctx);
if_block.c();
if_block.m(if_block_anchor.parentNode, if_block_anchor);
}
} else if (if_block) {
if_block.d(1);
if_block = null;
}
},
d: function destroy(detaching) {
if (detaching) {
detach(circle);
}
if (if_block) if_block.d(detaching);
if (detaching) {
detach(if_block_anchor);
}
}
};
}
function create_fragment(ctx) {
var svg;
var each_value = ctx.numbers;
var each_blocks = [];
for (var i = 0; i < each_value.length; i += 1) {
each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
}
return {
c: function create() {
svg = svg_element("svg");
for (var i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
attr(svg, "width", ctx.width);
attr(svg, "height", ctx.height);
add_location(svg, file, 23, 0, 416);
},
l: function claim(nodes) {
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
},
m: function mount(target, anchor) {
insert(target, svg, anchor);
for (var i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(svg, null);
}
},
p: function update(changed, ctx) {
if (changed.n || changed.width || changed.numbers || changed.height) {
each_value = ctx.numbers;
for (var i = 0; i < each_value.length; i += 1) {
const child_ctx = get_each_context(ctx, each_value, i);
if (each_blocks[i]) {
each_blocks[i].p(changed, child_ctx);
} else {
each_blocks[i] = create_each_block(child_ctx);
each_blocks[i].c();
each_blocks[i].m(svg, null);
}
}
for (; i < each_blocks.length; i += 1) {
each_blocks[i].d(1);
}
each_blocks.length = each_value.length;
}
if (changed.width) {
attr(svg, "width", ctx.width);
}
if (changed.height) {
attr(svg, "height", ctx.height);
}
},
i: noop,
o: noop,
d: function destroy(detaching) {
if (detaching) {
detach(svg);
}
destroy_each(each_blocks, detaching);
}
};
}
function instance($$self, $$props, $$invalidate) {
let { width, height, n = 50 } = $$props;
let numbers = [];
const f = (x, t) => Math.sin(x * t + t) * 200;
const setNumbers = t => {
for(let i = 0; i < n; i++){
const x = i / n;
numbers[i] = f(x, t); $$invalidate('numbers', numbers);
}
};
const animate = timestamp => {
setNumbers(timestamp / 1000);
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
$$self.$set = $$props => {
if ('width' in $$props) $$invalidate('width', width = $$props.width);
if ('height' in $$props) $$invalidate('height', height = $$props.height);
if ('n' in $$props) $$invalidate('n', n = $$props.n);
};
return { width, height, n, numbers };
}
class App extends SvelteComponentDev {
constructor(options) {
super(options);
init(this, options, instance, create_fragment, safe_not_equal, ["width", "height", "n"]);
const { ctx } = this.$$;
const props = options.props || {};
if (ctx.width === undefined && !('width' in props)) {
console.warn("<App> was created without expected prop 'width'");
}
if (ctx.height === undefined && !('height' in props)) {
console.warn("<App> was created without expected prop 'height'");
}
if (ctx.n === undefined && !('n' in props)) {
console.warn("<App> was created without expected prop 'n'");
}
}
get width() {
throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
set width(value) {
throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
get height() {
throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
set height(value) {
throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
get n() {
throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
set n(value) {
throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}
}
var main = new App({
target: document.body,
props: {
width: 960,
height: 500
}
});
return main;
}());
//# sourceMappingURL=bundle.js.map
body {
margin: 0;
}
line {
stroke: gray;
stroke-width: 2px;
}
import App from './App.svelte';
export default new App({
target: document.body,
props: {
width: 960,
height: 500
}
});
{
"name": "svelte-app",
"version": "1.0.0",
"devDependencies": {
"npm-run-all": "^4.1.5",
"rollup": "^1.10.1",
"rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^4.0.4",
"sirv-cli": "^0.3.1",
"svelte": "^3.0.0"
},
"scripts": {
"build": "rollup -c",
"autobuild": "rollup -c -w",
"dev": "run-p start:dev autobuild",
"start": "sirv .",
"start:dev": "sirv . --dev"
}
}
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file — better for performance
css: css => {
css.write('public/bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration —
// consult the documentation for details:
// https://github.com/rollup/rollup-plugin-commonjs
resolve(),
commonjs(),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
]
};