Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
JD9

Bug in CSSRulePlugin.min.js ?

Recommended Posts

Hi,
for my "gsFx.js" project (GSAP Effects Library), containing "plug-n-play / add water"-effects for GSAP beginners, I'm currently working on background transition/animation effects. I think I ran into a CSSRulePlugin bug, but please correct me if I'm wrong (I'm actually hoping I'm wrong so I can continue with adding effects to my library straight away ;-) ).

Please look at my Codepen example, here:

See the Pen MydOpJ by josdelien (@josdelien) on CodePen

 

I've created 2 hyperlinks, mimicking the same effect.

1) the first I animated using CSS, working fine on modern browsers;
2) the second using GSAP is only working on OSx Firefox, not on OSx Chrome or OSx Safari (I haven't tested any Microsoft browsers yet).

 

Note: For the :before / :after pseudo CSS, I've tried using inline CSS, external CSS (OSx Chrome then throws an error in the console).
But I prefer to setting the pseudo css properties in gsFx.js, because that way the only user-configurable variable calling this effect to add is the new backgroundColor of the pseudo)

What's the matter?

-Jos

 

EDIT1: hmm, I can try adding vendor prefixes... Just a minute, le tme try...

EDIT2: I was partially right .... On my dev server OSx Chrome ánd OSx Safari is OK now with the vendor-prefix included, but on the Codepen example it's still not working in Chrome nor Safari.

 

These are my header includes locally, which are working now for me. Is Codepen running other GSAP versions?
 

<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<script src="js/greensock-js-1.18.4/src/minified/TweenMax.min.js"></script>
<script src="js/greensock-js-1.18.4/src/minified/TimelineMax.min.js"></script>
<script src="js/greensock-js-1.18.4/src/minified/plugins/CSSPlugin.min.js"></script>
<script src="js/greensock-js-1.18.4/src/minified/plugins/CSSRulePlugin.min.js"></script>	
<script src="js/gsfx.js"></script>
<script src="js/main.js"></script>

(PS, partially off-topic, @Jack / @Carl, would this be an idea for implementing a .setRule() method onto CSSRulePlugin?

var sheet = (function(el) {	
	var style = document.createElement("style");		
	style.appendChild(document.createTextNode("")); // webKit hack	
	document.head.appendChild(style);
	return style.sheet;
})();
sheet.insertRule(el { background: rgb(103, 65, 114); content: ''; position: absolute; z-index: -1; top: 0; left: 0; right: 0; bottom: 0; transform: scaleX(0); transform-origin: 0% 50%; }", 0);

See the Pen XdwYZX by jonathan (@jonathan) on CodePen

Link to comment
Share on other sites

Nice job!

 

I have a solution but I'm not 100% sure why it was necessary to do that set()

 

//i don't know why but I had to do this first
  TweenMax.set(rule, {cssRule:{scaleX:0}})
  
  var animation = TweenMax.to(rule, time, { 
cssRule: {scaleX:1}, 
ease: Sine.easeOut,
    paused:true
} 
);

http://codepen.io/GreenSock/pen/ONYzLN?editors=0010

  • Like 1
Link to comment
Share on other sites

@Carl, cool huh! :P

PS 1: I still don't know why my version

See the Pen MydOpJ by josdelien (@josdelien) on CodePen

) isn't functioning on CodePen, but is indeed functioning on my develop server (http://monsterbrands.nl/projecten/gsFx/).

 

And I mean it was - and is - already functioning without your additional set()!

All OSx browsers I tested (FF, FF Dev Edition, Chrome, Safari) are working.
And I've just cross-browser tested it on Windows FireFox (working as well), Windows Chrome (working as well), Windows Edge (working as well), Windows IE11 (working as well).

 

PS 2: with your addition ...

TweenMax.set(rule, {cssRule:{scaleX:0}})

... all my OSx browsers are functioning as well on your CodePen version (

See the Pen ONYzLN by GreenSock (@GreenSock) on CodePen

)

 

So I'm very interested in knowing why it was necessary to do that set() , as well!

-Jos

Link to comment
Share on other sites

weird I see both versions working on Mac Chrome and I'm very confused especially because you are using this:

 

zbtJXAz.png

 

should be scaleX(1)

  • Like 1
Link to comment
Share on other sites

oh, also make sure you are using version 1.18.4 on CodePen. not /latest

Link to comment
Share on other sites

Huh? That's a typo I corrected hours ago! Yet if my webhost was caching that version, how come your browser's dev tools are showing it ánd it's working?
 

Edit: and even more strange, my CodePen version is still showing 1 typo (I'll keep it that way for debugging), yet it was already running correctly on Firefox (without the -webkit-transform prefix):

 

cssRule: { transform: "skaleX(1)", "-webkit-transform": "scaleX(1)" }

//--------------------  ^  -----------------------------  ^  ---------
Link to comment
Share on other sites

Hello JD9,

 

Firefox uses the Gecko rendering engine not webkit, so it just accepts transform. ;)

 

The webkit prefix is only for browsers like Safari and Chrome.

 

-webkit-transform is only required for:

  • Chrome version 29 and below
  • Safari 8.4 and below
  • Android 4.4.4 and below

http://caniuse.com/#feat=transforms2d

 

Also just a side note.. in the CSS style sheet, all prefixed versions come first and then the non prefixed version of the property is last.

 

UPDATE 8/17/2017

Firefox now accepts support for -webkit prefix as a fallback. 

 

;)

