mirror of
https://github.com/zen-browser/desktop.git
synced 2026-01-06 21:37:50 +00:00
chore: Glance motion animations to native, b=no-bug, c=glance
This commit is contained in:
@@ -35,7 +35,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT)
|
||||
});
|
||||
|
||||
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref('zen.glance.animation-duration') / 1000;
|
||||
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref('zen.glance.animation-duration');
|
||||
|
||||
init() {
|
||||
this.#setupEventListeners();
|
||||
@@ -253,19 +253,21 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
const xOffset = gZenVerticalTabsManager._prefsRightSide ? 20 : -20;
|
||||
|
||||
gZenUIManager.motion.animate(
|
||||
container,
|
||||
{
|
||||
opacity: [0, 1],
|
||||
x: [xOffset, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
type: 'spring',
|
||||
delay: this.#GLANCE_ANIMATION_DURATION - 0.2,
|
||||
bounce: 0,
|
||||
}
|
||||
);
|
||||
setTimeout(() => {
|
||||
gZenUIManager.elementAnimate(
|
||||
container,
|
||||
{
|
||||
opacity: [0, 1],
|
||||
x: [xOffset, 0],
|
||||
},
|
||||
{
|
||||
duration: 200,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
delay: this.#GLANCE_ANIMATION_DURATION - 200,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,7 +405,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
'.browserSidebarContainer'
|
||||
);
|
||||
|
||||
gZenUIManager.motion.animate(
|
||||
gZenUIManager.elementAnimate(
|
||||
parentSidebarContainer,
|
||||
{
|
||||
scale: [1, 0.98],
|
||||
@@ -411,8 +413,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
type: 'spring',
|
||||
bounce: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -428,8 +430,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
this.overlay.removeAttribute('fade-out');
|
||||
this.browserWrapper.setAttribute('animate', true);
|
||||
this.browserWrapper.style.top = `${top}px`;
|
||||
this.browserWrapper.style.left = `${left}px`;
|
||||
this.browserWrapper.style.transform = `translate(${left}px, ${top}px)`;
|
||||
this.browserWrapper.style.width = `${width}px`;
|
||||
this.browserWrapper.style.height = `${height}px`;
|
||||
|
||||
@@ -441,9 +442,18 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
* Store the original position for later restoration
|
||||
*/
|
||||
#storeOriginalPosition() {
|
||||
let transform = this.browserWrapper.style.transform;
|
||||
let [top, left] = [0, 0];
|
||||
if (transform && transform.startsWith('translate(')) {
|
||||
const match = transform.match(/translate\(([-\d.]+)px,\s*([-\d.]+)px\)/);
|
||||
if (match) {
|
||||
left = parseFloat(match[1]);
|
||||
top = parseFloat(match[2]);
|
||||
}
|
||||
}
|
||||
this.#glances.get(this.#currentGlanceID).originalPosition = {
|
||||
top: this.browserWrapper.style.top,
|
||||
left: this.browserWrapper.style.left,
|
||||
top,
|
||||
left,
|
||||
width: this.browserWrapper.style.width,
|
||||
height: this.browserWrapper.style.height,
|
||||
};
|
||||
@@ -473,14 +483,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
this.browserWrapper.prepend(imageDataElement);
|
||||
this.#glances.get(this.#currentGlanceID).elementImageData = data.elementData;
|
||||
|
||||
gZenUIManager.motion.animate(
|
||||
gZenUIManager.elementAnimate(
|
||||
imageDataElement,
|
||||
{
|
||||
opacity: [1, 0],
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 2,
|
||||
easing: 'easeInOut',
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
);
|
||||
|
||||
@@ -500,16 +510,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
browserElement.style.minHeight = `${minHeight}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transform origin for the animation
|
||||
* @param {Object} data - Glance data with position and dimensions
|
||||
* @returns {string} The transform origin CSS value
|
||||
*/
|
||||
#getTransformOrigin(data) {
|
||||
const { clientX, clientY } = data;
|
||||
return `${clientX}px ${clientY}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the main glance animation
|
||||
* @param {Object} data - Glance data
|
||||
@@ -521,21 +521,18 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
// Create curved animation sequence
|
||||
const arcSequence = this.#createGlanceArcSequence(data, 'opening');
|
||||
const transformOrigin = this.#getTransformOrigin(data);
|
||||
|
||||
this.browserWrapper.style.transformOrigin = transformOrigin;
|
||||
|
||||
// Only animate if there is element data, so we can apply a
|
||||
// nice fade-in effect to the content. But if it doesn't exist,
|
||||
// we just fall back to always showing the browser directly.
|
||||
if (data.elementData) {
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
gZenUIManager
|
||||
.elementAnimate(
|
||||
this.contentWrapper,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 2,
|
||||
easing: 'easeInOut',
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -544,10 +541,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
this.#animateParentBackground();
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
gZenUIManager
|
||||
.elementAnimate(this.browserWrapper, arcSequence, {
|
||||
duration: gZenUIManager.testingEnabled ? 0 : this.#GLANCE_ANIMATION_DURATION,
|
||||
ease: 'easeInOut',
|
||||
})
|
||||
.then(() => {
|
||||
this.#finalizeGlanceOpening(imageDataElement, browserElement, resolve);
|
||||
@@ -607,28 +603,29 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
);
|
||||
|
||||
const sequence = {
|
||||
top: [],
|
||||
left: [],
|
||||
transform: [],
|
||||
width: [],
|
||||
height: [],
|
||||
transform: [],
|
||||
};
|
||||
|
||||
const steps = this.#ARC_CONFIG.ARC_STEPS;
|
||||
const arcDirection = shouldArcDownward ? 1 : -1;
|
||||
|
||||
function easeInOutQuad(t) {
|
||||
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
function easeOutBack(x) {
|
||||
const c1 = 0.4;
|
||||
const c3 = c1 + 1;
|
||||
|
||||
return 1 + c3 * (x - 1) ** 3 + c1 * (x - 1) ** 2;
|
||||
}
|
||||
|
||||
function easeOutCubic(t) {
|
||||
return 1 - Math.pow(1 - t, 6);
|
||||
function easeInQuint(x) {
|
||||
return x * x * x * x * x;
|
||||
}
|
||||
|
||||
// First, create the main animation steps
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
const progress = i / steps;
|
||||
const eased = direction === 'opening' ? easeInOutQuad(progress) : easeOutCubic(progress);
|
||||
const eased = direction === 'opening' ? easeOutBack(progress) : easeInQuint(progress);
|
||||
|
||||
// Calculate size interpolation
|
||||
const currentWidth = startPosition.width + (endPosition.width - startPosition.width) * eased;
|
||||
@@ -643,32 +640,11 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
const y =
|
||||
startPosition.y + distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
|
||||
|
||||
sequence.transform.push(`translate(-50%, -50%) scale(1)`);
|
||||
sequence.top.push(`${y}px`);
|
||||
sequence.left.push(`${x}px`);
|
||||
sequence.transform.push(`translate(${x}px, ${y}px)`);
|
||||
sequence.width.push(`${currentWidth}px`);
|
||||
sequence.height.push(`${currentHeight}px`);
|
||||
}
|
||||
|
||||
let scale = 1;
|
||||
const bounceSteps = 60;
|
||||
if (direction === 'opening') {
|
||||
for (let i = 0; i < bounceSteps; i++) {
|
||||
const progress = i / bounceSteps;
|
||||
// Scale up slightly then back to normal
|
||||
scale = 1 + 0.003 * Math.sin(progress * Math.PI);
|
||||
// If we are at the last step, ensure scale is exactly 1
|
||||
if (i === bounceSteps - 1) {
|
||||
scale = 1;
|
||||
}
|
||||
sequence.transform.push(`translate(-50%, -50%) scale(${scale})`);
|
||||
sequence.top.push(sequence.top[sequence.top.length - 1]);
|
||||
sequence.left.push(sequence.left[sequence.left.length - 1]);
|
||||
sequence.width.push(sequence.width[sequence.width.length - 1]);
|
||||
sequence.height.push(sequence.height[sequence.height.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
@@ -724,8 +700,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
imageDataElement.remove();
|
||||
}
|
||||
|
||||
this.browserWrapper.style.transformOrigin = '';
|
||||
|
||||
browserElement.style.minWidth = '';
|
||||
browserElement.style.minHeight = '';
|
||||
|
||||
@@ -892,14 +866,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
*/
|
||||
#animateSidebarButtons(sidebarButtons) {
|
||||
if (sidebarButtons) {
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
gZenUIManager
|
||||
.elementAnimate(
|
||||
sidebarButtons,
|
||||
{ opacity: [1, 0] },
|
||||
{
|
||||
duration: 0.2,
|
||||
type: 'spring',
|
||||
bounce: this.#GLANCE_ANIMATION_DURATION - 0.1,
|
||||
duration: 100,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -928,8 +902,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
* @param {Element} browserSidebarContainer - The sidebar container
|
||||
*/
|
||||
#animateParentBackgroundClose(browserSidebarContainer) {
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
gZenUIManager
|
||||
.elementAnimate(
|
||||
browserSidebarContainer,
|
||||
{
|
||||
scale: [0.98, 1],
|
||||
@@ -937,8 +911,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 1.5,
|
||||
type: 'spring',
|
||||
bounce: 0,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -965,10 +938,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
const closingData = this.#createClosingDataFromOriginalPosition(originalPosition);
|
||||
const arcSequence = this.#createGlanceArcSequence(closingData, 'closing');
|
||||
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
gZenUIManager
|
||||
.elementAnimate(this.browserWrapper, arcSequence, {
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
ease: 'easeOut',
|
||||
})
|
||||
.then(() => {
|
||||
// Remove element preview after closing animation
|
||||
@@ -1485,7 +1457,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
this.browserWrapper.style.width = `${browserRect.width}px`;
|
||||
this.browserWrapper.style.height = `${browserRect.height}px`;
|
||||
|
||||
await gZenUIManager.motion.animate(
|
||||
await gZenUIManager.elementAnimate(
|
||||
this.browserWrapper,
|
||||
{
|
||||
width: ['85%', '100%'],
|
||||
@@ -1493,8 +1465,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
type: 'spring',
|
||||
bounce: 0,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
left: 0;
|
||||
flex: unset !important;
|
||||
/* Promote to its own layer during transitions to reduce jank */
|
||||
will-change: transform, top, left;
|
||||
will-change: transform;
|
||||
width: 85%;
|
||||
height: 100%;
|
||||
|
||||
@@ -148,6 +148,10 @@
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
}
|
||||
|
||||
&:not([has-finished-animation='true']) .browserStack {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
& browser {
|
||||
background: light-dark(rgb(255, 255, 255), rgb(32, 32, 32)) !important;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user