From cd5826398526eb561171c224f12e41ee6de3e5f1 Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Sun, 7 Apr 2019 17:07:28 -0400 Subject: [PATCH 1/6] experiment with animating in where width, height dimensions are set to 'auto' --- example/width-height-auto.html | 37 ++++++++++++++++++++++++++++++++++ src/DisplayToggleFx.js | 23 ++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 example/width-height-auto.html diff --git a/example/width-height-auto.html b/example/width-height-auto.html new file mode 100644 index 0000000..6e72ab5 --- /dev/null +++ b/example/width-height-auto.html @@ -0,0 +1,37 @@ + + + + + + + + + + +
+ +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius efficitur dolor ac venenatis. Donec eu risus a orci porta varius. Fusce volutpat nulla vestibulum mi tincidunt vestibulum. Suspendisse vel condimentum eros, quis hendrerit elit. Praesent suscipit et tortor a euismod. Nunc egestas leo est, id accumsan urna blandit nec. Aliquam ante eros, maximus eu eros eu, porta luctus neque. Quisque at pellentesque orci. Nullam varius libero sit amet purus eleifend aliquam vel auctor lorem. Suspendisse convallis in neque ut interdum. Nam at augue ex. Suspendisse potenti. Pellentesque a fermentum justo.
+ +
+ + + + + + diff --git a/src/DisplayToggleFx.js b/src/DisplayToggleFx.js index 9e82c76..c3b8862 100644 --- a/src/DisplayToggleFx.js +++ b/src/DisplayToggleFx.js @@ -16,6 +16,9 @@ const DisplayToggleFx = { if(timeoutId !== null) { clearTimeout(timeoutId); } + + const preWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); + const preHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); // apply classes and get computed display for(let i=0; i<_fxClasses.length; i++) { @@ -23,15 +26,30 @@ const DisplayToggleFx = { } const computedDisplay = (window.getComputedStyle(_elem).getPropertyValue('display')); + const computedWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); + const computedHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); // remove classes and trigger reflow to render initial state for(let i=0; i<_fxClasses.length; i++) { _elem.classList.remove(_fxClasses[i]); } + + _elem.style.width = preWidth; + _elem.style.height = preHeight; + DisplayToggleFx.forceReflow(_elem); // apply computed display value and trigger reflow _elem.style.display = computedDisplay; + + if(computedWidth !== preWidth) { // can we see if 'auto' is actually set? + _elem.style.width = computedWidth; + } + + if(computedHeight !== preHeight) { + _elem.style.height = computedHeight; + } + DisplayToggleFx.forceReflow(_elem); for(let i=0; i<_fxClasses.length; i++) { @@ -82,13 +100,16 @@ const DisplayToggleFx = { const maxTransitionDuration = DisplayToggleFx.getMaxTransitionDuration(_elem); + _elem.style.removeProperty('width'); + _elem.style.removeProperty('height'); + for(let i=0; i<_fxClasses.length; i++) { _elem.classList.remove(_fxClasses[i]); } const timeoutId = setTimeout(function() { _elem.style.removeProperty('display'); - + if(_onOutComplete) { _onOutComplete(); } From 87c968a2a1e50af9f6092d53ef6b1cd2adb7436d Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Sun, 7 Apr 2019 17:52:28 -0400 Subject: [PATCH 2/6] only inline width,height on elem if transitioning those properties --- dist/display-toggle-fx.min.js | 2 +- example/height-auto.html | 37 +++++++++++++++++++++++++++++++ example/width-height-auto.html | 2 +- src/DisplayToggleFx.js | 40 +++++++++++++++++++++++----------- 4 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 example/height-auto.html diff --git a/dist/display-toggle-fx.min.js b/dist/display-toggle-fx.min.js index edffe9b..de6aa44 100644 --- a/dist/display-toggle-fx.min.js +++ b/dist/display-toggle-fx.min.js @@ -1 +1 @@ -"use strict";var DisplayToggleFx={elementOutTimeouts:new Map,in:function(e,t){var o=DisplayToggleFx.elementOutTimeouts.get(e)||null;null!==o&&clearTimeout(o);for(var l=0;lo&&(o=t)}else{var l=1e3*e.replace(/s/g,"");l>o&&(o=l)}}),o},out:function(e,t,o){for(var l=DisplayToggleFx.getMaxTransitionDuration(e),i=0;io&&(o=t)}else{const t=1e3*e.replace(/s/g,"");t>o&&(o=t)}}),o},out:function(e,t,o){const l=DisplayToggleFx.getMaxTransitionDuration(e);e.style.removeProperty("width"),e.style.removeProperty("height");for(let o=0;o + + + + + + + + + +
+ +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius efficitur dolor ac venenatis. Donec eu risus a orci porta varius. Fusce volutpat nulla vestibulum mi tincidunt vestibulum. Suspendisse vel condimentum eros, quis hendrerit elit. Praesent suscipit et tortor a euismod. Nunc egestas leo est, id accumsan urna blandit nec. Aliquam ante eros, maximus eu eros eu, porta luctus neque. Quisque at pellentesque orci. Nullam varius libero sit amet purus eleifend aliquam vel auctor lorem. Suspendisse convallis in neque ut interdum. Nam at augue ex. Suspendisse potenti. Pellentesque a fermentum justo.
+ +
+ + + + + + diff --git a/example/width-height-auto.html b/example/width-height-auto.html index 6e72ab5..1bebd7f 100644 --- a/example/width-height-auto.html +++ b/example/width-height-auto.html @@ -14,7 +14,7 @@
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius efficitur dolor ac venenatis. Donec eu risus a orci porta varius. Fusce volutpat nulla vestibulum mi tincidunt vestibulum. Suspendisse vel condimentum eros, quis hendrerit elit. Praesent suscipit et tortor a euismod. Nunc egestas leo est, id accumsan urna blandit nec. Aliquam ante eros, maximus eu eros eu, porta luctus neque. Quisque at pellentesque orci. Nullam varius libero sit amet purus eleifend aliquam vel auctor lorem. Suspendisse convallis in neque ut interdum. Nam at augue ex. Suspendisse potenti. Pellentesque a fermentum justo.
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius efficitur dolor ac venenatis. Donec eu risus a orci porta varius. Fusce volutpat nulla vestibulum mi tincidunt vestibulum. Suspendisse vel condimentum eros, quis hendrerit elit. Praesent suscipit et tortor a euismod. Nunc egestas leo est, id accumsan urna blandit nec. Aliquam ante eros, maximus eu eros eu, porta luctus neque. Quisque at pellentesque orci. Nullam varius libero sit amet purus eleifend aliquam vel auctor lorem. Suspendisse convallis in neque ut interdum. Nam at augue ex. Suspendisse potenti. Pellentesque a fermentum justo.
diff --git a/src/DisplayToggleFx.js b/src/DisplayToggleFx.js index c3b8862..e1883a4 100644 --- a/src/DisplayToggleFx.js +++ b/src/DisplayToggleFx.js @@ -17,37 +17,51 @@ const DisplayToggleFx = { clearTimeout(timeoutId); } - const preWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); - const preHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); - + const transitionProps = (window.getComputedStyle(_elem).getPropertyValue('transition-property')); // command-delimited string + const isTransitioningProperty = function(_propName) { + if(transitionProps.indexOf(_propName) === -1) { + return false; + } + + return true; + }; + + const currentWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); + const currentHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); + // apply classes and get computed display for(let i=0; i<_fxClasses.length; i++) { _elem.classList.add(_fxClasses[i]); } - const computedDisplay = (window.getComputedStyle(_elem).getPropertyValue('display')); - const computedWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); - const computedHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); + const futureDisplay = (window.getComputedStyle(_elem).getPropertyValue('display')); + const futureWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); + const futureHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); // remove classes and trigger reflow to render initial state for(let i=0; i<_fxClasses.length; i++) { _elem.classList.remove(_fxClasses[i]); } - _elem.style.width = preWidth; - _elem.style.height = preHeight; + if(isTransitioningProperty('width')) { + _elem.style.width = currentWidth; + } + + if(isTransitioningProperty('height')) { + _elem.style.height = currentHeight; + } DisplayToggleFx.forceReflow(_elem); // apply computed display value and trigger reflow - _elem.style.display = computedDisplay; + _elem.style.display = futureDisplay; - if(computedWidth !== preWidth) { // can we see if 'auto' is actually set? - _elem.style.width = computedWidth; + if(isTransitioningProperty('width')) { + _elem.style.width = futureWidth; } - if(computedHeight !== preHeight) { - _elem.style.height = computedHeight; + if(isTransitioningProperty('height')) { + _elem.style.height = futureHeight; } DisplayToggleFx.forceReflow(_elem); From 863560fe23c5adf3693cb4dae6f5af1b13988215 Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Mon, 8 Apr 2019 15:08:32 -0400 Subject: [PATCH 3/6] update tests --- test/DisplayToggleFx.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/DisplayToggleFx.test.js b/test/DisplayToggleFx.test.js index c9c8d8c..2860d95 100644 --- a/test/DisplayToggleFx.test.js +++ b/test/DisplayToggleFx.test.js @@ -35,6 +35,18 @@ test('DisplayToggleFx.in changes element display state based on window.getComput if(_key === `display`) { return `block`; } + + if(_key === `transition-property`) { + return `opacity, transform`; + } + + if(_key === `width`) { + return `64px`; + } + + if(_key === `height`) { + return `64px`; + } } } }; From d5a2927ade539cf2ce75c6b913b64bd66a61acc2 Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Sat, 6 Jul 2019 22:28:20 +0100 Subject: [PATCH 4/6] add comments, update width-height-auto example --- example/width-height-auto.html | 10 ++++------ src/DisplayToggleFx.js | 10 +++++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/example/width-height-auto.html b/example/width-height-auto.html index 1bebd7f..acbfe21 100644 --- a/example/width-height-auto.html +++ b/example/width-height-auto.html @@ -6,7 +6,7 @@ @@ -23,14 +23,12 @@ diff --git a/src/DisplayToggleFx.js b/src/DisplayToggleFx.js index e1883a4..eaa8c98 100644 --- a/src/DisplayToggleFx.js +++ b/src/DisplayToggleFx.js @@ -1,6 +1,8 @@ /** * DisplayToggleFx helps with applying/unapplying CSS classes that trigger CSS transitions, * where the DOM element needs to change from a display:none state → displayed state, and vice-versa + * + * v1.2 also adds support for cases where an element's width or height changes to 'auto' */ const DisplayToggleFx = { @@ -17,6 +19,8 @@ const DisplayToggleFx = { clearTimeout(timeoutId); } + // Get which properties are setup to transition + // We'll get all of them, but we really only care about dimensional properties (width, height) const transitionProps = (window.getComputedStyle(_elem).getPropertyValue('transition-property')); // command-delimited string const isTransitioningProperty = function(_propName) { if(transitionProps.indexOf(_propName) === -1) { @@ -29,7 +33,7 @@ const DisplayToggleFx = { const currentWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); const currentHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); - // apply classes and get computed display + // apply fx classes, compute future state of display and properties being transitioned for(let i=0; i<_fxClasses.length; i++) { _elem.classList.add(_fxClasses[i]); } @@ -38,7 +42,7 @@ const DisplayToggleFx = { const futureWidth = (window.getComputedStyle(_elem).getPropertyValue('width')); const futureHeight = (window.getComputedStyle(_elem).getPropertyValue('height')); - // remove classes and trigger reflow to render initial state + // remove classes, reset width and height to initial values, and trigger reflow to render initial state for(let i=0; i<_fxClasses.length; i++) { _elem.classList.remove(_fxClasses[i]); } @@ -53,7 +57,7 @@ const DisplayToggleFx = { DisplayToggleFx.forceReflow(_elem); - // apply computed display value and trigger reflow + // apply computed display value, apply computed width and height values (in cases where width, height is being transitioned), and trigger reflow _elem.style.display = futureDisplay; if(isTransitioningProperty('width')) { From 54e943de6fb7aab2b1cfd3dc4ba0f864de3804e4 Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Sat, 6 Jul 2019 22:44:04 +0100 Subject: [PATCH 5/6] fix to DisplayToggleFx.getMaxTransitionDuration --- src/DisplayToggleFx.js | 9 +++++---- test/DisplayToggleFx.test.js | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/DisplayToggleFx.js b/src/DisplayToggleFx.js index eaa8c98..ec29dc7 100644 --- a/src/DisplayToggleFx.js +++ b/src/DisplayToggleFx.js @@ -89,16 +89,17 @@ const DisplayToggleFx = { var maxDurationMs = 0; allTransitionDurations.forEach(function(_dur) { - if(_dur.indexOf('s') !== -1) { - const durationMs = (_dur.replace(/s/g, '')) * 1000.0; + + if(_dur.indexOf('ms') !== -1) { // note that this parser rule is more specific and has to come first + const durationMs = (_dur.replace(/ms/g, '')) * 1.0; if(durationMs > maxDurationMs) { maxDurationMs = durationMs; } return; } - if(_dur.indexOf('ms') !== -1) { - const durationMs = (_dur.replace(/ms/g, '')); + if(_dur.indexOf('s') !== -1) { + const durationMs = (_dur.replace(/s/g, '')) * 1000.0; if(durationMs > maxDurationMs) { maxDurationMs = durationMs; } diff --git a/test/DisplayToggleFx.test.js b/test/DisplayToggleFx.test.js index 2860d95..32555ac 100644 --- a/test/DisplayToggleFx.test.js +++ b/test/DisplayToggleFx.test.js @@ -112,7 +112,7 @@ test('DisplayToggleFx.out calls completion callback', (done) => { }, 901); }); -test('DisplayToggleFx.getMaxTransitionDuration returns max duration', () => { +test('DisplayToggleFx.getMaxTransitionDuration returns max duration for values in seconds', () => { $('body').html(``); @@ -130,3 +130,22 @@ test('DisplayToggleFx.getMaxTransitionDuration returns max duration', () => { expect(maxDurMs).toEqual(900); }); + +test('DisplayToggleFx.getMaxTransitionDuration returns max duration for values in milliseconds', () => { + + $('body').html(``); + + window.getComputedStyle = function(_elem) { + return { + getPropertyValue: function(_key) { + if(_key === `transition-duration`) { + return "0.3s, 800ms"; + } + } + } + }; + + const maxDurMs = DisplayToggleFx.getMaxTransitionDuration(document.getElementById('telem')); + + expect(maxDurMs).toEqual(800.0); +}); From f6cda13239c3286b592cb08d6fc1b43675911401 Mon Sep 17 00:00:00 2001 From: Avishkar Autar Date: Sat, 6 Jul 2019 23:13:28 +0100 Subject: [PATCH 6/6] update README --- README.md | 8 ++++++-- src/DisplayToggleFx.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e51558c..6021a68 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,16 @@ ## Motivation -This is small library that addresses a few issues when working with CSS transitions & elements that need to transition to or from the `display:none` state. +This is small library that addresses a few issues that pop up when working with CSS transitions on elements that need to transition to or from the `display:none` state and/or elements that needs to transition to a width or height value of `auto`. -- When transitioning "in" and going from `display:none` to a display state where the element is part of the document flow (e.g. `display:block`), [Reflow](https://developer.mozilla.org/en-US/docs/Glossary/Reflow) much occur. If not, the element is displayed instanlty in the DOM, in its final state (as if all transitions have been instantly played to completion). +Issues tackled: + +- When transitioning "in" and going from `display:none` to a display state where the element is part of the document flow (e.g. `display:block`), [Reflow](https://developer.mozilla.org/en-US/docs/Glossary/Reflow) must occur. If not, the element is displayed instanlty in the DOM, in its final state (as if all transitions have been instantly played to completion). - When transitioning "out" and going to `display:none`, the `display:none` CSS rule must be applied after all transitions effects are played to completion. If not, the element will disappear instantly, before any transition effects are rendered. +- When transitioning "in" and going to a width or height value of `auto`, the element will instantly assume its final size and not transition smoothly to it. Reflow must occur, but `auto` also can't be used here and the width/height of the element needs to be computed as an actual value first. + ## Philosophy As much as possible this library tries to work with and respect the rules defined via CSS classes applied to DOM elements. diff --git a/src/DisplayToggleFx.js b/src/DisplayToggleFx.js index ec29dc7..a0eda4b 100644 --- a/src/DisplayToggleFx.js +++ b/src/DisplayToggleFx.js @@ -2,7 +2,7 @@ * DisplayToggleFx helps with applying/unapplying CSS classes that trigger CSS transitions, * where the DOM element needs to change from a display:none state → displayed state, and vice-versa * - * v1.2 also adds support for cases where an element's width or height changes to 'auto' + * v4.1 adds support for cases where an element's width or height changes to 'auto' */ const DisplayToggleFx = {