Link to comment
Share on other sites

Hi Jonathan,
 

Hello JD9,
 
Firefox uses the Gecko rendering engine not webkit, so it just accepts transform. ;)
 
The webkit prefix is only for browsers like Safari and Chrome.


That's my point exactly, why is and was my CodePen working on Firefox, running Gecko, when exactly there I made my typo "skaleX(1)"?
I deliberately left the typo on my CodePen, I added a comment above it. Have a look at it again if you like! ;-)

Cheers, Jos

Link to comment
Share on other sites

When i comment out the transform property and leave '-webkit-transform' only, then it doesn't work! Since Firefox does not support that -webkit prefix.

 

See the Pen YqbapJ by jonathan (@jonathan) on CodePen

 

So this will not work in Firefox and will only work in webkit since it will be ignored in Gecko:

TweenMax.to(CSSRulePlugin.getRule(".extra:after"), time, { 
    // below "skaleX(1)" is a typo, yet it's still functioning correctly
    // Jonathan - doesnt work with -webkit-transform
    cssRule: { 
        //transform: "skaleX(1)",
        "-webkit-transform": "scaleX(1)" // does not work in gecko (firefox)
    },		
    ease: Sine.easeOut, 
    paused: true 
});

So maybe one of the following is happening:

  • Firefox might be just doing something behind the scenes to allow skaleX() typo?
  • Or maybe Jack is doing something when he parses scaleX (skaleX()) and converts to a matrix()?

But either way Firefox does not run any CSS property with -webkit prefix ;)

 

:)

 

UPDATE 8/17/2017

Firefox now accepts support for -webkit prefix as a fallback. 

  • Like 1
Link to comment
Share on other sites

Hi Jonathan, since Carl wrote this...
 

weird I see both versions working on Mac Chrome and I'm very confused especially because you are using this:
... 
should be scaleX(1)

.. I doubt it to be a hidden built-in GSAP feature ;-) I've scanned the GSAP src-files for "skale", but couldn't find a match.
So the mystery remains! :P

 

Anyway, Jonathan, it's good that you've entered this thread. Because the whole point of this post of mine, is that I've been reading a specific thread  (http://greensock.com/forums/topic/12688-difficulty-converting-a-css3-transition-into-a-gsap-controlled-transition/page-2)  where the general advise - inspired by yourself actually! ;-) - was to "steer away from" pseudo element manipulation.

 

Yet for my (under development) "gsFx.js" Effects Snippet Library, (preview: http://monsterbrands.nl/projecten/gsFx/ ), specifically targeted at HTML / CSS / JavaScript / GSAP beginners, I explicitly needed a :before or :after pseudo element, yet without any relevant ("difficult") CSS. I want beginning users to be able to use gsFx.js as "add water" effects, like "hover.css" but running on GSAP. And therefore I needed to "inject" that CSS, not present in HTML or static CSS, for CSSRulePlugin to read (it has the .getRule() method, but no setRule() for pseudo elements). Pretty hard, being (somewhat of) a JS beginner myself! ;-)

 

Thanks for participating by the way!

Kind regards, Jos

Link to comment
Share on other sites

Yeah pseudo-elements can be really flaky. Webkit based browsers just started about a year and some change ago to support CSS transitions and animation on pseudo-elements. That is why i stay away until the spec and modern browsers can fix any of the weird behavior that happens when rendered. ;)

 

