Back to Popmotion

README

packages/projection/README.md

8.7.13.8 KB
Original Source
<p align="center"></p> <h3 align="center">A lightweight layout projection library</h3>

Introduction

Layout projection is a method for animating layout at 60fps.

In essence, it's the ability to project any element from its browser-computed layout to a size and position on screen of your choosing.

Learn more about layout projection's capabilities.

Projection is an experimental, lightweight layout projection library. It currently clocks in at 1.8kb, and I expect it to land somewhere in the 2.5kb range.

Demo

Install

Projection is currently in alpha. Expect bugs, and breaking changes as the scope of the library is better defined.

bash
npm install projection@alpha

Usage

Projecting an element

javascript
import { layoutNode, updateProjectionStyle } from "projection"

const element = document.getElementById("element-id")

/**
 * Create a layoutNode for each element you want to project
 */
const node = layoutNode({
  onProjectionUpdate: () => updateProjectionStyle(node, element)
})

/**
 * To project, we need an accurate measurement of the element. To do this 
 * accurately, the measured element can't currently have a transform applied.
 * Future releases will handle this automatically.
 * 
 * Ensure that that `setLayout` is called every time the element's layout is recomputed.
 */
const bbox = element.getBoundingClientRect()
node.setLayout(bbox)

/**
 * Now we can project. Setting a target will project an element into
 * the provided bounding box.
 */
node.setTarget({
  top: 100,
  left: 100,
  right: 400,
  bottom: 400
})

Projecting trees

A major difficulty of layout animations is once you apply a transform to a parent, you distort children elements that you might wish to be of a particular size or position throughout the animation.

The calculations involved in correcting this distortion become increasingly complex for deeper layout animation trees.

Projection removes this complexity by allowing layoutNode to accept another layoutNode as a parent. In this way, a tree can be formed. Projection will ensure all child transforms are calculated to compensate for parent transforms.

javascript
const childNode = layoutNode(options, parentNode)

Relative projection

Set relative to parent:

javascript
childNode.setRelativeTarget({ top: 10, left: 10 })

Change relative parent:

javascript
childNode.setRelativeParent(node)

Animations

By using a low-level animation library like Popmotion, layout animations are a matter of calling setTarget once per frame.

javascript
import { animate, mix } from "popmotion"

// Just before layout change:
const prev = element.getBoundingClientRect()

// Immediately after layout change:
element.style.transform = ""
const next = element.getBoundingClientRect()
node.setLayout(next)

animate({
  from: 0,
  to: 1,
  onUpdate: progress => {
    node.setTarget({
      left: mix(prev.left, next.left, progress),
      right: mix(prev.right, next.right, progress),
      top: mix(prev.top, next.top, progress),
      bottom: mix(prev.bottom, next.bottom, progress),
    })
  }
})

TODO

  • Add helper functions for scale correcting box shadow, border etc.
  • Make more DOM-specific API.
  • Develop relative target support to include pinned width/height animations.
  • Add support for rotate.
  • Add support for custom origins.
  • Add support for additional transforms.

Thanks to Alex Nault for donating the projection package name!