UNPKG

material-motion

Version:

Makes it easy to add rich, interactive motion to your application.

132 lines 5.11 kB
/** @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 { when, } from '../aggregators'; import { combineLatest, } from '../combineLatest'; import { subscribe, } from '../subscribe'; import { Axis, Direction, State, ThresholdRegion, } from '../enums'; import { createProperty, } from '../observables'; import { NumericSpring, } from './NumericSpring'; export var SwipeState; (function (SwipeState) { SwipeState["NONE"] = "none"; SwipeState["LEFT"] = "left"; SwipeState["RIGHT"] = "right"; })(SwipeState || (SwipeState = {})); ; export class Swipeable { constructor({ tossable, width$ }) { this.iconSpring = new NumericSpring(); this.backgroundSpring = new NumericSpring(); // Should `State` be called `MotionState` so `state$` can be reserved for interactions? this.swipeState$ = createProperty({ initialValue: SwipeState.NONE }); /** * If an item is swiped past the threshold, it will animate by its own width * + destinationMargin, in the direction of the swipe. * * This ensures that decoration that might overflow an item's bounds (like a * shadow) isn't visible when it's been swiped away. */ this.destinationMargin$ = createProperty({ initialValue: 0, }); this.tossable = tossable; this.width$ = width$; this.state$ = this.tossable.state$; tossable.draggable.axis = Axis.X; const ICON_SPRING_INITIAL_VALUE = 0.67; const draggable = tossable.draggable; const spring = tossable.spring; const draggedX$ = tossable.draggedLocation$.pluck({ path: 'x' }); this.iconSpring.initialValue = ICON_SPRING_INITIAL_VALUE; this.direction$ = draggedX$.threshold(0).isAnyOf([ThresholdRegion.ABOVE]).rewrite({ mapping: { true: Direction.RIGHT, false: Direction.LEFT, } }); this.isThresholdMet$ = draggedX$.distanceFrom(0).threshold(Swipeable.VISUAL_THRESHOLD).isAnyOf([ ThresholdRegion.ABOVE, ThresholdRegion.WITHIN, ]); this.whenThresholdCrossed$ = when(this.isThresholdMet$.dedupe()); subscribe({ sink: this.backgroundSpring.destination$, source: this.isThresholdMet$.rewrite({ mapping: { true: 1, false: 0, } }), }); subscribe({ sink: this.iconSpring.destination$, source: this.isThresholdMet$.rewrite({ mapping: { true: 1, false: ICON_SPRING_INITIAL_VALUE, } }), }); // This needs to also take velocity into consideration; right now, it only // cares about final position. subscribe({ sink: this.swipeState$, source: when(draggable.state$.isAnyOf([State.AT_REST])).rewriteTo({ value$: this.isThresholdMet$.rewrite({ mapping: { true: this.direction$, false: SwipeState.NONE, }, }), onlyEmitWithUpstream: true, }), }); const destinationDistance$ = width$.addedBy(this.destinationMargin$); subscribe({ sink: spring.destination$, source: combineLatest({ x: this.swipeState$.rewrite({ mapping: { [SwipeState.NONE]: 0, [SwipeState.LEFT]: destinationDistance$.multipliedBy(-1), [SwipeState.RIGHT]: destinationDistance$, } }), y: 0, }) }); this.styleStreamsByTargetName = { item: tossable.styleStreams, icon: { scale$: this.iconSpring.value$, willChange$: tossable.styleStreams.willChange$, }, background: { scale$: this.backgroundSpring.value$, willChange$: tossable.styleStreams.willChange$, }, }; } get destinationMargin() { return this.destinationMargin$.read(); } set destinationMargin(value) { this.destinationMargin$.write(value); } } Swipeable.VISUAL_THRESHOLD = 72; export default Swipeable; //# sourceMappingURL=Swipeable.js.map