Material Motion Exploring solutions that will empower creators with the tools needed to describe and implement rich, interactive motion on any platform. Edit this page · History
$._map rubberBanded
Status Interface level Implementation level Library
Stable as of February 21, 2016 L2: Interaction creator L3: Stream creator material-motion
Android View
iOS (Swift) View View
upstream (number)
(number) downstream

rubberBanded specification

This is the engineering specification for the MotionObservable operator: rubberBanded.


rubberBanded applies resistance to values that fall outside of the given range and emits the result.


Expose a rubberBanded operator API

Use _map to implement the operator. Accept a number value. Emit the result of incoming + value.

class MotionObservable<number> {
  public func rubberBanded(below: number, above: number, length: number) -> MotionObservable<number>

Implement the rubber-banding math

public func rubberBand(value: number, min: number, max: number, bandLength: number) -> number {
  if value >= min && value <= max {
    // While we're within range we don't rubber band the value.
    return value

  if bandLength <= 0 {
    // The rubber band doesn't exist, return the minimum value so that we stay put.
    return min

  // 0.55 chosen as an approximation of iOS' rubber banding behavior.
  let rubberBandCoefficient: number = 0.55
  // Accepts values from [0...+inf and ensures that f(x) < bandLength for all values.
  let band: (number) -> number = { value in
    let demoninator = value * rubberBandCoefficient / bandLength + 1
    return bandLength * (1 - 1 / demoninator)
  if (value > max) {
    return band(value - max) + max

  } else if (value < min) {
    return min - band(min - value)

  return value

Apply the rubber band math

class MotionObservable<number> {
  func rubberBanded(below: number, above: number, length: number) -> MotionObservable<number> {
    return _map {
      return rubberBand(value: $0, min: below, max: above, bandLength: length)