That's weird because I'm pretty sure that Jack is converting the transform in this case skale or scale to use matrix() .. so all Firefox is receiving from GSAP for the transform property is the matrix() not scale ( skale() ).

 

So that's why I don't think it's Firefox in this case. You can see in your #css:before pseudo-element rule that if you use:

transform: skale(1);
Firefox and Chrome and other browsers ignore the transform property, since skale() is not valid css transform value function.

 

See the Pen XdwYZX by jonathan (@jonathan) on CodePen

 

That is why I believe maybe GSAP is parsing the transform converting it into matrix(), but i could be wrong? :blink:

 

Without the CSSRulePlugin, and just using the CSSPlugin ... transform: "skale()" doesn't even work.

 

See the Pen GZaGdd by jonathan (@jonathan) on CodePen

 

So this is weird why only in the CSSRulePlugin accepts transform: "skale()" but not the CSSPlugin?

 

:)

  • Like 3
Link to comment
Share on other sites

Thanks for all the great detective work, Jonathan. 

 

I think with your findings and mine we can boil this down to

 

"Why do we need to do the set() in addition to setting the transform values in the rule when it is created?"

 

Being that it could take quite a while for Jack or anyone to keep track of all the details in this conversation I have a little video that I think sums it up nicely:

 

http://greensock.com/forums-support-files/CSSRule-transform.mp4

  • Like 4
Link to comment
Share on other sites

Very nice Carl. :)

 

Rudimentary phonics engine - that made my day.  :-D  :-D

 

I thought perhaps the CSSRulePlugin had become self-aware and would be changing its name to Skynet.

  • Like 4
Link to comment
Share on other sites

That was pretty crazy Carl ;)

 

Great video, rudimentary phonics engine.. I love it!

 

And I thought this video I saw the other day of an automated robot that cooked and prepared Ramen noodles without a human was cool, but this is pretty awesome!

 

I see a GSAP Artificial Intelligence coming online in the near future like SkyNet, like Craig pointed out above :)

  • Like 3
Link to comment
Share on other sites

Hilarious, guys. Love it. 

 

Carl's assumption was correct. No, not about the rudimentary phonics engine (though I'm gonna write that one down on the list of potential future upgrades), but the fact that an invalid value applied to a transform will basically just get ignored by the browser, and make it a "normal" (identity) matrix. In other words, try applying transform:skayle(1) directly in CSS and watch what the browser does. The parser will be like "Dude, that ain't valid. I'm ignoring that and assuming you want 'none'" which happens to be a scale of 1. 

 

The reason the set() made things "work" was simply because it set an initial value of scale to a non-normal value (0 in this case), so when you tried applying transform:skayle(1), GSAP fed that to the browser and read back the computed style which reported as matrix(1,0,0,1,0,0) which is the same as none which is the same as scaleX:1 and scaleY:1, thus it animated from scaleX:0 to scaleX:1. 

 

Make sense now? 

  • Like 4
Link to comment
Share on other sites

By the way, I strongly recommend avoiding the "transform" value itself and instead use GSAP's shortcuts like x, y, scaleX, scaleY, rotation, etc. because they're faster and more accurate when you've got rotational values that exceed 180 degrees. When you use "transform", GSAP must apply that to the element, then read back the computed style so that the browser mushes it all into a matrix() or matrix3d() which GSAP must then parse. This is because "transform" values can be infinitely long, chained values. In order to maximize accuracy, it must allow the browser to parse through the whole string and calculate the matrix values and then GSAP extracts the component data. If you just set the individual compenent(s) directly, it can skip all that. Not the end of the world; you'd probably only notice the performance difference in extreme situations where you're asking it to parse hundreds or thousands of values on one frame, but I'm a performance nut. 

  • Like 2
Link to comment
Share on other sites

This is almost exactly what I argued here...

