From 321e3eafa72313e4fe3c6af0d6222f8a2125da3a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 10:00:29 -0400 Subject: [PATCH] feat: better `flip` animations (#13317) * fix: flip not accounting for scaled lengths * zoom is uniform, one calculation will do * stash width/height multipliers * use var * scale-first appears to work better? * more robust zoom calculation * changeset --------- Co-authored-by: Etai Nadler <75544136+etainad008@users.noreply.github.com> --- .changeset/cold-socks-learn.md | 5 +++ packages/svelte/src/animate/index.js | 48 +++++++++++++++++++++------- 2 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 .changeset/cold-socks-learn.md diff --git a/.changeset/cold-socks-learn.md b/.changeset/cold-socks-learn.md new file mode 100644 index 0000000000..51c45b8f19 --- /dev/null +++ b/.changeset/cold-socks-learn.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +feat: account for `zoom` when calculating animation transforms diff --git a/packages/svelte/src/animate/index.js b/packages/svelte/src/animate/index.js index f676ff6bf8..b04fe6492b 100644 --- a/packages/svelte/src/animate/index.js +++ b/packages/svelte/src/animate/index.js @@ -12,22 +12,48 @@ import { cubicOut } from '../easing/index.js'; * @returns {AnimationConfig} */ export function flip(node, { from, to }, params = {}) { - const style = getComputedStyle(node); - const transform = style.transform === 'none' ? '' : style.transform; - const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat); - const dx = from.left + (from.width * ox) / to.width - (to.left + ox); - const dy = from.top + (from.height * oy) / to.height - (to.top + oy); - const { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params; + var style = getComputedStyle(node); + var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom + + var transform = style.transform === 'none' ? '' : style.transform; + var [ox, oy] = style.transformOrigin.split(' ').map(parseFloat); + var dsx = from.width / to.width; + var dsy = from.height / to.height; + + var dx = (from.left + dsx * ox - (to.left + ox)) / zoom; + var dy = (from.top + dsy * oy - (to.top + oy)) / zoom; + var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params; + return { delay, duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration, easing, css: (t, u) => { - const x = u * dx; - const y = u * dy; - const sx = t + (u * from.width) / to.width; - const sy = t + (u * from.height) / to.height; - return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`; + var x = u * dx; + var y = u * dy; + var sx = t + u * dsx; + var sy = t + u * dsy; + return `transform: ${transform} scale(${sx}, ${sy}) translate(${x}px, ${y}px);`; } }; } + +/** + * @param {Element} element + */ +function get_zoom(element) { + if ('currentCSSZoom' in element) { + return /** @type {number} */ (element.currentCSSZoom); + } + + /** @type {Element | null} */ + var current = element; + var zoom = 1; + + while (current !== null) { + zoom *= +getComputedStyle(current).zoom; + current = /** @type {Element | null} */ (current.parentNode); + } + + return zoom; +}