Skip to content

Commit

Permalink
Cleanup inferno animation (#1575)
Browse files Browse the repository at this point in the history
* We need to store the dimensions to allow animating width/height if needed.

* Fix comments and improve readme

* Added example of animating child on mount/unmount

* Removed unused css rule

* Remove unused prop param and clarify how to use animation helpers for Bootstrap style modal.

* Belongs to last commit
  • Loading branch information
jhsware authored Oct 22, 2021
1 parent 4e27947 commit e590184
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 9 deletions.
175 changes: 175 additions & 0 deletions docs/animations-demo-inner/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* Using CSS-vars for transitions so you can experiment easily */
:root {
--infernoAnimationEnter: all 1.2s ease-out;
--infernoAnimationLeave: all .6s ease-out;
}

/*******************************************/
/* Animate height and opacity of card <li> */
/*******************************************/
.inner-leave {
/* Leave animation start state */
opacity: 1;
transform: translateX(0);
}

.inner-leave-active {
/* Leave animation transitions */
overflow: visible;
transition: var(--infernoAnimationLeave);
pointer-events: none; /* prevent hover to fire transition events */
}

.inner-leave-end {
/* Leave animation end state */
opacity: 0;
transform: translateX(-100%);
}

.inner-enter {
/* Enter animation start state */
opacity: 0.5 ;
transform: translateX(50%);
}

.inner-enter-active {
/* Enter animation transitions */
transition: var(--infernoAnimationEnter);
pointer-events: none; /* prevent hover to fire transition events */
}

.inner-enter-end {
/* Enter animation end state */
opacity: 1;
transform: translateX(0);
}


/* Some CSS for the list and cards */
.page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.page img {
margin: 20vh auto 0;
display: block;
background-color: white;
}

.page h3 {
background-color: white;
margin: 1rem;
text-align: center;
}

.page p {
background-color: white;
margin: 1rem auto;
max-width: 20rem;
text-align: left;
}

.inner {
max-width: 30rem;
margin: 10vh auto 2rem;
padding: 4rem;
background-color: #eee;
}

.inner h2 {
font-size: 3em;
margin: 3rem auto;
text-align: center;
}

/* Some general CSS for the example */

button {
padding: 0.5rem 1rem;
margin: 0.5rem auto 0;
display: block;
background: #35a748;
color: white;
border-style: none;
border-radius: 4px;
width: 10rem;
} button:hover {
background: #3775bb;
}

body {
font-family: helvetica;
box-sizing: border-box;
margin: 0;
padding: 1rem;
display: flex;
gap: 2rem;
justify-content: center;
align-items: flex-end;
height: 100vh;
overflow-y: scroll;
overflow-x: hidden;
}

.App {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
padding: 1rem;
width: calc(100vw - 2rem);
height: 100vh;
}

h1 {
position: absolute;
bottom: 0;
left: 0;
transform: rotate(-90deg);
font-size: 2rem;
font-weight: 800;
color: #ddd;
text-align: center;
margin: 0.5rem;
white-space: nowrap;
width: 2rem;
transform-origin: 50% 50%;
}

h1 small {
font-size: 0.5em;
}
h1 small a {
color: #4A90E2;
text-decoration: none;
}
h1 small a:hover {
color: #3775bb;
}

h2 {
font-size: 1rem;
font-weight: 100;
color: #4A90E2;
text-align: center;
}

h3 {
font-size: 0.85rem;
font-weight: 100;
}

p {
font-size: 0.85rem;
font-weight: 100;
color: #888;
text-align: center;
margin-top: -0.5em;
width: 40rem;
max-width: 100%;
}
90 changes: 90 additions & 0 deletions docs/animations-demo-inner/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
(function() {
"use strict";
var Component = Inferno.Component;
var createElement = Inferno.createElement;
var createRef = Inferno.createRef;
var InfernoAnimation = Inferno.Animation;

var {
AnimatedComponent,
componentDidAppear,
componentWillDisappear
} = InfernoAnimation;

var {
addClassName,
removeClassName,
forceReflow,
registerTransitionListener,
} = InfernoAnimation.utils;

class Page extends Component {
componentDidAppear(dom) {
// We need to store a reference to the animating child that
// isn't removed on unmount. Currently this requires passing
// a ref as property and referencing the .current property
// of that object.
this._innerEl = this.props.innerRef.current;
componentDidAppear(this._innerEl, { animation: "inner" });
}

componentWillDisappear(dom, callback) {
componentWillDisappear(this._innerEl, { animation: "inner" }, callback);
}

render() {
return (
createElement('div', {
className: "page",
}, createElement('div', { className: "random-wrapper" }, [
createElement('h3', null, "Page " + this.props.step),
createElement('img', { width: "120px", height: "120px", src: "avatar.png" }),
createElement('p', null, "The entire page is swapped, but we are only animating div.inner. This gives the apperance of only swapping the box below."),
createElement('p', null, "In order not to hide the incoming content we can't set background on div.page. The background needs to be provided by a backdrop in the wizard component."),
createElement('div', { ref: this.props.innerRef, className: "inner"}, [
createElement('h2', null, "Step " + this.props.step),
createElement('button', {onClick: (e) => { e.preventDefault(); this.props.onNext() }}, "Next")
])
])
)
);
}
}

const nrofSteps = 3;

class Wizard extends Component {
constructor() {
super();

// Ref objects used to reference the animating children of each page
this._innerAnimRefs = [];
for (let i = 0; i < nrofSteps; i++) {
this._innerAnimRefs.push(createRef());
}

this.state = {
showStepIndex: 0
};
}

doGoNext = () => {
this.setState({
showStepIndex: (this.state.showStepIndex + 1) % nrofSteps
})
}

render() {
const { showStepIndex } = this.state;

return createElement(Page, { key: "page_" + showStepIndex, step: showStepIndex + 1, innerRef: this._innerAnimRefs[showStepIndex], onNext: this.doGoNext });
}
}


document.addEventListener('DOMContentLoaded', function () {
var container_1 = document.querySelector('#App1');

Inferno.render(createElement(Wizard), container_1);
});
})();
Binary file added docs/animations-demo-inner/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/animations-demo-inner/dist/bundle.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions docs/animations-demo-inner/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="./app.css" rel="stylesheet" />
<title>inferno-animation</title>
<script defer src="../../packages/inferno/dist/inferno.js"></script>
<script defer src="../../packages/inferno-create-element/dist/inferno-create-element.js"></script>
<script defer src="../../packages/inferno-animation/dist/inferno-animation.js"></script>
<script defer src="app.js" type="text/javascript"></script>
</head>
<body>
<h1>inferno-animation demo animation of child</h1>
<div id="App1" class="App"></div>
</body>
</html>
7 changes: 6 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ <h2>Examples</h2>
This demo uses inferno-animation to animate multiple elements of a card when it is added and removed from the DOM.<br />
<small><a href="animations-demo/index.html">Example</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations-demo/app.js">View source...</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations-demo/app.css">View CSS...</a></small>
</p>
<p>
<a href="animations-demo-inner/index.html">Animating child when parent is mounted/unmounted</a><br />
Here the animation is only performed on a child component of page when the page is mounted and unnmounted.<br />
<small><a href="animations-demo-inner/index.html">Example</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations-demo-inner/app.js">View source...</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations-demo-inner/app.css">View CSS...</a></small>
</p>
<p>
<a href="animations/index.html">Animations on mount and unmount</a><br />
This is an implementation of inferno-animation where components are animated when added and removed from the DOM.<br />
This is an implementation of inferno-animation where components are animated when added, removed or moved in the DOM.<br />
<small><a href="animations/index.html">Example</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations/app.js">View source...</a> | <a href="https://github.com/infernojs/inferno/blob/master/docs/animations/app.css">View CSS...</a></small>
</p>
<p>
Expand Down
55 changes: 52 additions & 3 deletions packages/inferno-animation/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ There are three base components you can extend from to get animations in a strai
- AnimatedMoveComponent -- animates on move (within the same parent)
- AnimatedAllComponent -- animates on add/remove and move (within the same parent)

You can also animate functional components. There are a couple of examples of animations in the main repos in the `docs/animations` and `docs/animations-demo` folder.

If you don't want to extend from one of the pre-wired components, look att src/AnimatedAllComponent.ts to see
how to wire up the three animation hooks:

- componentDidAppear
- componentWillDisappear
- componentWillMove

There are a couple of examples of animations in the main repos in the `docs/animations` and `docs/animations-demo` folder.

Using AnimatedAllComponent is just like working with ordinary components. Don't forget to
add the CSS or you can get strange results:

Expand Down Expand Up @@ -93,4 +93,53 @@ import { componentDidAppear, componentWillDisappear, componentWillMove } from 'i

IMPORTANT! Always use the provided helper methods instead of implementing the hooks yourself. There
might be optimisations and/or changes to how the animation hooks are implemented in future versions
of Inferno that you want to benefit from.
of Inferno that you want to benefit from.

### Bootstrap style modal animation
This is an example of how you could implement a Bootstrap style Modal animation using inferno-animation. These two animations are used both for the backdrop and the modal and the purpose is to support the CSS-rules without modification.

- always use the inferno-animation utility functions
- implementation is straight forward
- `callback` in animateModalOnWillDisappear triggers the dom-removal in Inferno and is crucial!

Custom animations won't be coordinated with the standard animations to reduce reflow, but performance is not an issue with just a few animations running simultaneously. Use the standard animations for grid or list items.

Call these helper methods from `componentDidAppear` and `componentWillDisapper` of your backdrop and content component when you build a Bootstrap style modal.

```js
import { utils } from 'inferno-animation'
const {
addClassName,
removeClassName,
registerTransitionListener,
forceReflow,
setDisplay
} = utils

export function animateModalOnWillDisappear (dom, callback, onClosed) {
registerTransitionListener([dom], () => {
// Always call the dom removal callback first!
callback && callback()
onClosed && onClosed()
})

setTimeout(() => {
removeClassName(dom, 'show')
}, 5)
}

export function animateModalOnDidAppear (dom, onOpened) {
setDisplay(dom, 'none')
addClassName(dom, 'fade')
forceReflow(dom)
setDisplay(dom, undefined)

registerTransitionListener([dom, dom.children[0]], function () {
// *** Cleanup ***
setDisplay(dom, undefined)
onOpened && onOpened(dom)
})

addClassName(dom, 'show')
}
```
Loading

0 comments on commit e590184

Please sign in to comment.