Jump to content
Search Community

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Posted

I was asked yesterday about animating to/from backgroundSize: "cover" or "contain" with GSAP, so I figured I'd share the solution here in case it helps anyone else. 

 

The problem: GSAP interpolates between numbers, but how is it supposed to interpolate between something like "300px 250px" and "contain" (not a number)? So I whipped together a function that basically translates "contain" or "cover" into their px-based equivalents for that particular element at whatever size it is then. Once we've got it converted, it's easy to animate. 

 

//this function converts the backgroundSize of an element from "cover" or "contain" or "auto" into px-based dimensions. To set it immediately, pass true as the 2nd parameter.
function getBGSize(element, setInPx) {
    var e = (typeof(element) === "string") ? document.querySelector(element) : element,
            cs = window.getComputedStyle(e),
            imageUrl = cs.backgroundImage,
            size = cs.backgroundSize,
            image, w, h, iw, ih, ew, eh, ratio;
    if (imageUrl && !/\d/g.test(size)) {
        image = new Image();
        image.setAttribute("src", imageUrl.replace(/(^url\("|^url\('|^url\(|"\)$|'\)$|\)$)/gi, "")); //remove any url() wrapper. Note: some browsers include quotes, some don't.
        iw = image.naturalWidth;
        ih = image.naturalHeight;
        ratio = iw / ih;
        ew = e.offsetWidth;
        eh = e.offsetHeight;
        if (!iw || !ih) {
            console.log("getBGSize() failed; image hasn't loaded yet.");
        }
        if (size === "cover" || size === "contain") {
            if ((size === "cover") === (iw / ew > ih / eh)) {
                h = eh;
                w = eh * ratio;
            } else {
                w = ew;
                h = ew / ratio;    
            }
        } else { //"auto"
            w = iw;
            h = ih;
        }
        size = Math.ceil(w) + "px " + Math.ceil(h) + "px";
        if (setInPx) {
            e.style.backgroundSize = size;
        }
    }
    return size;
}

 

The only catch is that the image must be already loaded, otherwise it's impossible to figure out the native dimensions of the image (aspect ratio). 

 

While it's technically possible to add this functionality into CSSPlugin, it didn't seem advisable because it eats up a fair amount of kb and it's EXTREMELY uncommon for folks to want to animate to/from a background-size of cover or contain. So maybe 0.0001% of the audience would benefit but 100% would pay the kb price. Didn't seem worthwhile, so a helper function like this struck me as more appropriate. Feel free to chime in if you disagree. 

 

Happy tweening!

See the Pen ?editors=0010 by GreenSock (@GreenSock) on CodePen.

  • Like 4
Posted

There goes @GreenSock again -- GreenShocking the community by whipping up new functions and dropping them into the forum like it's nothing. ;)

 

I can honestly say I've never even thought about animating from contain to cover, but this is pretty darn cool. 

:)

 

  • Like 5
Posted
10 hours ago, GreenSock said:

While it's technically possible to add this functionality into CSSPlugin, it didn't seem advisable because it eats up a fair amount of kb and it's EXTREMELY uncommon for folks to want to animate to/from a background-size of cover or contain. So maybe 0.0001% of the audience would benefit but 100% would pay the kb price. Didn't seem worthwhile, so a helper function like this struck me as more appropriate. Feel free to chime in if you disagree. 

 

It's a hard choice. I know people are going to start asking for this functionality more. I think there are better ways of animating this, like using an image and scale, but I know a lot of people will still want to do it with backgroundSize. The CSSPlugin is already pretty big, and trying to match every CSS feature, while probably technically possible for the most part, would just make the plugin even bigger. 

 

Maybe this would be better off as a utility. This is a very common problem for canvas developers too. I can't tell you how many helpers libraries I see like this.

https://github.com/kittykatattack/scaleToWindow

 

The math is still the same.

  • Like 5
Shaun Gorneau
Posted
15 hours ago, GreenSock said:

So maybe 0.0001% of the audience would benefit

 

I'm honestly curious ... what are those 0.0001% doing that requires scaling the background from `contain` or `cover` to a fixed/relative size? Are they looking for the tiling that occurs when either background dimension is less than the size of the element it's applied to and the background-image is set to repeat?

  • Like 1
Posted

Join the Slack - Animation at Work and you can see where the question came from. It was posted in the #gsap channel.

http://damp-lake-50659.herokuapp.com/

 

But I don't think the repeat was really part of the question. That's just what the demo had in place when Jack forked it. I think what people are trying to do is more of lightbox effect. Click on small image, and make it fill a larger area.

  • Like 2
Shaun Gorneau
Posted

OK, I see what dunkie was getting at ... wanting to do a .set('.something', {backgroundSize: 'cover'}) ...which @GreenSock addressed with a GSAP update! (man is Jack quick!!)

 

Back to the wanting an image to scale up to occupy a bigger space (which I definitely see the need for :) ) ... I've always found it super easy to apply the image background with cover ... but tween the element's size -- and perhaps within a wrapper so it doesn't mess with the document flow -- and let CSS worry about the actual sizing of the background. Like this (a perfectly hideous example :) )

 

See the Pen pLOdvx by sgorneau (@sgorneau) on CodePen.

 

Or like this in another thread that I chimed in on

 

See the Pen JLKbBm by sgorneau (@sgorneau) on CodePen.

 

But I might be missing a more specific use case. I'm not dismissing the solution :) ... just honestly curious where people would need to actually tween a background image.

  • Like 4
Posted

I would echo Blake's advice above about joining the Animation At Work slack and following these channels

http://damp-lake-50659.herokuapp.com/

 

#GSAP

#general

#codepen

#uianimation

#svg

 

There are definitely some interesting conversations going on and you get exposed to some ideas and techniques that you wouldn't necessarily stumble into elsewhere. 

 

At first we were worried that the #GSAP channel would turn into a support nightmare (with people expecting instant "live-chat" answers from Jack or myself) but so far it really hasn't been too bad. Just a few questions per week that are often answered by other people in a reasonable amount of time. 

  • Like 5
Posted

Hi @Shaun Gorneau,

 

26 minutes ago, Shaun Gorneau said:

But I might be missing a more specific use case. I'm not dismissing the solution :) ... just honestly curious where people would need to actually tween a background image.

 

You can tween a background image when animating image sprites or doing say a ken burns type of effect for an image using background-size. But usually the limitation falls short due to some browsers not doing sub-pixel rendering. For example, i used to be able to force autoRound to false and I would see Firefox and or Chrome animate on a sub-pixel level for values of background-size and background-position without pixel snapping, even though it would not render with GPU. But now it seems to be intermittent in honoring autoRound:false.

 

See the Pen AigpI by jonathan (@jonathan) on CodePen.

 

I prefer animating transforms like scale for images like @OSUblake said above, but there are a lot of uses for animating a background image, it just depends what works for each project.

 

Thanks for this solution @GreenSock Jack!

 

Thanks guys i just signed up for that Slack - Animation at Work.

 

Just my two bits :)

  • Like 4
  • 3 years later...
