diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 19a19c4f41..14093b5264 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -433,6 +433,19 @@ Out of the box, Svelte will interpolate between two numbers, two arrays or two o --- +If the initial value is `undefined` or `null`, the first value change will take effect immediately. This is useful when you have tweened values that are based on props, and don't want any motion when the component first renders. + +```js +const size = tweened(undefined, { + duration: 300, + easing: cubicOut +}); + +$: $size = big ? 100 : 10; +``` + +--- + The `interpolate` option allows you to tween between *any* arbitrary values. It must be an `(a, b) => t => value` function, where `a` is the starting value, `b` is the target value, `t` is a number between 0 and 1, and `value` is the result. For example, we can use the [d3-interpolate](https://github.com/d3/d3-interpolate) package to smoothly interpolate between two colours. ```html @@ -493,6 +506,15 @@ Both `set` and `update` can take a second argument — an object with `hard` or ``` +--- + +If the initial value is `undefined` or `null`, the first value change will take effect immediately, just as with `tweened` values (see above). + +```js +const size = spring(); +$: $size = big ? 100 : 10; +``` + ### `svelte/transition` The `svelte/transition` module exports six functions: `fade`, `fly`, `slide`, `scale`, `draw` and `crossfade`. They are for use with svelte [`transitions`](docs#Transitions). diff --git a/src/runtime/motion/spring.ts b/src/runtime/motion/spring.ts index 7742dd4106..1804ff43a5 100644 --- a/src/runtime/motion/spring.ts +++ b/src/runtime/motion/spring.ts @@ -65,7 +65,7 @@ interface Spring extends Readable{ stiffness: number; } -export function spring(value: T, opts: SpringOpts = {}): Spring { +export function spring(value?: T, opts: SpringOpts = {}): Spring { const store = writable(value); const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts; @@ -84,12 +84,12 @@ export function spring(value: T, opts: SpringOpts = {}): Spring { target_value = new_value; const token = current_token = {}; - if (opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) { + if (value == null || opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) { cancel_task = true; // cancel any running animation last_time = now(); - last_value = value; + last_value = new_value; store.set(value = target_value); - return new Promise(f => f()); // fulfil immediately + return Promise.resolve(); } else if (opts.soft) { const rate = opts.soft === true ? .5 : +opts.soft; inv_mass_recovery_rate = 1 / (rate * 60); diff --git a/src/runtime/motion/tweened.ts b/src/runtime/motion/tweened.ts index 7159c17eb5..e33f0f79f9 100644 --- a/src/runtime/motion/tweened.ts +++ b/src/runtime/motion/tweened.ts @@ -69,13 +69,18 @@ interface Tweened extends Readable { update(updater: Updater, opts: Options): Promise; } -export function tweened(value: T, defaults: Options = {}): Tweened { +export function tweened(value?: T, defaults: Options = {}): Tweened { const store = writable(value); let task: Task; let target_value = value; function set(new_value: T, opts: Options) { + if (value == null) { + store.set(value = new_value); + return Promise.resolve(); + } + target_value = new_value; let previous_task = task; diff --git a/test/motion/index.js b/test/motion/index.js new file mode 100644 index 0000000000..33489b18e1 --- /dev/null +++ b/test/motion/index.js @@ -0,0 +1,23 @@ +import * as assert from 'assert'; +import { get } from '../../store'; +import { spring, tweened } from '../../motion'; + +describe('motion', () => { + describe('spring', () => { + it('handles initially undefined values', () => { + const size = spring(); + + size.set(100); + assert.equal(get(size), 100); + }); + }); + + describe('tweened', () => { + it('handles initially undefined values', () => { + const size = tweened(); + + size.set(100); + assert.equal(get(size), 100); + }); + }); +});