
package.build.modules.hero.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of snabbdom Show documentation
Show all versions of snabbdom Show documentation
A virtual DOM library with focus on simplicity, modularity, powerful features and performance.
The newest version!
const raf = (typeof window !== "undefined" && window.requestAnimationFrame) || setTimeout;
const nextFrame = function (fn) {
raf(function () {
raf(fn);
});
};
function setNextFrame(obj, prop, val) {
nextFrame(function () {
obj[prop] = val;
});
}
function getTextNodeRect(textNode) {
let rect;
if (document.createRange) {
const range = document.createRange();
range.selectNodeContents(textNode);
if (range.getBoundingClientRect) {
rect = range.getBoundingClientRect();
}
}
return rect;
}
function calcTransformOrigin(isTextNode, textRect, boundingRect) {
if (isTextNode) {
if (textRect) {
// calculate pixels to center of text from left edge of bounding box
const relativeCenterX = textRect.left + textRect.width / 2 - boundingRect.left;
const relativeCenterY = textRect.top + textRect.height / 2 - boundingRect.top;
return `${relativeCenterX}px ${relativeCenterY}px`;
}
}
return "0 0"; // top left
}
function getTextDx(oldTextRect, newTextRect) {
if (oldTextRect && newTextRect) {
return (oldTextRect.left +
oldTextRect.width / 2 -
(newTextRect.left + newTextRect.width / 2));
}
return 0;
}
function getTextDy(oldTextRect, newTextRect) {
if (oldTextRect && newTextRect) {
return (oldTextRect.top +
oldTextRect.height / 2 -
(newTextRect.top + newTextRect.height / 2));
}
return 0;
}
function isTextElement(elm) {
return elm.childNodes.length === 1 && elm.childNodes[0].nodeType === 3;
}
let removed, created;
function pre() {
removed = {};
created = [];
}
function create(oldVnode, vnode) {
const hero = vnode.data.hero;
if (hero && hero.id) {
created.push(hero.id);
created.push(vnode);
}
}
function destroy(vnode) {
const hero = vnode.data.hero;
if (hero && hero.id) {
const elm = vnode.elm;
vnode.isTextNode = isTextElement(elm); // is this a text node?
vnode.boundingRect = elm.getBoundingClientRect(); // save the bounding rectangle to a new property on the vnode
vnode.textRect = vnode.isTextNode
? getTextNodeRect(elm.childNodes[0])
: null; // save bounding rect of inner text node
const computedStyle = window.getComputedStyle(elm, undefined); // get current styles (includes inherited properties)
vnode.savedStyle = JSON.parse(JSON.stringify(computedStyle)); // save a copy of computed style values
removed[hero.id] = vnode;
}
}
function post() {
let i, id, newElm, oldVnode, oldElm, hRatio, wRatio, oldRect, newRect, dx, dy, origTransform, origTransition, newStyle, oldStyle, newComputedStyle, isTextNode, newTextRect, oldTextRect;
for (i = 0; i < created.length; i += 2) {
id = created[i];
newElm = created[i + 1].elm;
oldVnode = removed[id];
if (oldVnode) {
isTextNode = oldVnode.isTextNode && isTextElement(newElm); // Are old & new both text?
newStyle = newElm.style;
newComputedStyle = window.getComputedStyle(newElm, undefined); // get full computed style for new element
oldElm = oldVnode.elm;
oldStyle = oldElm.style;
// Overall element bounding boxes
newRect = newElm.getBoundingClientRect();
oldRect = oldVnode.boundingRect; // previously saved bounding rect
// Text node bounding boxes & distances
if (isTextNode) {
newTextRect = getTextNodeRect(newElm.childNodes[0]);
oldTextRect = oldVnode.textRect;
dx = getTextDx(oldTextRect, newTextRect);
dy = getTextDy(oldTextRect, newTextRect);
}
else {
// Calculate distances between old & new positions
dx = oldRect.left - newRect.left;
dy = oldRect.top - newRect.top;
}
hRatio = newRect.height / Math.max(oldRect.height, 1);
wRatio = isTextNode ? hRatio : newRect.width / Math.max(oldRect.width, 1); // text scales based on hRatio
// Animate new element
origTransform = newStyle.transform;
origTransition = newStyle.transition;
if (newComputedStyle.display === "inline") {
// inline elements cannot be transformed
newStyle.display = "inline-block"; // this does not appear to have any negative side effects
}
newStyle.transition = origTransition + "transform 0s";
newStyle.transformOrigin = calcTransformOrigin(isTextNode, newTextRect, newRect);
newStyle.opacity = "0";
newStyle.transform = `${origTransform}translate(${dx}px, ${dy}px) scale(${1 / wRatio}, ${1 / hRatio})`;
setNextFrame(newStyle, "transition", origTransition);
setNextFrame(newStyle, "transform", origTransform);
setNextFrame(newStyle, "opacity", "1");
// Animate old element
for (const key in oldVnode.savedStyle) {
// re-apply saved inherited properties
if (String(parseInt(key)) !== key) {
const ms = key.substring(0, 2) === "ms";
const moz = key.substring(0, 3) === "moz";
const webkit = key.substring(0, 6) === "webkit";
if (!ms && !moz && !webkit) {
// ignore prefixed style properties
oldStyle[key] = oldVnode.savedStyle[key];
}
}
}
oldStyle.position = "absolute";
oldStyle.top = `${oldRect.top}px`; // start at existing position
oldStyle.left = `${oldRect.left}px`;
oldStyle.width = `${oldRect.width}px`; // Needed for elements who were sized relative to their parents
oldStyle.height = `${oldRect.height}px`; // Needed for elements who were sized relative to their parents
oldStyle.margin = "0"; // Margin on hero element leads to incorrect positioning
oldStyle.transformOrigin = calcTransformOrigin(isTextNode, oldTextRect, oldRect);
oldStyle.transform = "";
oldStyle.opacity = "1";
document.body.appendChild(oldElm);
setNextFrame(oldStyle, "transform", `translate(${-dx}px, ${-dy}px) scale(${wRatio}, ${hRatio})`); // scale must be on far right for translate to be correct
setNextFrame(oldStyle, "opacity", "0");
oldElm.addEventListener("transitionend", function (ev) {
if (ev.propertyName === "transform") {
document.body.removeChild(ev.target);
}
});
}
}
removed = created = undefined;
}
export const heroModule = { pre, create, destroy, post };
//# sourceMappingURL=hero.js.map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy