material-motion
Version:
Makes it easy to add rich, interactive motion to your application.
183 lines • 6.31 kB
JavaScript
/** @license
* Copyright 2016 - present The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { combineLatest, } from '../combineLatest';
import { createProperty, } from '../observables';
import { State, } from '../enums/State';
import { anyOf, } from '../aggregators';
import { subscribe, } from '../subscribe';
import { DEFAULT_DAMPING, DEFAULT_STIFFNESS, DEFAULT_THRESHOLD, NumericSpring, } from './NumericSpring';
/**
* `Point2DSpring` is a spring that accepts and emits `Point2D`s for its
* configuration values. Internally, it is composed of two independent
* `NumericSpring`s.
*
* Because each internal spring emits independently, `value$` will emit
* intermediate values (e.g. changing `initialValue` from `{ x: 0, y: 0 }` to
* `{ x: 5, y: 4 }` will cause 2 emissions, one when `x` changes and one when
* `y` changes.) This may be corrected in a future release, when emits could be
* batched into one per frame.
*/
export class Point2DSpring {
constructor() {
this.xSpring = new NumericSpring();
this.ySpring = new NumericSpring();
this.destination$ = createProperty({
initialValue: { x: 0, y: 0 },
});
this.initialValue$ = createProperty({
initialValue: { x: 0, y: 0 },
});
this.initialVelocity$ = createProperty({
initialValue: { x: 0, y: 0 },
});
this.stiffness$ = createProperty({
initialValue: DEFAULT_STIFFNESS,
});
this.damping$ = createProperty({
initialValue: DEFAULT_DAMPING,
});
this.threshold$ = createProperty({
initialValue: DEFAULT_THRESHOLD,
});
this.enabled$ = createProperty({
initialValue: true,
});
this.state$ = anyOf([
this.xSpring.state$.isAnyOf([State.ACTIVE]),
this.ySpring.state$.isAnyOf([State.ACTIVE]),
]).dedupe().rewrite({
mapping: {
true: State.ACTIVE,
false: State.AT_REST,
},
})._remember();
// Since this emits `Point2D`s, it's probably safe to presume it will be used
// for translation. But, to be consistent with `NumericSpring`, we write
// outputs to `value$` rather than `styleStreams`.
// If `value$` were debounced, it would emit its terminal value after `state$`
// is `AT_REST`. This might be OK in practice (since there would only be one
// final frame emitted while the interaction is `AT_REST`). However, to
// maintain the contract that `state$` will be `ACTIVE` whenever the stream is
// animating, and to make testing simpler, we don't debounce here.
//
// If it were debounced, we'd have to either ensure that `state$` waits until
// the next frame before emitting `AT_REST`, or accept that they are out-of-
// sync and add an extra mockRAF.step() to the relevant tests.
this.value$ = combineLatest({
x: this.xSpring.value$,
y: this.ySpring.value$,
});
subscribe({
sink: this.xSpring.destination$,
source: this.destination$.pluck('x'),
});
subscribe({
sink: this.ySpring.destination$,
source: this.destination$.pluck('y'),
});
subscribe({
sink: this.xSpring.initialValue$,
source: this.initialValue$.pluck('x'),
});
subscribe({
sink: this.ySpring.initialValue$,
source: this.initialValue$.pluck('y'),
});
subscribe({
sink: this.xSpring.initialVelocity$,
source: this.initialVelocity$.pluck('x'),
});
subscribe({
sink: this.ySpring.initialVelocity$,
source: this.initialVelocity$.pluck('y'),
});
subscribe({
sinks: [
this.xSpring.stiffness$,
this.ySpring.stiffness$,
],
source: this.stiffness$,
});
subscribe({
sinks: [
this.xSpring.damping$,
this.ySpring.damping$,
],
source: this.damping$,
});
subscribe({
sinks: [
this.xSpring.threshold$,
this.ySpring.threshold$,
],
source: this.threshold$,
});
subscribe({
sinks: [
this.xSpring.enabled$,
this.ySpring.enabled$,
],
source: this.enabled$,
});
}
get destination() {
return this.destination$.read();
}
set destination(value) {
this.destination$.write(value);
}
get initialValue() {
return this.initialValue$.read();
}
set initialValue(value) {
this.initialValue$.write(value);
}
get initialVelocity() {
return this.initialVelocity$.read();
}
set initialVelocity(value) {
this.initialVelocity$.write(value);
}
get stiffness() {
return this.stiffness$.read();
}
set stiffness(value) {
this.stiffness$.write(value);
}
get damping() {
return this.damping$.read();
}
set damping(value) {
this.damping$.write(value);
}
get threshold() {
return this.threshold$.read();
}
set threshold(value) {
this.threshold$.write(value);
}
get enabled() {
return this.enabled$.read();
}
set enabled(value) {
this.enabled$.write(value);
}
get state() {
return this.state$._read();
}
}
export default Point2DSpring;
//# sourceMappingURL=Point2DSpring.js.map