chore: Motion to native animatiosn migration, p=#11796

* chore: Motion to native animatiosn migration, b=no-bug, c=common, glance, split-view

* Discard changes to src/zen/glance/zen-glance.css

* Discard changes to src/zen/glance/ZenGlanceManager.mjs
This commit is contained in:
mr. m
2026-01-03 02:42:17 +01:00
committed by GitHub
parent bc8a26223b
commit e118cc1a20
3 changed files with 65 additions and 36 deletions

View File

@@ -65,6 +65,49 @@ window.gZenUIManager = {
ZenMenubar.init();
},
/**
* Animate an element using Element.animate API.
* This is not using gZenUIManager.motion, because motion library has some issues
* with certain properties and we want to have a simple wrapper for that.
*/
async elementAnimate(element, rawKeyframes, ...args) {
rawKeyframes = { ...rawKeyframes };
// Convert 'y' property to 'transform' with translateY and 'x' to translateX,
// and 'scale' to 'transform' with scale.
if ((rawKeyframes.y || rawKeyframes.x || rawKeyframes.scale) && !rawKeyframes.transform) {
const yValues = rawKeyframes.y || [];
const xValues = rawKeyframes.x || [];
const scaleValues = rawKeyframes.scale || [];
delete rawKeyframes.y;
delete rawKeyframes.x;
delete rawKeyframes.scale;
rawKeyframes.transform = [];
console.assert(
yValues.length === 0 || xValues.length === 0 || yValues.length === xValues.length,
'y and x keyframes must have the same length'
);
const length = Math.max(yValues.length, xValues.length, scaleValues.length);
for (let i = 0; i < length; i++) {
const y = yValues[i] !== undefined ? `translateY(${yValues[i]}px)` : '';
const x = xValues[i] !== undefined ? `translateX(${xValues[i]}px)` : '';
const scale = scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : '';
rawKeyframes.transform.push(`${x} ${y} ${scale}`.trim());
}
}
let keyframes = [];
for (let i = 0; i < Object.values(rawKeyframes)[0].length; i++) {
let frame = {};
for (const [property, values] of Object.entries(rawKeyframes)) {
frame[property] = values[i];
}
keyframes.push(frame);
}
return await new Promise((resolve) => {
const animation = element.animate(keyframes, ...args);
animation.onfinish = () => resolve();
});
},
_addNewCustomizableButtonsIfNeeded() {
const kPref = 'zen.ui.migration.compact-mode-button-added';
let navbarPlacements = CustomizableUI.getWidgetIdsInArea('zen-sidebar-top-buttons');

View File

@@ -97,9 +97,7 @@
const { offsetX, offsetY } = this.#getDragImageOffset(event, tab, draggingTabs);
const dragImage = this.#createDragImageForTabs(draggingTabs);
this.originalDragImageArgs = [dragImage, offsetX, offsetY];
setTimeout(() => {
dt.setDragImage(...this.originalDragImageArgs);
}, 0);
dt.setDragImage(...this.originalDragImageArgs);
}
#createDragImageForTabs(movingTabs) {
@@ -520,18 +518,7 @@
let dropElementGroup = dropElement?.group;
let colorCode = dropElementGroup?.color;
let lastUnmovingTabInGroup = dropElementGroup?.tabs.findLast((t) => !movingTabsSet.has(t));
if (
isTab(dropElement) &&
dropElementGroup &&
dropElement == lastUnmovingTabInGroup &&
!dropBefore
) {
// Dragging tab over the last tab of a tab group, but not enough
// for it to drop into the tab group. Drop it after the tab group instead.
dropElement = dropElementGroup;
colorCode = undefined;
} else if (isTabGroupLabel(dropElement)) {
if (isTabGroupLabel(dropElement)) {
// Dropping right before the first tab in the tab group.
dropElement = dropElementGroup.tabs[0];
dropBefore = true;
@@ -711,7 +698,6 @@
!gZenStartup.isReady ||
gReduceMotion ||
!dropElement ||
dropElement.group !== draggedTab.group ||
dropElement.hasAttribute('zen-essential') ||
draggedTab.hasAttribute('zen-essential') ||
draggedTab.getAttribute('zen-workspace-id') != gZenWorkspaces.activeWorkspace
@@ -725,26 +711,24 @@
}
const animateElement = (ele, translateY) => {
ele.style.transform = `translateY(${translateY}px)`;
setTimeout(() => {
setTimeout(() => {
animations.push(
gZenUIManager.motion
.animate(
ele,
{
y: [translateY, 0],
},
{
duration: 0.1,
bounce: 0,
}
)
.then(() => {
ele.style.transform = '';
})
);
});
});
let animateInternal = (resolve) => {
gZenUIManager
.elementAnimate(ele, { y: [translateY, 0] }, { duration: 100, easing: 'ease-out' })
.then(() => {
ele.style.transform = '';
ele.style.zIndex = '';
})
.finally(resolve);
};
// Wait for the next event loop tick to ensure the initial transform style is applied.
// We need to ensure the element has already been moved in the DOM before starting the animation.
animations.push(
new Promise((resolve) =>
setTimeout(() => {
setTimeout(() => animateInternal(resolve), 0);
})
)
);
};
const items = this._tabbrowserTabs.ariaFocusableItems;
let rect = window.windowUtils.getBoundsWithoutFlushing(draggedTab);
@@ -781,6 +765,7 @@
: -rect.height * tabsInBetween.length;
draggedTabTranslateY +=
extraTranslate * (draggedTab.elementIndex > dropElement.elementIndex ? 1 : -1);
draggedTab.style.zIndex = '9';
animateElement(draggedTab, draggedTabTranslateY);
} catch (e) {
console.error(e);

View File

@@ -335,6 +335,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
const side = event.clientX - threshold > halfWidth ? 'right' : 'left';
for (const browser of gBrowser.browsers) {
if (!browser) continue;
const width = browser.getBoundingClientRect().width;
// Only apply it to the left side because if we add it to the right side,
// we wont be able to move the element to the left.