Chris Heuberger
Posted

Can this method be applied to animate a background image size from a percentage to cover?

 

So from

background-size: 150% auto;

to

background-size: cover;

 

Using the original function doesn't seem to work as configured: 

See the Pen ExmjWRW by ChrisBup (@ChrisBup) on CodePen.

Posted

Doesn't look like it, 

it's animating but it's not respecting the initial cover size

See the Pen 9f61045d39ed7ffb10b89ad9495a4dfe?editors=1111 by cassie-codes (@cassie-codes) on CodePen.



I'd probably steer clear of animating background images if possible though - it's likely to be a lot more janky than animating with transforms.

If you really need it maybe @GreenSock would update the helper?

Posted

I agree with @Cassie - it's almost always better to animate an actual image with scaleX/scaleY/x/y but I went ahead and enhanced the helper function to have several new features.

 

You can define a config object where you can set size, nativeWidth, and/or nativeHeight. It handles percentage-based values too. 

 

Here's a fork with that function in place: 

See the Pen BaRNZxm?editors=0010 by GreenSock (@GreenSock) on CodePen.

  • Like 4
  • 1 month later...
Chris Heuberger
Posted

Thanks! For anyone interested down the line, I made a slight modification just to prevent aspect ratio distortion when initial width or height are defined as auto instead of a percentage:

 

See the Pen oNwXewJ by ChrisBup (@ChrisBup) on CodePen.

  • Like 3
  • 5 months later...
Posted

I came across an interesting use-case for this function.

 

I wanted to animate TO a background-size:cover FROM a scaled UP size. I needed the background image to maintain it's aspect ratio and stay centered at it's large size through the tween. Prior to reading @Chris Heuberger's post I came up with my own custom solution that required bgSize() to return the dimensions without the "px" so I could scale them up.

 

As always I was looking for a quick, brute-force, solution and not necessarily the most elegant.

 

I hacked bgSize() so I could get back a custom object with width and height {width:xxx, height:yyy}) if I passed in a parameter of returnType:"object"

See the Pen dyZYdBv?editors=0110 by snorkltv (@snorkltv) on CodePen.

 

In the example above note:

  • the image is square at 1200x1200 native size
  • you can click to replay the animation
  • it isn't responsive but you can resize the browser and then reload to see it work at different aspect ratios
  • circles always stay perfectly round

I wasn't able to get the official OR Chris' modified function to perform like that but I may not have been using them right.

 

I think there is some value in being able to scale the "cover" dimensions and preserve aspect ratio.

 

My API suggestion would be that if you want to get "scaled cover" dimensions you could pass in

 

