Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dom-flip

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dom-flip - npm Package Compare versions

Comparing version 0.2.4 to 0.2.5

dist/element.d.ts

128

dist/dom-flip.d.ts

@@ -1,126 +0,2 @@

/**
* `dom-flip`
*
* FLIP move animations for web components.
*
* Wrap this around your child elements and they will slide smoothly over the screen
* if their order or position changes.
*
* @example
* ```html
* <!-- The divs' positions will be animated if they change. -->
* <dom-flip>
* <div>Item 1</div
* <div>Item 2</div
* <div>Item 3</div
* <div>Item 4</div
* </dom-flip>
* ```
*
* @demo demo/index.html
* @see https://aerotwist.com/blog/flip-your-animations/
*/
export default class DomFlip extends HTMLElement {
static readonly observedAttributes: string[];
/**
* Indicates whether the element will listen for changes and apply
* animations.
*
* Disable this if you want to temporarily control the DOM-animations yourself.
* Defaults to `true`.
*
* @type Boolean
*/
active: boolean;
/**
* The name of the attribute to use as model ID.
*
* Defaults to `data-flip-id`.
*
* @type String
*/
attrName: string;
/**
* The CSS animation delay in milliseconds.
*
* Defaults to 0ms.
*
* @type Number
*/
delayMs: number;
/**
* The CSS animation duration in milliseconds.
*
* Defaults to 200ms.
*
* @type Number
*/
durationMs: number;
/**
* The CSS animation easing mode to use.
*
* Defaults to `ease-in-out`.
*
* @type String
*/
easing: string;
/**
* The class name to apply when the elements are moving. This
* only need be changed in case of conflicts.
*
* Defaults to `dom-flip-transitioning`.
*
* @type String
*/
transitionClassName: string;
/**
* Whether a dom change event handler is enqueued for the current animation frame.
*/
private _animationEnqueued;
/**
* The last known client rects of the children keyed by their ID.
*/
private _childData;
/**
* The mutation observer listening for changes to the attributes of the child elements.
*/
private _childObserver;
/**
* The shadow slot containing the children.
*/
private _slot;
/**
* The bound event handler for mutation observer updating.
*/
private _mutationObserverUpdateHandler;
constructor();
connectedCallback(): void;
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
/**
* Animates the transition of the elements that have moved.
*/
private _animateChangedElements();
/**
* Goes through the node's children and collects styling metadata in a map.
*
* @returns A map with styling data by child ID.
*/
private _collectChildData();
/**
* Enqueues the animation of moved elements at animation frame timing.
*/
private _enqueueAnimateChangedElements();
/**
* Updates the registered event handlers, mutation observers and triggers animation..
*/
private _updateListeners();
/**
* Updates the mutation observer configuration and collects child position data,
* if necessary.
*/
private _updateMutationObserver();
/**
* Render the shadow dom contents.
*/
private _render();
}
import DomFlip from './element';
export default DomFlip;

@@ -1,284 +0,4 @@

