Skip to content

react pose

Created: 2018-06-20 10:49:01 -0700 Modified: 2018-06-21 15:39:11 -0700

The full name of this library is Popmotion Pose according to this page. It is a potentially better API than react-spring even though they provide almost exactly the same functionality.

Each individual package in the Popmotion repo is published, but to get the code on your own machine for testing, you need the Popmotion repo. I.e. “npm i react-pose” works, but you’d clone via “https://github.com/Popmotion/popmotion.git”.

The dependency hierarchy is as follows: react-pose → react-pose-core → animated-pose → pose-core

For more information on all of this, read here.

A “pose” is what they call “slots” from react-spring.

To build their examples from their repo, read this page. Note: it is not clear without that page how to get everything running.

To find information on react-pose, check out this link specifically. It’s very difficult to find that link from their main page.

Use onValueChange, which is an object whose keys are property names and whose values are functions that get called with the property’s new value:

const PosedDiv = posed.div({
// poses go here
});
class Example {
onHeightChange(heightValue) {
// use new value here
}
render() {
return <PosedDiv
onValueChange={{ height: this.onHeightChange.bind(this) }}
/>
}
}

There are elements like “div”, “circle”, or “button” that correspond to HTML or SVG, but they’re enhanced by Pose. These all show up in supported-elements.ts.

Note that SVG elements need to be placed inside of an SVG (the “DraggableCircle” is taken from here):

<svg>
<DraggableCircle stroke="red" fill="blue" cx={50} cy={50} radius={100}/>
</svg>

If you want to run two animations in a row, e.g. “move a div to the right and then color it blue”, you can use Keyframes (which are low level), or you can just use multiple poses and control them via these props. Here’s a quick example (taken from this CodeSandbox that I made):

import posed from "react-pose";
import React from "react";
import _ from "lodash";
const PosedDiv = posed.div({
firstPose: {
x: "50%",
background: "#ff0000"
},
secondPose: {
x: "75%",
background: "#00ff00",
transition: {
duration: 1000
}
},
thirdPose: {
x: "25%",
background: "#0000ff",
transition: {
duration: 2000
}
},
fourthPose: {
x: "85%",
background: "#ff00ff",
transition: {
duration: 1500
}
},
fifthPose: {
x: "5%",
background: "#00ffff",
transition: {
duration: 750
}
}
});
class FakeKeyFramesDiv extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPose: "secondPose"
};
this.poseOrder = [
"firstPose",
"secondPose",
"thirdPose",
"fourthPose",
"fifthPose"
];
}
onPoseComplete = () => {
const indexOfPose = _.indexOf(this.poseOrder, this.state.currentPose);
if (indexOfPose !== -1 && indexOfPose !== this.poseOrder.length - 1) {
this.setState({
currentPose: this.poseOrder[indexOfPose + 1]
});
}
};
render() {
return (
<PosedDiv
pose={this.state.currentPose}
onPoseComplete={this.onPoseComplete}
initialPose="firstPose"
>
{this.props.children}
</PosedDiv>
);
}
}
export default FakeKeyFramesDiv;

There are a bunch of things to note:

  • If you’re going to animate the “background” or “color” properties changing, you have to use a hex string for the color, not any HTML-compatible string like “red”.
  • You can specify transitions like this or like what you see above (which was taken from here).
const PosedDiv = posed.div({
somePose: {
height: ({ initialFillPercent }) => `${initialFillPercent}%`,
}
});
render() {
<PosedDiv initialFillPercent={50}/>;
}

Just keep in mind that there’s sort of a bug around this right now; see this troubleshooting note.

”React does not recognize ____ prop on a DOM element”

Section titled ”React does not recognize ____ prop on a DOM element”

There’s an issue about this here. This arises from how they suggest using dynamic props in React:

<RatingChangeDiv
style={{ height: '50%' }}
pose={this.state.currentPose}
onPoseComplete={this.onPoseComplete.bind(this)}
initialFillPercent={this.state.initialFillPercent}
initialPose="firstPose"
>
<div>Hello world</div>
</RatingChangeDiv>

You’ll then see “React does not recognize the initialFillPercent prop on a DOM element.”

There’s no easy solution to this for now, but I bundled all of the props into a single object called “transitionprops” (specifically lowercase so as to avoid the warning). This still emits an object into the DOM, but at least there are no warnings:

const transitionprops = {
initialFillPercent: this.state.initialFillPercent,
finalFillPercent: this.state.finalFillPercent,
};
<RatingChangeDiv
style={{ height: '50%' }}
pose={this.state.currentPose}
onPoseComplete={this.onPoseComplete.bind(this)}
transitionprops={transitionprops}
initialPose="firstPose"
className={this.props.backgroundStyle}
>
<div>Hello world</div>
</RatingChangeDiv>

Then, when making my posed.div, I do this:

const RatingChangeDiv = posed.div({
firstPose: {
height: ({ transitionprops: { initialFillPercent } }) =>
`${initialFillPercent}%`,
},
[…]