size:"cover-3" and return the cover dimensions at a scale of 3.

 

While I agree that in most cases it is probably better and easier just to scale an image as opposed to a background I had a very specific use where I was using the background image as part of a "clip-text" effect and thus needed the scaled-up cover dimensions.

 

See the Pen ea5402e6ae9a5a70723c280f3b06539c by snorkltv (@snorkltv) on CodePen.

 

If you look closely you will see that the background image inside the text is scaling down (as the text scales up) and it perfectly aligns with another background image that is set to "cover" and is sitting BEHIND the scaling text.

 

Notes

  • not responsive unless you reload after resize (didn't put that in)
  • performance is atrocious on FireFox but silky smooth on Chrome
  • I will probably try to just recreate this with SVG

Any way I guess what I'm voting for here is a clean way to scale up (or down) the cover dimensions (like in my first demo) and possibly contain too.

If that's already available OR if Chris' modifications can handle it just let me know.

 

Like @Shaun Gorneau mentioned I think these scenarios may be more common than the tiling effect in the original demo.

 

Regardless of my mis-use or misunderstanding of the function I'm glad it was available! Thanks!

 

 

 

 

 

 

 

 

  • Like 3
Posted

Thanks for the feedback and suggestion(s), @Carl. In the CodePen below, I whipped together a plugin just for you that'll let you do stuff like this: 

gsap.fromTo(".photo", {
	backgroundSize: {
		size: "cover", 
		scale: 3, // scale up the "cover" dimensions (pixels)
		nativeWidth:1200, 
		nativeHeight:1200
	}
}, {
	backgroundSize: {
		size: "cover", 
		nativeWidth:1200, 
		nativeHeight:1200
	}, 
	duration: 2
});

 

See the Pen rNYxENg?editors=0010 by GreenSock (@GreenSock) on CodePen.

 

Does that help? 

 

I added it to the helper functions page too. :)

  • Like 1
  • Thanks 2
Posted

Wow. That's Great.

The plugin makes it very easy to do what I need and also do things like animate from contain to a scaled up cover (which I found quite cool).

 

However, as I tend to do, I found an interesting issue.

Being that the background image in the demo had existing css to set background-size:cover I tried omitting the "size" property from the tween. I thought maybe it would just pick up the existing value.

 

I tried this tween

 

// now let's animate...
let tween = gsap.to(".photo", {
    backgroundSize: {
        nativeWidth:1200, 
        nativeHeight:1200,
        scale:2
    }, 
    duration: 2
});

 

I'm not embedding the pen as it seemed to cause codepen to lockup and I couldn't edit it or re-run. Eventually Chrome gave me an "aw snap, something went wrong". 

 

If specifying size in the tween is required that's totally fine, but maybe it's worth looking into preventing any big problems if it isn't.

 

Otherwise, this is great. Thanks so much for creating it!

Posted
8 hours ago, Carl said:

Being that the background image in the demo had existing css to set background-size:cover I tried omitting the "size" property from the tween. I thought maybe it would just pick up the existing value.

Sure, I added the code necessary to accommodate that. Better? 

 

See the Pen rNYxENg by GreenSock (@GreenSock) on CodePen.

  • Like 1
Posted

Thanks  @GreenSock that's really great.

 

To keep up with the forum trend of "just one more question"...

 

At first I wasn't sure if the getBGSize() helper function would still have value, but alas I find I could still use it. I wonder if it would be worthwhile to combine the two so that BackgroundSize Plugin has a getBGSize() method? 

 

I know this may be pushing the bounds of what should be expected of an un-official plugin so I don't have high expectations and it's not the end of the world to include them both separately. 

  • Haha 1
Posted
3 hours ago, Carl said:

To keep up with the forum trend of "just one more question"...

 

Please read the forum guidelines - we cannot provide custom consulting services...

 

Just kidding - we make exceptions for chaps like you who have invested thousands of hours into this community :)

 

Your wish is my command:

See the Pen rNYxENg?editors=0110 by GreenSock (@GreenSock) on CodePen.

 

let containSize = BackgroundSizePlugin.getSize(".photo", {size: "contain", nativeWidth: 1200, nativeHeight: 1200}); 
console.log("containSize:", containSize); // {width: 513, height: 513}

 

Is that what you were looking for? 

  • Haha 3
Posted

OOh this will be me all morning

giphy-downsized-large.gif

The returned object with the width and height will be very easy to work with. Thanks a million!

  • Like 1
  • Haha 2
Posted

I just released a little lesson about this plugin.

Here's a demo that anyone is free to use which should help illustrate what the plugin can do.

Thanks again for whipping this together!

 

See the Pen QWOEjBZ?editors=0010 by snorkltv (@snorkltv) on CodePen.

  • Like 2
  • Thanks 1
Posted

Nice one, @Carl. I watched the new lesson video. ?

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...