Project

General

Profile

Bug #11067 » animation.patch

WWebWidget.js patch - Roel Standaert, 11/09/2022 03:13 PM

View differences:

src/js/WWebWidget.js
/* Note: this is at the same time valid JavaScript and C++. */
WT_DECLARE_WT_MEMBER(1, JavaScriptFunction, "animateDisplay", function(APP, id, effects, timing, duration, display) {
const WT = APP.WT;
const doAnimateDisplay = function(id, effects, timing, duration, display) {
const NoEffect = 0x0;
const SlideInFromLeft = 0x1;
const SlideInFromRight = 0x2;
const SlideInFromBottom = 0x3;
const SlideInFromTop = 0x4;
const Pop = 0x5;
const Fade = 0x100;
// const Ease = 0;
// const Linear = 1;
// const EaseIn = 2;
// const EaseOut = 3;
// const EaseInOut = 4;
// const CubicBezier = 5;
const timings = ["ease", "linear", "ease-in", "ease-out", "ease-in-out"],
inverseTiming = [0, 1, 3, 2, 4, 5];
const animationPrefix = WT.vendorPrefix(WT.styleAttribute("animation"));
const transitionPrefix = WT.vendorPrefix(WT.styleAttribute("transition"));
const transformPrefix = WT.vendorPrefix(WT.styleAttribute("transform"));
const el = WT.$(id),
animationEventEnd = animationPrefix === "Webkit" ?
"webkitAnimationEnd" :
"animationend",
transitionEventEnd = transitionPrefix === "Webkit" ?
"webkitTransitionEnd" :
"transitionend";
if (WT.css(el, "display") !== display) {
const p = el.parentNode;
if (p.wtAnimateChild) {
p.wtAnimateChild(WT, el, effects, timing, duration, { display: display });
return;
}
if (el.classList.contains("animating")) {
el.addEventListener(transitionEventEnd, function() {
doAnimateDisplay(id, effects, timing, duration, display);
}, { once: true });
return;
}
el.classList.add("animating");
const effect = effects & 0xFF,
hide = display === "none",
cssTiming = timings[hide ? inverseTiming[timing] : timing],
elStyle = {};
WT_DECLARE_WT_MEMBER(
1,
JavaScriptFunction,
"animateDisplay", /**
* @param {Object} APP
* @param {Object} APP.WT
* @param {Object} APP.layouts2
* @param {string} id
* @param {number} effects
* @param {number} timing
* @param {number} duration
* @param {string} display
*/
function(APP, id, effects, timing, duration, display) {
const WT = APP.WT;
/**
* @param {string} id
* @param {number} effects
* @param {number} timing
* @param {number} duration
* @param {string} display
*/
function doAnimateDisplay(id, effects, timing, duration, display) {
const Effect = {
None: 0x0,
SlideInFromLeft: 0x1,
SlideInFromRight: 0x2,
SlideInFromBottom: 0x3,
SlideInFromTop: 0x4,
Pop: 0x5,
Fade: 0x100,
};
const Timing = {
Ease: 0,
Linear: 1,
EaseIn: 2,
EaseOut: 3,
EaseInOut: 4,
CubicBezier: 5,
};
const timings = [
"ease",
"linear",
"ease-in",
"ease-out",
"ease-in-out",
];
const inverseTiming = [
Timing.Ease,
Timing.Linear,
Timing.EaseOut,
Timing.EaseIn,
Timing.EaseInOut,
Timing.CubicBezier,
];
const el = document.getElementById(id);
const elc = el.childElementCount === 1 ? el.firstElementChild : null;
/**
* @param {HTMLElement} el
* @param {Object} style
* @param {Object} [savedStyle]
*/
function set(el, style, savedStyle) {
for (const i of Object.keys(style)) {
let k = i;
if (k === "animationDuration" && animationPrefix !== "") {
k = animationPrefix + k.substring(0, 1).toUpperCase() +
k.substring(1);
} else if (k === "transform" && transformPrefix !== "") {
k = transformPrefix + k.substring(0, 1).toUpperCase() +
k.substring(1);
} else if (k === "transition" && transitionPrefix !== "") {
k = transitionPrefix + k.substring(0, 1).toUpperCase() +
k.substring(1);
}
if (savedStyle && typeof (savedStyle[k]) === "undefined") {
savedStyle[k] = el.style[k];
if (savedStyle && typeof savedStyle[i] === "undefined") {
savedStyle[i] = el.style[i];
}
el.style[k] = style[i];
el.style[i] = style[i];
}
}
const restore = set;
function onEnd() {
if (el.wtAnimatedHidden) {
el.wtAnimatedHidden(hide);
const displayChanged = (() => {
// Experimentally set display to the given display option, so
// we can check if the display value actually changed.
const elStyle = {};
const prevDisplay = WT.css(el, "display");
set(el, { display }, elStyle);
const newDisplay = WT.css(el, "display");
restore(el, elStyle);
return prevDisplay !== newDisplay;
})();
if (displayChanged) {
const p = el.parentNode;
if (p.wtAnimateChild) {
p.wtAnimateChild(WT, el, effects, timing, duration, { display });
return;
}
el.classList.remove("animating");
if (APP.layouts2) {
APP.layouts2.setElementDirty(el);
if (el.classList.contains("animating")) {
// already animating, wait until animation is done
Promise.all(
el.getAnimations()
.map((animation) => animation.finished)
).then(() => {
doAnimateDisplay(id, effects, timing, duration, display);
});
return;
}
}
function show() {
el.style.display = display;
if (el.wtPosition) {
el.wtPosition();
}
if (window.onshow) {
window.onshow();
}
}
el.classList.add("animating");
function animateStaticVertical() {
let targetHeight, currentHeight;
const elcStyle = {};
let elc;
const effect = effects & 0xFF,
hide = display === "none",
easing = timings[hide ? inverseTiming[timing] : timing],
elStyle = {},
elcStyle = {};
if (hide) {
currentHeight = WT.css(el, "height");
set(el, { height: currentHeight, overflow: "hidden" }, elStyle);
const animationOptions = {
duration,
iterations: 1,
easing,
};
if (effect === SlideInFromTop && el.childNodes.length === 1) {
elc = el.firstChild;
set(elc, { transform: "translateY(0)" }, elcStyle);
if (!WT.hasTag(elc, "TABLE")) {
set(elc, { display: "block" }, elcStyle);
}
function onEnd() {
if (el.wtAnimatedHidden) {
el.wtAnimatedHidden(hide);
}
targetHeight = "0px";
} else {
const pStyle = {};
set(p, { height: WT.css(p, "height"), overflow: "hidden" }, pStyle);
show();
if (WT.css(el, "height") === "0px") {
el.style.height = "auto";
if (el.getAnimations().length === 0) {
el.classList.remove("animating");
}
targetHeight = WT.css(el, "height");
set(el, { height: "0px", overflow: "hidden" }, elStyle);
restore(p, pStyle);
if (effect === SlideInFromTop) {
set(el, { WebkitBackfaceVisibility: "visible" }, elStyle);
el.scrollTop = 1000;
if (APP.layouts2) {
APP.layouts2.setElementDirty(el);
}
}
if (effects & Fade) {
set(el, { opacity: (hide ? 1 : 0) }, elStyle);
function show() {
el.style.display = display;
if (el.wtPosition) {
el.wtPosition();
}
if (window.onshow) {
window.onshow();
}
}
currentHeight = el.clientHeight; // force 'absorbing' set height
function animateStaticVertical() {
if (!hide) {
show();
}
set(el, { overflow: "hidden" }, elStyle);
setTimeout(function() {
set(el, { transition: "all " + duration + "ms " + cssTiming, height: targetHeight }, elStyle);
const animations = [];
const elcPos = (() => {
if (!elc) {
return null;
}
const elRect = el.getBoundingClientRect();
const elcRect = elc.getBoundingClientRect();
return {
left: elcRect.x - elRect.x - WT.px(el, "border-left-width"),
top: elcRect.y - elRect.y - WT.px(el, "border-top-width"),
};
})();
if (elc && effect === Effect.SlideInFromTop) {
// Content slides along
const visibleKeyframe = {
transform: "translateY(0px)",
};
const hiddenKeyframe = {
transform: `translateY(-${el.clientHeight}px)`,
};
const keyframes = hide ? [visibleKeyframe, hiddenKeyframe] : [hiddenKeyframe, visibleKeyframe];
animations.push(elc.animate(keyframes, animationOptions));
}
{
const hiddenKeyframe = {
height: "0px",
paddingTop: "0px",
paddingBottom: "0px",
borderTopWidth: "0px",
borderBottomWidth: "0px",
};
const visibleKeyframe = {
height: WT.css(el, "height"),
paddingTop: WT.css(el, "padding-top"),
paddingBottom: WT.css(el, "padding-bottom"),
borderTopWidth: WT.css(el, "border-top-width"),
borderBottomWidth: WT.css(el, "border-bottom-width"),
};
if (effects & Effect.Fade) {
hiddenKeyframe.opacity = 0;
visibleKeyframe.opacity = 1;
}
if (effects & Fade) {
set(el, { opacity: (hide ? 0 : 1) });
const keyframes = hide ? [visibleKeyframe, hiddenKeyframe] : [hiddenKeyframe, visibleKeyframe];
animations.push(el.animate(keyframes, animationOptions));
}
if (elc) {
set(elc, {
transition: "all " + duration + "ms " + cssTiming,
transform: "translateY(-" + currentHeight + ")",
}, elcStyle);
// Fix content position
set(el, { position: "relative" }, elStyle);
const style = {
position: "absolute",
left: `${elcPos.left}px`,
top: `${elcPos.top}px`,
};
set(elc, style, elcStyle);
}
el.addEventListener(transitionEventEnd, function() {
Promise.all(animations.map((animation) => animation.finished)).then(() => {
if (hide) {
el.style.display = display;
}
restore(el, elStyle);
if (effect === SlideInFromTop) {
el.scrollTop = 0;
if (elc) {
restore(elc, elcStyle);
}
}
restore(elc, elcStyle);
onEnd();
}, { once: true });
}, 0);
}
function animateAbsolute(cssSize, cssOffset, topleft, U) {
if (!hide) {
show();
});
}
const size = WT.px(el, cssSize),
hiddenU = (WT.px(el, cssOffset) + size) * (topleft ? -1 : 1);
let targetU;
if (hide) {
set(el, { transform: "translate" + U + "(0px)" }, elStyle);
targetU = hiddenU;
} else {
set(el, { transform: "translate" + U + "(" + hiddenU + "px)" }, elStyle);
targetU = 0;
}
/**
* @param {string} cssSize
* @param {string} cssOffset
* @param {boolean} topleft
* @param {string} U
*/
function animateAbsolute(cssSize, cssOffset, topleft, U) {
if (!hide) {
show();
}
if (effects & Fade) {
set(el, { opacity: (hide ? 1 : 0) }, elStyle);
}
const size = WT.px(el, cssSize),
hiddenU = (WT.px(el, cssOffset) + size) * (topleft ? -1 : 1);
setTimeout(function() {
set(el, {
transition: "all " + duration + "ms " + cssTiming,
transform: "translate" + U + "(" + targetU + "px)",
}, elStyle);
const hiddenKeyframe = {
transform: `translate${U}(${hiddenU}px)`,
};
const visibleKeyframe = {
transform: `translate${U}(0px)`,
};
if (effects & Fade) {
set(el, { opacity: (hide ? 0 : 1) });
if (effects & Effect.Fade) {
hiddenKeyframe.opacity = 0;
visibleKeyframe.opacity = 1;
}
el.addEventListener(transitionEventEnd, function() {
const keyframes = hide ? [visibleKeyframe, hiddenKeyframe] : [hiddenKeyframe, visibleKeyframe];
const animation = el.animate(keyframes, animationOptions);
animation.finished.then(() => {
if (hide) {
el.style.display = display;
}
restore(el, elStyle);
onEnd();
}, { once: true });
}, 50); // If this timeout is too small or 0, this will cause some browsers, like
// Chrome to sometimes not perform the animation at all.
}
function animateAbsoluteVertical() {
animateAbsolute("height", effect === SlideInFromTop ? "top" : "bottom", effect === SlideInFromTop, "Y");
}
function animateAbsoluteHorizontal() {
animateAbsolute("width", effect === SlideInFromLeft ? "left" : "right", effect === SlideInFromLeft, "X");
}
function animateTransition() {
set(el, { animationDuration: duration + "ms" }, elStyle);
if (hide) {
el.classList.remove("in");
});
}
let cl;
switch (effect) {
case Pop:
cl = "pop";
break;
case SlideInFromLeft:
cl = hide ? "slide" : "slide reverse";
break;
case SlideInFromRight:
cl = hide ? "slide reverse" : "slide";
break;
function animateAbsoluteVertical() {
animateAbsolute(
"height",
effect === Effect.SlideInFromTop ? "top" : "bottom",
effect === Effect.SlideInFromTop,
"Y"
);
}
cl += hide ? " out" : " in";
if (effects & Fade) {
cl += " fade";
function animateAbsoluteHorizontal() {
animateAbsolute(
"width",
effect === Effect.SlideInFromLeft ? "left" : "right",
effect === Effect.SlideInFromLeft,
"X"
);
}
if (!hide) {
show();
}
el.classList.add(...cl.split(" ").filter((el) => el !== ""));
el.addEventListener(animationEventEnd, function() {
function animateTransition() {
if (!hide) {
cl = cl.replace(" in", "");
show();
}
el.classList.remove(...cl.split(" ").filter((el) => el !== ""));
if (hide) {
el.style.display = display;
const hiddenKeyframe = {};
const visibleKeyframe = {};
switch (effect) {
case Effect.Pop:
set(el, { transformOrigin: "50% 50%" }, elStyle);
hiddenKeyframe.transform = "scale(.2)";
visibleKeyframe.transform = "scale(1)";
break;
case Effect.SlideInFromRight:
hiddenKeyframe.transform = "translateX(100%)";
// hiddenKeyframe.clipPath = 'polygon(-100% 0%, 0% 0%, 0% 100%, -100% 100%)';
visibleKeyframe.transform = "translateX(0)";
// visibleKeyframe.clipPath = 'polygon(-100% 0%, 100% 0%, 100% 100%, -100% 100%)';
break;
case Effect.SlideInFromLeft:
hiddenKeyframe.transform = "translateX(-100%)";
// hiddenKeyframe.clipPath = 'polygon(100% 0%, 200% 0%, 200% 100%, 100% 100%)';
visibleKeyframe.transform = "translateX(0)";
// visibleKeyframe.clipPath = 'polygon(0% 0%, 200% 0%, 200% 100%, 0% 100%)';
break;
}
restore(el, elStyle);
onEnd();
}, { once: true });
}
if (effects & Effect.Fade) {
hiddenKeyframe.opacity = 0;
visibleKeyframe.opacity = WT.css(el, "opacity");
}
const keyframes = hide ? [visibleKeyframe, hiddenKeyframe] : [hiddenKeyframe, visibleKeyframe];
const animation = el.animate(keyframes, animationOptions);
animation.finished.then(() => {
if (hide) {
el.style.display = display;
}
restore(el, elStyle);
setTimeout(function() {
const position = WT.css(el, "position"),
absolute = (position === "absolute" || position === "fixed");
onEnd();
});
}
const absolute = ["absolute", "fixed"].includes(WT.css(el, "position"));
switch (effect) {
case SlideInFromTop:
case SlideInFromBottom:
if (!absolute) {
animateStaticVertical();
} else {
case Effect.SlideInFromTop:
case Effect.SlideInFromBottom:
if (absolute) {
animateAbsoluteVertical();
} else {
animateStaticVertical();
}
break;
case SlideInFromLeft:
case SlideInFromRight:
case Effect.SlideInFromLeft:
case Effect.SlideInFromRight:
if (absolute) {
animateAbsoluteHorizontal();
} else {
animateTransition();
}
break;
case NoEffect:
case Pop:
case Effect.None:
case Effect.Pop:
animateTransition();
break;
}
}, 0);
}
}
};
doAnimateDisplay(id, effects, timing, duration, display);
});
doAnimateDisplay(id, effects, timing, duration, display);
}
);
WT_DECLARE_WT_MEMBER(
2,
src/js/WWebWidget.min.js
WT_DECLARE_WT_MEMBER(1,JavaScriptFunction,"animateDisplay",(function(t,i,e,n,s,a){const o=t.WT,r=function(i,e,n,s,a){const l=256,c=["ease","linear","ease-in","ease-out","ease-in-out"],d=[0,1,3,2,4,5],f=o.vendorPrefix(o.styleAttribute("animation")),u=o.vendorPrefix(o.styleAttribute("transition")),p=o.vendorPrefix(o.styleAttribute("transform")),h=o.$(i),m="Webkit"===f?"webkitAnimationEnd":"animationend",y="Webkit"===u?"webkitTransitionEnd":"transitionend";if(o.css(h,"display")!==a){const L=h.parentNode;if(L.wtAnimateChild){L.wtAnimateChild(o,h,e,n,s,{display:a});return}if(h.classList.contains("animating")){h.addEventListener(y,(function(){r(i,e,n,s,a)}),{once:!0});return}h.classList.add("animating");const T=255&e,k="none"===a,x=c[k?d[n]:n],A={};function b(t,i,e){for(const n of Object.keys(i)){let s=n;"animationDuration"===s&&""!==f?s=f+s.substring(0,1).toUpperCase()+s.substring(1):"transform"===s&&""!==p?s=p+s.substring(0,1).toUpperCase()+s.substring(1):"transition"===s&&""!==u&&(s=u+s.substring(0,1).toUpperCase()+s.substring(1));e&&void 0===e[s]&&(e[s]=t.style[s]);t.style[s]=i[n]}}const C=b;function g(){h.wtAnimatedHidden&&h.wtAnimatedHidden(k);h.classList.remove("animating");t.layouts2&&t.layouts2.setElementDirty(h)}function w(){h.style.display=a;h.wtPosition&&h.wtPosition();window.onshow&&window.onshow()}function v(t,i,n,r){k||w();const c=o.px(h,t),d=(o.px(h,i)+c)*(n?-1:1);let f;if(k){b(h,{transform:"translate"+r+"(0px)"},A);f=d}else{b(h,{transform:"translate"+r+"("+d+"px)"},A);f=0}e&l&&b(h,{opacity:k?1:0},A);setTimeout((function(){b(h,{transition:"all "+s+"ms "+x,transform:"translate"+r+"("+f+"px)"},A);e&l&&b(h,{opacity:k?0:1});h.addEventListener(y,(function(){k&&(h.style.display=a);C(h,A);g()}),{once:!0})}),50)}function E(){b(h,{animationDuration:s+"ms"},A);k&&h.classList.remove("in");let t;switch(T){case 5:t="pop";break;case 1:t=k?"slide":"slide reverse";break;case 2:t=k?"slide reverse":"slide"}t+=k?" out":" in";e&l&&(t+=" fade");k||w();h.classList.add(...t.split(" ").filter((t=>""!==t)));h.addEventListener(m,(function(){k||(t=t.replace(" in",""));h.classList.remove(...t.split(" ").filter((t=>""!==t)));k&&(h.style.display=a);C(h,A);g()}),{once:!0})}setTimeout((function(){const t=o.css(h,"position"),i="absolute"===t||"fixed"===t;switch(T){case 4:case 3:i?v("height",4===T?"top":"bottom",4===T,"Y"):function(){let t,i;const n={};let r;if(k){i=o.css(h,"height");b(h,{height:i,overflow:"hidden"},A);if(4===T&&1===h.childNodes.length){r=h.firstChild;b(r,{transform:"translateY(0)"},n);o.hasTag(r,"TABLE")||b(r,{display:"block"},n)}t="0px"}else{const i={};b(L,{height:o.css(L,"height"),overflow:"hidden"},i);w();"0px"===o.css(h,"height")&&(h.style.height="auto");t=o.css(h,"height");b(h,{height:"0px",overflow:"hidden"},A);C(L,i);if(4===T){b(h,{WebkitBackfaceVisibility:"visible"},A);h.scrollTop=1e3}}e&l&&b(h,{opacity:k?1:0},A);i=h.clientHeight;setTimeout((function(){b(h,{transition:"all "+s+"ms "+x,height:t},A);e&l&&b(h,{opacity:k?0:1});r&&b(r,{transition:"all "+s+"ms "+x,transform:"translateY(-"+i+")"},n);h.addEventListener(y,(function(){k&&(h.style.display=a);C(h,A);if(4===T){h.scrollTop=0;r&&C(r,n)}g()}),{once:!0})}),0)}();break;case 1:case 2:i?v("width",1===T?"left":"right",1===T,"X"):E();break;case 0:case 5:E()}}),0)}};r(i,e,n,s,a)}));WT_DECLARE_WT_MEMBER(2,JavaScriptFunction,"animateVisible",(function(t,i,e,n,s,a,o,r){}));
WT_DECLARE_WT_MEMBER(1,JavaScriptFunction,"animateDisplay",(function(t,n,i,s,e,o){const a=t.WT;!function n(i,s,e,o,r){const c=0,d=1,l=2,p=3,f=4,m=5,h=256,u=["ease","linear","ease-in","ease-out","ease-in-out"],y=[0,1,3,2,4,5],g=document.getElementById(i),w=1===g.childElementCount?g.firstElementChild:null;function b(t,n,i){for(const s of Object.keys(n)){i&&void 0===i[s]&&(i[s]=t.style[s]);t.style[s]=n[s]}}const x=b;if((()=>{const t={},n=a.css(g,"display");b(g,{display:r},t);const i=a.css(g,"display");x(g,t);return n!==i})()){const A=g.parentNode;if(A.wtAnimateChild){A.wtAnimateChild(a,g,s,e,o,{display:r});return}if(g.classList.contains("animating")){Promise.all(g.getAnimations().map((t=>t.finished))).then((()=>{n(i,s,e,o,r)}));return}g.classList.add("animating");const C=255&s,v="none"===r,R={},$={},_={duration:o,iterations:1,easing:u[v?y[e]:e]};function E(){g.wtAnimatedHidden&&g.wtAnimatedHidden(v);0===g.getAnimations().length&&g.classList.remove("animating");t.layouts2&&t.layouts2.setElementDirty(g)}function B(){g.style.display=r;g.wtPosition&&g.wtPosition();window.onshow&&window.onshow()}function T(t,n,i,e){v||B();const o=a.px(g,t),c={transform:`translate${e}(${(a.px(g,n)+o)*(i?-1:1)}px)`},d={transform:`translate${e}(0px)`};if(s&h){c.opacity=0;d.opacity=1}const l=v?[d,c]:[c,d];g.animate(l,_).finished.then((()=>{v&&(g.style.display=r);E()}))}function W(){v||B();const t={},n={};switch(C){case m:b(g,{transformOrigin:"50% 50%"},R);t.transform="scale(.2)";n.transform="scale(1)";break;case l:t.transform="translateX(100%)";n.transform="translateX(0)";break;case d:t.transform="translateX(-100%)";n.transform="translateX(0)"}if(s&h){t.opacity=0;n.opacity=a.css(g,"opacity")}const i=v?[n,t]:[t,n];g.animate(i,_).finished.then((()=>{v&&(g.style.display=r);x(g,R);E()}))}const k=["absolute","fixed"].includes(a.css(g,"position"));switch(C){case f:case p:k?T("height",C===f?"top":"bottom",C===f,"Y"):function(){v||B();b(g,{overflow:"hidden"},R);const t=[],n=(()=>{if(!w)return null;const t=g.getBoundingClientRect(),n=w.getBoundingClientRect();return{left:n.x-t.x-a.px(g,"border-left-width"),top:n.y-t.y-a.px(g,"border-top-width")}})();if(w&&C===f){const n={transform:"translateY(0px)"},i={transform:`translateY(-${g.clientHeight}px)`},s=v?[n,i]:[i,n];t.push(w.animate(s,_))}{const n={height:"0px",paddingTop:"0px",paddingBottom:"0px",borderTopWidth:"0px",borderBottomWidth:"0px"},i={height:a.css(g,"height"),paddingTop:a.css(g,"padding-top"),paddingBottom:a.css(g,"padding-bottom"),borderTopWidth:a.css(g,"border-top-width"),borderBottomWidth:a.css(g,"border-bottom-width")};if(s&h){n.opacity=0;i.opacity=1}const e=v?[i,n]:[n,i];t.push(g.animate(e,_))}if(w){b(g,{position:"relative"},R);const t={position:"absolute",left:`${n.left}px`,top:`${n.top}px`};b(w,t,$)}Promise.all(t.map((t=>t.finished))).then((()=>{v&&(g.style.display=r);x(g,R);x(w,$);E()}))}();break;case d:case l:k?T("width",C===d?"left":"right",C===d,"X"):W();break;case c:case m:W()}}}(n,i,s,e,o)}));WT_DECLARE_WT_MEMBER(2,JavaScriptFunction,"animateVisible",(function(t,n,i,s,e,o,a,r){}));
(3-3/3)