/**
* Generates a CSS `translate`-rule compatible string that does a 2D transform.
*
* @param {number} dx the X delta
* @param {number} dy the Y delta
* @param {number} sx the X scale
* @param {number} sy the Y scale
* @return {string} the CSS rule
*/
const generateTransformString = (dx, dy, sx, sy) => `translate(${dx}px, ${dy}px) scale(${sx}, ${sy})`;
/**
* Determines whether the actual number lies within a close margin to the
* target number.
*
* @param {number} actual The number to check.
* @param {number} target The target number.
* @param {number} epsilon The allowed margin of error. Defaults to 1e-5.
*/
const isCloseTo = (actual, target, epsilon = 1e-5) => Math.abs(actual - target) <= epsilon;
/**
* The regex used to parse the transform matrix string.
*/
const transformRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/;
/**
* `dom-flip`
*
* FLIP move animations for web components.
*
* Wrap this around your child elements and they will slide smoothly over the screen
* if their order or position changes.
*
* @example
* ```html
* <!-- The divs' positions will be animated if they change. -->
* <dom-flip>
* <div>Item 1</div
* <div>Item 2</div
* <div>Item 3</div
* <div>Item 4</div
* </dom-flip>
* ```
*
* @demo demo/index.html
* @see https://aerotwist.com/blog/flip-your-animations/
*/
export default class DomFlip extends HTMLElement {
constructor() {
super();
/**
* Indicates whether the element will listen for changes and apply
* animations.
*
* Disable this if you want to temporarily control the DOM-animations yourself.
* Defaults to `true`.
*
* @type Boolean
*/
this.active = true;
/**
* The name of the attribute to use as model ID.
*
* Defaults to `data-flip-id`.
*
* @type String
*/
this.attrName = 'data-flip-id';
/**
* The CSS animation delay in milliseconds.
*
* Defaults to 0ms.
*
* @type Number
*/
this.delayMs = 0;
/**
* The CSS animation duration in milliseconds.
*
* Defaults to 200ms.
*
* @type Number
*/
this.durationMs = 200;
/**
* The CSS animation easing mode to use.
*
* Defaults to `ease-in-out`.
*
* @type String
*/
this.easing = 'ease-in-out';
/**
* The class name to apply when the elements are moving. This
* only need be changed in case of conflicts.
*
* Defaults to `dom-flip-transitioning`.
*
* @type String
*/
this.transitionClassName = 'dom-flip-transitioning';
/**
* Whether a dom change event handler is enqueued for the current animation frame.
*/
this._animationEnqueued = false;
/**
* The last known client rects of the children keyed by their ID.
*/
this._childData = new Map();
this._childObserver = new MutationObserver(() => this._enqueueAnimateChangedElements());
this._mutationObserverUpdateHandler = () => this._updateMutationObserver();
this.attachShadow({ mode: 'open' });
}
static get observedAttributes() {
return [
"active" /* Active */,
"attr-name" /* AttrName */,
"delay-ms" /* DelayMs */,
"duration-ms" /* DurationMs */,
"easing" /* Easing */,
"transition-class-name" /* TransitionClassName */,
];
}
connectedCallback() {
this._render();
this._slot = this.shadowRoot.querySelector('slot');
this._updateListeners();
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "active" /* Active */:
this.active = !!newValue;
this._updateListeners();
break;
case "attr-name" /* AttrName */:
this.attrName = newValue;
this._updateListeners();
break;
case "delay-ms" /* DelayMs */:
this.delayMs = Number(newValue);
this._render();
break;
case "duration-ms" /* DurationMs */:
this.durationMs = Number(newValue);
this._render();
break;
case "easing" /* Easing */:
this.easing = newValue;
this._render();
break;
case "transition-class-name" /* TransitionClassName */:
this.transitionClassName = newValue;
this._render();
break;
}
}
/**
* Animates the transition of the elements that have moved.
*/
_animateChangedElements() {
const newChildData = this._collectChildData();
for (const [id, [el, n]] of newChildData.entries()) {
const oldChildData = this._childData.get(id);
if (!oldChildData) {
continue;
}
const [_, old] = oldChildData;
const dT = old.top - n.top;
const dL = old.left - n.left;
// Animate only if there have been changes
if (isCloseTo(dT, 0) &&
isCloseTo(dL, 0) &&
isCloseTo(old.scaleX / n.scaleX, 1) &&
isCloseTo(old.scaleY / n.scaleY, 1)) {
continue;
}
el.classList.remove(this.transitionClassName);
// Revert new layout into old positions
el.style.opacity = String(old.opacity);
el.style.transform = generateTransformString(dL, dT, old.scaleX, old.scaleY);
requestAnimationFrame(() => {
el.classList.add(this.transitionClassName);
// Remove our reverts and let animation play
el.style.opacity = String(n.opacity);
el.style.transform = generateTransformString(0, 0, n.scaleX, n.scaleY);
setTimeout(() => el.classList.remove(this.transitionClassName), this.durationMs);
});
}
this._childData = newChildData;
}
/**
* Goes through the node's children and collects styling metadata in a map.
*
* @returns A map with styling data by child ID.
*/
_collectChildData() {
const bbox = this.getBoundingClientRect();
const map = new Map();
for (const el of this._slot.assignedNodes()) {
if (!(el instanceof HTMLElement)) {
continue;
}
const id = el.getAttribute(this.attrName);
if (!id) {
continue;
}
const elemBox = el.getBoundingClientRect();
const styles = window.getComputedStyle(el);
let scaleX = '1.0';
let scaleY = '1.0';
const matches = styles.transform.match(transformRegex);
if (matches) {
[, scaleX, scaleY] = matches;
}
const data = {
top: elemBox.top - bbox.top,
left: elemBox.left - bbox.left,
opacity: Number.parseFloat(styles.opacity || '1'),
scaleX: Number.parseFloat(scaleX),
scaleY: Number.parseFloat(scaleY),
};
map.set(id, [el, data]);
}
return map;
}
/**
* Enqueues the animation of moved elements at animation frame timing.
*/
_enqueueAnimateChangedElements() {
if (this._animationEnqueued) {
return;
}
this._animationEnqueued = true;
// Render at microtask timing to prevent Safari flickers
Promise.resolve().then(() => {
this._animationEnqueued = false;
this._animateChangedElements();
});
}
/**
* Updates the registered event handlers, mutation observers and triggers animation..
*/
_updateListeners() {
this.removeEventListener('dom-change', this._mutationObserverUpdateHandler);
this._slot.removeEventListener('slotchange', this._mutationObserverUpdateHandler);
if (this.active) {
this.addEventListener('dom-change', this._mutationObserverUpdateHandler);
this._slot.addEventListener('slotchange', this._mutationObserverUpdateHandler);
}
this._updateMutationObserver();
}
/**
* Updates the mutation observer configuration and collects child position data,
* if necessary.
*/
_updateMutationObserver() {
this._childObserver.disconnect();
if (!this.active) {
return;
}
this._slot.assignedNodes()
.filter(el => el instanceof HTMLElement)
.forEach(child => this._childObserver.observe(child, {
attributes: true,
attributeFilter: [this.attrName],
}));
this._enqueueAnimateChangedElements();
}
/**
* Render the shadow dom contents.
*/
_render() {
this.shadowRoot.innerHTML = `
<style>
::slotted(.${this.transitionClassName}) {
transition: transform ${this.durationMs}ms ${this.easing} ${this.delayMs}ms,
opacity ${this.durationMs}ms ${this.easing} ${this.delayMs}ms;
}
</style>
<slot></slot>
`;
}
}
import DomFlip from './element';
customElements.define('dom-flip', DomFlip);
export default DomFlip;
//# sourceMappingURL=dom-flip.js.map
{
"name": "dom-flip",
"version": "0.2.4",
"version": "0.2.5",
"description": "Smooth position animation for web components",

@@ -5,0 +5,0 @@ "main": "dist/dom-flip.js",

# \<dom-flip\>
[![Build Status](https://travis-ci.org/Festify/dom-flip.svg?branch=master)](https://travis-ci.org/Festify/dom-flip)
[![Build Status](https://travis-ci.org/NeoLegends/dom-flip.svg?branch=master)](https://travis-ci.org/NeoLegends/dom-flip)
[![Greenkeeper badge](https://badges.greenkeeper.io/Festify/dom-flip.svg)](https://greenkeeper.io/)

@@ -15,3 +15,3 @@ [![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/Festify/dom-flip)

## Usage
You can use this element together with any templatizing element that modifies the DOM. The animated elements must be direct children of the `dom-flip` element.
You can use this element together with any element that modifies the DOM. The animated elements must be direct children of the `dom-flip` element.

@@ -51,3 +51,3 @@ To be able to correlate changes in the model to changes to the DOM, this element requires that you give every element a unique ID. This must be an attribute on the element itself and cannot be a property (because properties cannot be observed via MutationObserver).

// ...
// ... next animation frame

@@ -68,3 +68,9 @@ // Change their order

### Automatic registration
You can import the custom element class from `dom-flip/element` if you don't want it to automatically be registered within the custom elements registry.
## Performance
The element is designed to avoid layout thrashing as much as possible by batching work into microtasks and to animation frame timing.
## License
MIT

@@ -20,4 +20,4 @@ {

"files": [
"dom-flip.ts"
"src/dom-flip.ts"
]
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc