Jump to content
GreenSock

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

Custom animated property does not reset when fromTo used in rare case

Recommended Posts

Hi,

 

maybe you remember, I have created a custom animated property for the filter blur property. I think it has a bug, but I'm not sure, I think you can help me out.

 

I know that my timeline contains non optimized code, but it is the simplier version of the real code and it is made to be able to show the bug. If you reduce it to one timeline it will work fine, I know :)

 

First timeline animates the box from opacity 0 to 1. x and n2blur is there to make sure the properties stays at 0. After it completes, it starts the second timeline from 0s.

Second timeline animates the same box and moves it to x:200 and blur:5.

If you click on red box, the first timeline plays from the start.

 

After the page load the timeline1 starts ......... then we reach the end of the second timeline too. At this point, everything is fine.

Then click on the red box, It jumps back to opacity:0 and x:0, which is great, but it does NOT jump to blur:0 and this is what my problem is. I would like to behave like the in-built x property behaves and resets itself with the from value which applied in the first timeline.

 

 

 

 

 

window.n2FilterProperty = false;
var element = document.createElement("div");
if (/Edge\/\d./i.test(navigator.userAgent)) {
  //Edge has buggy filter implementation
} else if (element.style.webkitFilter !== undefined) {
  window.n2FilterProperty = "webkitFilter";
} else if (element.style.filter !== undefined) {
  window.n2FilterProperty = "filter";
}

if (window.n2FilterProperty) {
  _gsScope.CSSPlugin.registerSpecialProp(
    "n2blur",
    function(target, value, tween) {
      var start = 0,
        match;
      if (
        (match = target.style[window.n2FilterProperty].match(/blur\((.+)?px\)/))
      ) {
        start = parseFloat(match[1]);
      }
      if (start == value) {
        return function() {};
      }
      var diff = value - start;
      return function(ratio) {
        target.style[window.n2FilterProperty] =
          "blur(" + (start + diff * ratio) + "px)";
      };
    },
    0
  );
}

 

See the Pen GEXOmj by anon (@anon) on CodePen

Link to post
Share on other sites

Hello @RolandSoos and Welcome to the GreenSock  Forum!

 

In GSAP you don't need that custom function for CSS filter. Best to keep it simple with less code. ;)

 

You can just include both webkit and standard filter blur() CSS property and GSAP will apply both when needed. Latest Firefox now understands webkit-filter even if filter is not used. GSAP will use the -webkit-filter when needed for mobile chrome/safari on older android and iOS devices as fallback.

 

Also in order to bring filter blur() back to 0, its best to add a set() method at the start of timeline var instance.

 

Check out this example:

 

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

 

You can see the changes i made below:

 

var timeline = new TimelineLite(),
    timeline2 = new TimelineLite({paused: true});

// set filter blur(0px) at the begining of timeline even if restarted
timeline.set(".red",{
  filter: "blur(0px)",
  "-webkit-filter": "blur(0px)" /* GSAP will use if needed */
});

timeline.fromTo(
  ".red",
  3,
  {
    x: 0,
    autoAlpha: 0
  },
  { 
    x: 0, 
    autoAlpha: 1,
    onComplete: function(){
      timeline2.play(0);
    }
  }
);

timeline2.to(
  ".red",
  3,
  {   
    filter: "blur(5px)", 
    "-webkit-filter": "blur(5px)", /* GSAP will use if needed */
    x: 200, 
  }
);

$('.red').on('click', function(){
  timeline.invalidate().restart();
});

 

SVG filter blur is more widely supported than CSS filter.

 

You will also notice i changed opacity to autoAlpha for better performance. You will also notice i added the CSS property visibility: hidden to the .red CSS rule.

 

CSSPlugin Docs - autoAlpha property:

  • autoAlpha
    Identical to opacity except that when the value hits 0 the visibility property will be set to "hidden" in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than 0, visibility will be set to "inherit". It is not set to "visible" in order to honor inheritance (imagine the parent element is hidden - setting the child to visible explicitly would cause it to appear when that's probably not what was intended). And for convenience, if the element's visibility is initially set to "hidden" and opacity is 1, it will assume opacity should also start at 0. This makes it simple to start things out on your page as invisible (set your css visibility:hidden) and then fade them in whenever you want.
//fade out and set visibility:hidden
TweenLite.to(element, 2, {autoAlpha:0});
 
//in 2 seconds, fade back in with visibility:visible
TweenLite.to(element, 2, {autoAlpha:1, delay:2});

 

Resource:

GSAP set() : https://greensock.com/docs/TimelineMax/set()

CSSPlugin autoAlpha : https://greensock.com/docs/Plugins/CSSPlugin

 

Happy Tweening :)
 

  • Like 5
Link to post
Share on other sites

