jenkins-bot has submitted this change and it was merged.
Change subject: mediawiki.notification: Use CSS tranforms for in/out animations
......................................................................
mediawiki.notification: Use CSS tranforms for in/out animations
Remove the whole placeholder system. This was needed to preserve
parking space while visually animating the element elsewhere.
CSS transforms have this behaviour naturally.
Change-Id: I01473a199665b9fd86ed50b7d117532ac94f3edf
---
M resources/src/mediawiki/mediawiki.notification.css
M resources/src/mediawiki/mediawiki.notification.js
2 files changed, 66 insertions(+), 163 deletions(-)
Approvals:
Ori.livneh: Looks good to me, approved
jenkins-bot: Verified
diff --git a/resources/src/mediawiki/mediawiki.notification.css
b/resources/src/mediawiki/mediawiki.notification.css
index 632ae82..82ccd4f 100644
--- a/resources/src/mediawiki/mediawiki.notification.css
+++ b/resources/src/mediawiki/mediawiki.notification.css
@@ -12,9 +12,27 @@
margin-bottom: 0.5em;
border: solid 1px #ddd;
background-color: #fcfcfc;
- /* Message hides on-click */
- /* See also mediawiki.notification.js */
+ /* Click handler in mediawiki.notification.js */
cursor: pointer;
+
+ opacity: 0;
+ -webkit-transform: translateX(35px);
+ transform: translateX(35px);
+ -webkit-transition: opacity 0.35s ease-in-out, -webkit-transform 0.35s
ease-in-out;
+ transition: opacity 0.35s ease-in-out, transform 0.35s ease-in-out;
+}
+
+.mw-notification-visible {
+ opacity: 1;
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+}
+
+.mw-notification-replaced {
+ opacity: 0;
+ -webkit-transform: translateY(-35px);
+ transform: translateY(-35px);
+ pointer-events: none;
}
.mw-notification-title {
diff --git a/resources/src/mediawiki/mediawiki.notification.js
b/resources/src/mediawiki/mediawiki.notification.js
index eeb7bb3..3f7b8fd 100644
--- a/resources/src/mediawiki/mediawiki.notification.js
+++ b/resources/src/mediawiki/mediawiki.notification.js
@@ -7,7 +7,8 @@
// Number of open notification boxes at any time
openNotificationCount = 0,
isPageReady = false,
- preReadyNotifQueue = [];
+ preReadyNotifQueue = [],
+ rAF = window.requestAnimationFrame || setTimeout;
/**
* A Notification object for 1 message.
@@ -92,17 +93,7 @@
* @private
*/
Notification.prototype.start = function () {
- var
- // Local references
- $notification, options,
- // Original opacity so that we can animate back to it
later
- opacity,
- // Other notification elements matching the same tag
- $tagMatches,
- outerHeight,
- placeholderHeight,
- autohideCount,
- notif;
+ var options, $notification, $tagMatches, autohideCount;
$area.show();
@@ -116,109 +107,43 @@
options = this.options;
$notification = this.$notification;
- opacity = this.$notification.css( 'opacity' );
-
- // Set the opacity to 0 so we can fade in later.
- $notification.css( 'opacity', 0 );
-
if ( options.tag ) {
- // Check to see if there are any tagged notifications
with the same tag as the new one
+ // Find notifications with the same tag
$tagMatches = $area.find( '.mw-notification-tag-' +
options.tag );
}
- // If we found a tagged notification use the replacement
pattern instead of the new
- // notification fade-in pattern.
+ // If we found existing notification with the same tag, replace
them
if ( options.tag && $tagMatches.length ) {
- // Iterate over the tag matches to find the outerHeight
we should use
- // for the placeholder.
- outerHeight = 0;
+ // While there can be only one "open" notif with a
given tag, there can be several
+ // matches here because they remain in the DOM until
the animation is finished.
$tagMatches.each( function () {
var notif = $( this ).data( 'mw.notification' );
- if ( notif ) {
- // Use the notification's height +
padding + border + margins
- // as the placeholder height.
- outerHeight =
notif.$notification.outerHeight( true );
- if ( notif.$replacementPlaceholder ) {
- // Grab the height of a
placeholder that has not finished animating.
- placeholderHeight =
notif.$replacementPlaceholder.height();
- // Remove any placeholders
added by a previous tagged
- // notification that was in the
middle of replacing another.
- // This also makes sure that we
only grab the placeholderHeight
- // for the most recent
notification.
-
notif.$replacementPlaceholder.remove();
- delete
notif.$replacementPlaceholder;
- }
- // Close the previous tagged
notification
- // Since we're replacing it do this
with a fast speed and don't output a placeholder
- // since we're taking care of that
transition ourselves.
- notif.close( { speed: 'fast',
placeholder: false } );
+ if ( notif && notif.isOpen ) {
+ // Detach from render flow with
position absolute so that the new tag can
+ // occupy its space instead.
+ notif.$notification
+ .css( {
+ position: 'absolute',
+ width:
notif.$notification.width()
+ } )
+ .css(
notif.$notification.position() )
+ .addClass(
'mw-notification-replaced' );
+ notif.close();
}
} );
- if ( placeholderHeight !== undefined ) {
- // If the other tagged notification was in the
middle of replacing another
- // tagged notification, continue from the
placeholder's height instead of
- // using the outerHeight of the notification.
- outerHeight = placeholderHeight;
- }
$notification
- // Insert the new notification before the
tagged notification(s)
.insertBefore( $tagMatches.first() )
- .css( {
- // Use an absolute position so that we
can use a placeholder to gracefully push other notifications
- // into the right spot.
- position: 'absolute',
- width: $notification.width()
- } )
- // Fade-in the notification
- .animate( { opacity: opacity },
- {
- duration: 'slow',
- complete: function () {
- // After we've faded in
clear the opacity and let css take over
- $( this ).css( {
opacity: '' } );
- }
- } );
-
- notif = this;
-
- // Create a clear placeholder we can use to make the
notifications around the notification that is being
- // replaced expand or contract gracefully to fit the
height of the new notification.
- notif.$replacementPlaceholder = $( '<div>' )
- // Set the height to the space the previous
notification or placeholder took
- .css( 'height', outerHeight )
- // Make sure that this placeholder is at the
very end of this tagged notification group
- .insertAfter( $tagMatches.eq( -1 ) )
- // Animate the placeholder height to the space
that this new notification will take up
- .animate( { height: $notification.outerHeight(
true ) },
- {
- // Do space animations fast
- speed: 'fast',
- complete: function () {
- // Reset the
notification position after we've finished the space animation
- // However do not do it
if the placeholder was removed because another tagged
- // notification went
and closed this one.
- if (
notif.$replacementPlaceholder ) {
-
$notification.css( 'position', '' );
- }
- // Finally, remove the
placeholder from the DOM
- $( this ).remove();
- }
- } );
+ .addClass( 'mw-notification-visible' );
} else {
- // Append to the notification area and fade in to the
original opacity.
- $notification
- .appendTo( $area )
- .animate( { opacity: opacity },
- {
- duration: 'fast',
- complete: function () {
- // After we've faded in
clear the opacity and let css take over
- $( this ).css(
'opacity', '' );
- }
- }
- );
+ $area.append( $notification );
+ rAF( function () {
+ // This frame renders the element in the area
(invisible)
+ rAF( function () {
+ $notification.addClass(
'mw-notification-visible' );
+ } );
+ } );
}
// By default a notification is paused.
@@ -267,26 +192,20 @@
};
/**
- * Close/hide the notification.
- *
- * @param {Object} options An object containing options for the closing
of the notification.
- *
- * - speed: Use a close speed different than the default 'slow'.
- * - placeholder: Set to false to disable the placeholder transition.
+ * Close the notification.
*/
- Notification.prototype.close = function ( options ) {
+ Notification.prototype.close = function () {
+ var notif = this;
+
if ( !this.isOpen ) {
return;
}
+
this.isOpen = false;
openNotificationCount--;
+
// Clear any remaining timeout on close
this.pause();
-
- options = $.extend( {
- speed: 'slow',
- placeholder: true
- }, options );
// Remove the mw-notification-autohide class from the
notification to avoid
// having a half-closed notification counted as a notification
to resume
@@ -297,56 +216,22 @@
// notification that has now become one of the first
{autoHideLimit} notifications.
notification.resume();
- this.$notification
- .css( {
- // Don't trigger any mouse events while fading
out, just in case the cursor
- // happens to be right above us when we
transition upwards.
- pointerEvents: 'none',
- // Set an absolute position so we can move
upwards in the animation.
- // Notification replacement doesn't look right
unless we use an animation like this.
- position: 'absolute',
- // We must fix the width to avoid it shrinking
horizontally.
- width: this.$notification.width()
- } )
- // Fix the top/left position to the current computed
position from which we
- // can animate upwards.
- .css( this.$notification.position() );
+ rAF( function () {
+ notif.$notification.removeClass(
'mw-notification-visible' );
- // This needs to be done *after* notification's position has
been made absolute.
- if ( options.placeholder ) {
- // Insert a placeholder with a height equal to the
height of the
- // notification plus it's vertical margins in place of
the notification
- var $placeholder = $( '<div>' )
- .css( 'height', this.$notification.outerHeight(
true ) )
- .insertBefore( this.$notification );
- }
-
- // Animate opacity and top to create fade upwards animation for
notification closing
- this.$notification
- .animate( {
- opacity: 0,
- top: '-=35'
- }, {
- duration: options.speed,
- complete: function () {
- // Remove the notification
- $( this ).remove();
- // Hide the area manually after closing
the last notification, since it has padding,
- // causing it to obscure whatever is
behind it in spite of being invisible (bug 52659).
- // It's okay to do this before getting
rid of the placeholder, as it's invisible as well.
- if ( openNotificationCount === 0 ) {
- $area.hide();
- }
- if ( options.placeholder ) {
- // Use a fast slide up
animation after closing to make it look like the notifications
- // below slide up into place
when the notification disappears
- $placeholder.slideUp( 'fast',
function () {
- // Remove the
placeholder
- $( this ).remove();
- } );
- }
+ setTimeout( function () {
+ if ( openNotificationCount === 0 ) {
+ // Hide the area after the last
notification closes. Otherwise, the padding on
+ // the area can be obscure content,
despite the area being empty/invisible (T54659). // FIXME
+ $area.hide();
+ notif.$notification.remove();
+ } else {
+ notif.$notification.slideUp( 'fast',
function () {
+ $( this ).remove();
+ } );
}
- } );
+ }, 500 );
+ } );
};
/**
--
To view, visit https://gerrit.wikimedia.org/r/241262
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I01473a199665b9fd86ed50b7d117532ac94f3edf
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Krinkle <[email protected]>
Gerrit-Reviewer: Bartosz DziewoĆski <[email protected]>
Gerrit-Reviewer: Daniel Friesen <[email protected]>
Gerrit-Reviewer: Edokter <[email protected]>
Gerrit-Reviewer: Gilles <[email protected]>
Gerrit-Reviewer: Jack Phoenix <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits