garris / BackstopJS

Catch CSS curve balls.
http://backstopjs.org
MIT License
6.71k stars 603 forks source link

How to avoid diffs on elements with css animation? #1496

Open use opened 1 year ago

use commented 1 year ago

I have CSS animations which happen when the page gains focus in the browser. No amount of the delay property stops these animations from firing at the moment the screenshot is taken.

Inevitably, this causes diffs due to tiny differences in the frame of CSS animation the element is on.

Is there any way to avoid these diffs?

garris commented 1 year ago

How about waiting for onAnimationEnd events?

use commented 1 year ago

So to clarify - I would add an onAnimationEnd listener to the element in question. This listener would print something to the console. And I would use readyEvent to check for that string in the console?

rossjrw commented 1 year ago

Try disabling the animations. I do the following:

You should endeavour to make sure that any large CSS animations of the kind you describe are gated behind a prefers-reduced-motion check. Then VRT process gets a nice clean static page to screenshot, and as a free bonus, your site is more accessible.

Works great for JS-based animations too (unlike, say, * { animation: none })

/* backstop_data/engine_scripts/backstop.css */
* {
  transition: none !important;
  transition-duration: 0 !important;
  transition-delay: 0 !important;
}
img[src$=".gif"] { display: none !important; }
// backstop_data/engine_scripts/puppet/onBefore.js
module.exports = async (page, scenario, vp) => {
  await page.emulateMediaFeatures([{name: 'prefers-reduced-motion', value: 'reduce'}])
}
// backstop_data/engine_scripts/puppet/onReady.js
const fs = require("fs")

module.exports = async (page, scenario, vp) => {
  console.log(`SCENARIO START: ${scenario.label}`)

  await page.addStyleTag({ content: fs.readFileSync("backstop_data/engine_scripts/backstop.css", "utf-8") })

  // Prompt lazy-loaded images to load
  await page.evaluate(() => document.querySelectorAll("img[loading=lazy]").forEach(img => img.loading = "eager"))
  await page.waitForNetworkIdle({ idleTime: 250 })

  console.log(`SCENARIO END: ${scenario.label}`)
}