Thanks for the demo.

 

As Jonathan explained its probably best just to tween the filter value directly now.

 

I don't know all that is happening in your plugin, but a quick fix is to just set your starting blur value in your fromTo to a small non-zero value like:

 

timeline.fromTo(
  ".red",
  3,
  {
    n2blur: 0.00001,
    x: 100,
    opacity: 0,
  },
  { 
    n2blur: 0,
    x: -100, 
    opacity: 1,
    onComplete: function(){
      timeline2.play(0);
    }
  }
);

 

Not exactly sure why that helps. If we can see what is wrong with your plugin or GSAP we will let you know.

  • Like 2
Link to post
Share on other sites

Jonathan: Thanks, I understand your solution and I think it's great that you support filter property now. But our software already has its own structure for the data of animations. I'm unable to change the n2blur name and the value to something different. Maybe I can do the transformation of our objects when needed, but I feel it is just a hack and the registerSpecialProp should work consistent with the in-built properties.

 

Carl: Also I'm not able to change the default values from 0 to 0.000001 as it would be the same like transforming the property into a new name.

 

What I can do is to make changes to the function which registered with

Quote

_gsScope.CSSPlugin.registerSpecialProp

 

I feel that it is somewhere in GSAP as the reset works fine for the inbuilt properties, but does not work for properties which handled with the _gsScope.CSSPlugin.registerSpecialProp. Do you feel the same?

Link to post
Share on other sites

It took a while to learn to use your internal functions, but I think I found a workaround for this case which seems to work. My code is a little messy, but I wanted to update you about my progress.

 

See the Pen LLJMyx?editors=0010 by anon (@anon) on CodePen

 

Link to post
Share on other sites

I give you extra points for determination and work ethic ;)

 

I'd strongly recommend against tapping into private internal methods and properties like that. It's actually quite dangerous because if the internals change in future versions, your code is at risk. We don't officially support things like registerSpecialProp(). But don't worry, I think I have a much cleaner solution for you - just create your own plugin like this: 

 

(function() {
    var _div = document.createElement("div"),
        _filterProp = /Edge\/\d./i.test(navigator.userAgent) ? false : (_div.style.webkitFilter !== undefined) ? "webkitFilter" : (_div.style.filter !== undefined) ? "filter" : false;
    _gsScope._gsDefine.plugin({
        propName: "n2blur",
        API: 2,
        version: "1.0.0",
        overwriteProps: ["n2blur"],
        init: function(target, value, tween, index) {
            if (!_filterProp) { //browser doesn't support filters
                return true;
            }
            if (typeof(value) === "function") { //accommodate function-based values
                value = value(index, target);
            }
            var start = window.getComputedStyle(target)[_filterProp],
                end = "blur(" + value + "px)";
            if (start === "none") {
                start = "blur(0px)";
            }
            this._addTween(target.style, _filterProp, start, end, "n2blur");
            return true;
        }
    });
})();

 

That'll run completely independent of CSSPlugin. Zero dependencies (other than TweenLite). And it's a lot less code. I added support for function-based values as well. 

 

Here's a forked codepen: 

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

 

Does that help? 

  • Like 2
Link to post
Share on other sites

I'm really grateful for the plugin you made for me! It works like charm :) I didn't know that it is so easy to make like this in a GSAP plugin. It's great to see that GSAP is so extendable! 

 

Ps.: Maybe you should hide the _gsScope.CSSPlugin.registerSpecialProp inside the internals too :)

  • Like 1
Link to post
Share on other sites

Hey Jack,

I would like to use the clearProps parameter to clear n2blur my custom "property". 

 

I added the following code to your implementation, which seems to work when clearProps:"n2blur" present, but maybe you have an official suggestion to remove the property when not needed.

 

                if (start == "blur(0px)" && start == end) {
                    end = "none";
                }

 

Modified plugin:

    (function () {
        var _div = document.createElement("div"),
            _filterProp = /Edge\/\d./i.test(navigator.userAgent) ? false : (_div.style.webkitFilter !== undefined) ? "webkitFilter" : (_div.style.filter !== undefined) ? "filter" : false;
        _gsScope._gsDefine.plugin({
            propName: "n2blur",
            API: 2,
            version: "1.0.0",
            overwriteProps: ["n2blur"],
            init: function (target, value, tween, index) {
                if (!_filterProp) { //browser doesn't support filters
                    return true;
                }
                if (typeof(value) === "function") { //accommodate function-based values
                    value = value(index, target);
                }
                var start = window.getComputedStyle(target)[_filterProp],
                    end = "blur(" + value + "px)";
                if (start === "none") {
                    start = "blur(0px)";
                }
                if (start == "blur(0px)" && start == end) {
                    end = "none";
                }
                this._addTween(target.style, _filterProp, start, end, "n2blur");
                return true;
            }
        });
    })();

 