http://greensock.com/forums/topic/14263-losing-original-transform-css-on-animation/?p=60843

 

Getting the identity matrix when you try mixing CSS transforms with GSAP transforms, leading to unexpected results. Maybe you should make a PSA about how you shouldn't mix the two without first loading the rudimentary phonics engine. 

  • Like 2
Link to comment
Share on other sites

Cool, I made it to the video series already! Yay! ;-)

 

Another thought: I could have omitted declaring (a bunch of) arguments in sheet.insertRule() , and move those arguments to TweenMax.set(rule, {cssRule:{ } })

I'm guessing performance-wise that would save a bit of under-the-hood translations?

-Jos

Link to comment
Share on other sites

Guys, another question regarding my gsFx.js library:

if, on a given web page using gsFx.js, multiple background effects were to be applied, where each function (to be copy pasted as-is into a "main.js" that's the whole idea) would contain the same function for "stylesheet injection" like so...

  // inject an extra stylesheet at runtime using an IIFE passed back into the 'sheet' object
	var sheet = (function() {	
		var style = document.createElement("style");		
		style.appendChild(document.createTextNode("")); // webKit hack	
		document.head.appendChild(style);
		return style.sheet;
	})();

... then I'm guessing a whole bunch of style objects are being added to the DOM. (Not sure if that's a big issue anyway when only e.g. 5 of these effects are to applied, but anyway it's not optimal).

=> How about running an IIFE in gsFx.js creating the sheet object once by default? It's then globally scoped so I can skip creating new style elements for every individual effect function.

 

EDIT #1: renaming the IIFE to "gsFx_sheet()" is probably even better for avoiding naming conflicts.
EDIT #2: if I would declare the gsFx_sheet() IIFE once in gsFx.js, including that (giant?) lib would become obligatory since the effect functions are then not fully self-contained. I could also, in each function, check if the "gsFx_sheet" object exists, and if not, create it by assigning the function to the "gsFx_sheet" object by omitting the "var" declaration, forcing it to become globally scoped.

Hmm.. need to think about what's most practical...

 

-Jos
 

Link to comment
Share on other sites

I think I'm getting the hang of it now! ;-)

Revised code:

$('.gsfx-bgsweep-right').on( gsFx_BgSweepRight( $('.gsfx-bgsweep-right'), { backgroundColor: "rgb(103, 65, 114)"} ) );

function gsFx_BgSweepRight(element, config) {
	config = config || {};
	var time = config.time != null ? config.time : 0.3;
	var backgroundColor = config.backgroundColor != null ? config.backgroundColor : "#f00";

	// if needed, inject an extra stylesheet at runtime
	
	if ( typeof gsFx_sheet == "undefined" )		
	{
		gsFx_sheet = (function() {	
			var style = document.createElement("style");		
			style.appendChild(document.createTextNode("")); // webKit hack	
			document.head.appendChild(style);
			return style.sheet;
		})();		
	}

	// inject pseudo element to the DOM at runtime	
	gsFx_sheet.insertRule(element.selector + ":after { content: ''; }", 0);
	var pseudo = element.selector + ":after";

	// read the DOM for the pseudo element
	var rule = CSSRulePlugin.getRule(pseudo);

	//  inspect all properties for debugging purposes
	//  var ruleObject = '';
	//  for (property in rule) {
	//  	ruleObject += property + ':' + rule[property] + '; ';
	//  };
	//  console.log(ruleObject);

	// add properties to the rule object
	TweenMax.set(rule, { cssRule: { 
		scaleX: 0,
		scaleY: 1,
		position: "absolute",
		zIndex: -1,		
		backgroundColor: backgroundColor,
		top: 0,
		left: 0,
		right: 0,
		bottom: 0,
		transformOrigin: "0% 50%"
	}})

	var animation = TweenMax.to(CSSRulePlugin.getRule(pseudo), time, { 			
		cssRule: { scaleX: 1, scaleY: 1 },		
		ease: Sine.easeOut, 
		paused: true } 
	);	

	return {	    
		mouseenter: function() {      
			animation.play();
		},

		mouseleave: function() {
			animation.reverse();	      
		}			   
	};	
}
  • Like 1
Link to comment
Share on other sites

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.
×