29Mar

GSAP and ScrollTrigger blur animation not working properly in Safari

ScrollTrigger should allow you to update the filter CSS value as the user scroll. It works well in Chrome and Firefox, but our friend Safari tells another story. Here is how to fix a blur timeline animation with ScrollTrigger in Safari.

GSAP and ScrollTrigger blur animation

On my web page, I have a background image I want to blur and a full-size colored overlay that fades in to increase readability. The background is fixed and the content scrolls over it.

This is an extract of my original code:

ScrollTrigger.create({
	trigger: '.hero',
	start: "top 30px",
	end: "bottom -50%",
	scrub: true,
	animation: gsap.timeline().to('.bga-fixed-image', {filter:"blur(100px)", webkitFilter:"blur(100px)", duration: 1}).to('.bga-fixed-overlay', { opacity: '1', duration: 1 }, '<')
});

The HTML was

<div class="bga-fixed">
	<img class="bga-fixed-image" src="media/images/hero.jpg" alt="Group of people speaking">
	<div class="bga-fixed-overlay"></div>
</div>

This should work, and it does in Firefox and Chrome, it’s smooth. The blur value goes from 0 to 100px as the users scroll and at the same time, the overlay opacity also goes from 0 to 1.

Sadly, Safari is buggy. I tried a few CSS things to enable hardware acceleration for CSS animations. It can be done using the ‘transform’ property in your CSS code. But no luck.

My solution: using an SVG and update the feGaussianBlur with the ScrollTrigger animation

ScrollTrigger.create({
	trigger: '.hero',
	start: "top 30px",
	end: "bottom -50%",
	scrub: true,
	animation: gsap.timeline()
		.to('#bgfeGaussianBlur', { attr:{stdDeviation:100} })
		.to('.bga-fixed-overlay', { opacity: 1 }, '<')
});

Here is my HTML, I have my fixed background (position fixed in CSS) and a DIV with the class hero on the page.

<div class="bga-fixed">
	<svg width="100%" height="100%" viewBox="0 0 1920 1080" overflow="visible" preserveAspectRatio="xMidYMid slice">
		<filter id="blurhero" x="-50%" y="-50%" width="300%" height="300%">
			<feGaussianBlur id="bgfeGaussianBlur" in="SourceGraphic" stdDeviation="0" />
		</filter>
		<image xlink:href="media/images/hero.jpg" width="100%" height="100%" filter="url(#blurhero)" />
	</svg>
	<div class="bga-fixed-overlay"></div>
</div>

<div class="hero" id="hero">
[...]
</div>

It’s a bit more annoying as the SVG needs to have a set dimension of the embedded image, it can’t just be an image with CSS object-fit: cover and responsive images, but that did the trick for the needs I have. Maybe this will do for you too!

Subscribe
Notify of

0 Comments
Inline Feedbacks
View all comments