Link to post
Share on other sites

Hm, I don't quite understand - are you saying you want to be able to set the final value to "none"? Can you show me a use case, maybe in a codepen? 

Link to post
Share on other sites

Sure.

 

In this case when the animation ends, it removes the transform property from the element. I would like to do the same with my custom property as I had rendering issues in Chrome when filter:blur(0px) were preset and filter:none; or the removed css property solved the issue.

 

TweenLite.fromTo(".red", 3, {
  n2blur: 5,
  x: 100
}, {
  n2blur: 0,
  x: 0,
  clearProps: "transform,n2blur,filter"
});

 

 

See the Pen aLwNxG by mm00 (@mm00) on CodePen

 

Link to post
Share on other sites

Ah, okay - then I'd make it more automatic so that if it ends at 0, it just removes it automatically:

 

(function() {
    var _div = document.createElement("div"),
        _filterProp = /Edge\/\d./i.test(navigator.userAgent) ? false : (_div.style.webkitFilter !== undefined) ? "webkitFilter" : (_div.style.filter !== undefined) ? "filter" : false,
        _filterCSSProp = _filterProp ? _filterProp.replace(/([A-Z])/g, "-$1").toLowerCase() : "";
    _gsScope._gsDefine.plugin({
        propName: "n2blur",
        API: 2,
        version: "1.1.0",
        overwriteProps: ["n2blur"],
        init: function(target, value, tween, index) {
            if (!_filterProp) { //browser doesn't support filters
                return true;
            }
            if (typeof(value) === "function") { //accommodate function-based values
                value = value(index, target);
            }
            var start = window.getComputedStyle(target)[_filterProp],
                end = "blur(" + value + "px)";
            if (start === "none") {
                start = "blur(0px)";
            }
            this._style = target.style;
            this._remove = !value;
            this._addTween(target.style, _filterProp, start, end, "n2blur");
            return true;
        },
        set: function(ratio) {
          this._super.setRatio.call(this, ratio);
          //ratio is typically 1 when tween is done.
          if (ratio === 1 && this._remove) {
            this._style.removeProperty(_filterCSSProp);
          }
        }
    });
})();

 

Here's a fork: 

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

 

  • Like 1
Link to post
Share on other sites

Thank you Jack, it is good to see how extendable your plugin system. It would be great if it would have documentation, maybe in that case I would be able to write these things myself :)

 

BTW.: I'm not sure why, but I can not fork your Codepens as the Fork button is not there. Maybe it is a setting on your end which makes it harder to do changes to your codepens.

Link to post
Share on other sites
Noticed a bug in your code, so here is a fixed version.
In your code the _filterCSSProp variable was "webkit-filter", but it should be "-webkit-filter".


    (function () {
        var _div = document.createElement("div"),
            _filterProp = /Edge\/\d./i.test(navigator.userAgent) ? false : (_div.style.webkitFilter !== undefined) ? "webkitFilter" : (_div.style.filter !== undefined) ? "filter" : false,
            _filterCSSProp = _filterProp ? (_filterProp == 'filter' ? _filterProp : '-' + _filterProp.replace(/([A-Z])/g, "-$1").toLowerCase()) : "";
        _gsScope._gsDefine.plugin({
            propName: "n2blur",
            API: 2,
            version: "1.1.0",
            overwriteProps: ["n2blur"],
            init: function (target, value, tween, index) {
                if (!_filterProp) { //browser doesn't support filters
                    return true;
                }
                if (typeof(value) === "function") { //accommodate function-based values
                    value = value(index, target);
                }
                var start = window.getComputedStyle(target)[_filterProp],
                    end = "blur(" + value + "px)";
                if (start === "none") {
                    start = "blur(0px)";
                }
                this._style = target.style;
                this._remove = !value;
                this._addTween(target.style, _filterProp, start, end, "n2blur");
                return true;
            },
            set: function (ratio) {
                this._super.setRatio.call(this, ratio);
                console.trace(_filterProp, ratio, this._remove);
                //ratio is typically 1 when tween is done.
                if (ratio === 1 && this._remove) {
                    this._style.removeProperty(_filterCSSProp);
                }
            }
        });
    })();

 

Link to post
Share on other sites

Good catch. All you probably needed to do was to change the _filterProp from "webkitFilter" to "WebkitFilter" (capital). But hey, if your other version works, great! :)

Link to post
Share on other sites

Oh, and I forgot to mention - you asked about documenting the plugin stuff. There has been a TEMPLATE_Plugin.js file in the downloads for years. Just crack that open and you'll find exactly what [I think] you're asking for. :)

Link to post
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.

×