mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-11-24 08:13:38 +00:00
Make the panel animation more subtle
Also turns it into a CSS animation, which allows us to clean a lot of code Change-Id: Id6705b63b26d8be341e77352924cad6ac4b73da1
This commit is contained in:
parent
d2f7e785ea
commit
c6683b7e3f
|
@ -26,14 +26,6 @@
|
|||
* @constructor
|
||||
*/
|
||||
function MultimediaViewer() {
|
||||
/**
|
||||
* Whether we've fired an animation for the metadata div.
|
||||
* @property {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.hasAnimatedMetadata = window.localStorage !== undefined &&
|
||||
localStorage.getItem( 'mmv.hasOpenedMetadata' );
|
||||
|
||||
/**
|
||||
* @property {mw.mmv.provider.Image}
|
||||
* @private
|
||||
|
@ -295,10 +287,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
viewer.stopListeningToScroll();
|
||||
viewer.animateMetadataDivOnce()
|
||||
// We need to wait until the animation is finished before we listen to scroll
|
||||
.then( function() { viewer.startListeningToScroll(); } );
|
||||
viewer.ui.panel.animateMetadataOnce();
|
||||
} );
|
||||
|
||||
this.comingFromHashChange = false;
|
||||
|
@ -502,55 +491,6 @@
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Animates the metadata area when the viewer is first opened.
|
||||
* @return {jQuery.Promise} an empty promise which resolves when the animation is finished
|
||||
*/
|
||||
MMVP.animateMetadataDivOnce = function () {
|
||||
if ( !this.hasAnimatedMetadata ) {
|
||||
this.hasAnimatedMetadata = true;
|
||||
$.scrollTo( 20, 300 )
|
||||
.scrollTo( 0, 300 );
|
||||
}
|
||||
return $.scrollTo.window().promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop listening to the page's scroll events
|
||||
*/
|
||||
MMVP.stopListeningToScroll = function () {
|
||||
$.scrollTo().off( 'scroll.mmvp' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Start listening to the page's scroll events
|
||||
* Will call MMVP.scroll(), throttled so it is not triggered on every pixel.
|
||||
*/
|
||||
MMVP.startListeningToScroll = function () {
|
||||
var viewer = this;
|
||||
|
||||
$.scrollTo().on( 'scroll.mmvp', $.throttle( 250, function() { viewer.scroll(); } ) );
|
||||
|
||||
// Trigger a check in case the user scrolled manually during the animation
|
||||
viewer.scroll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receives the window's scroll events and flips the chevron if necessary.
|
||||
*/
|
||||
MMVP.scroll = function () {
|
||||
var scrolled = !!$.scrollTo().scrollTop();
|
||||
|
||||
this.ui.panel.$dragIcon.toggleClass( 'pointing-down', scrolled );
|
||||
|
||||
if ( !this.savedHasOpenedMetadata &&
|
||||
scrolled &&
|
||||
window.localStorage !== undefined ) {
|
||||
localStorage.setItem( 'mmv.hasOpenedMetadata', true );
|
||||
this.savedHasOpenedMetadata = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads all the size-independent information needed by the lightbox (image metadata, repo
|
||||
* information, file usage, uploader data).
|
||||
|
@ -695,7 +635,6 @@
|
|||
*/
|
||||
MMVP.cleanupEventHandlers = function () {
|
||||
$( document ).off( 'mmv-close.mmvp mmv-next.mmvp mmv-prev.mmvp mmv-resize.mmvp' );
|
||||
this.stopListeningToScroll();
|
||||
};
|
||||
|
||||
mw.mmv.MultimediaViewer = MultimediaViewer;
|
||||
|
|
|
@ -83,6 +83,50 @@
|
|||
background-color: @metadata-background;
|
||||
position: absolute;
|
||||
min-height: (@bottom-height + 1);
|
||||
opacity: 0;
|
||||
|
||||
&.invite {
|
||||
-webkit-animation-name: invite-animation;
|
||||
-webkit-animation-duration: 0.5s;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-name: invite-animation;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.mlb-post-image.invited {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@-webkit-keyframes invite-animation {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
margin-top: 5px;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
margin-top: -3px;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes invite-animation {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
margin-top: 5px;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
margin-top: -3px;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mlb-controls {
|
||||
|
|
|
@ -31,6 +31,14 @@
|
|||
mw.mmv.ui.Element.call( this, $container );
|
||||
this.$controlBar = $controlBar;
|
||||
|
||||
/**
|
||||
* Whether we've fired an animation for the metadata div.
|
||||
* @property {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.hasAnimatedMetadata = window.localStorage !== undefined &&
|
||||
localStorage.getItem( 'mmv.hasOpenedMetadata' );
|
||||
|
||||
this.initializeHeader();
|
||||
this.initializeImageMetadata();
|
||||
this.initializeAboutLinks();
|
||||
|
@ -45,10 +53,16 @@
|
|||
this.handleEvent( 'keydown', function ( e ) {
|
||||
panel.keydown( e );
|
||||
} );
|
||||
|
||||
$.scrollTo().on( 'scroll.mmvp', $.throttle( 250, function() {
|
||||
panel.scroll();
|
||||
} ) );
|
||||
};
|
||||
|
||||
MPP.unattach = function() {
|
||||
this.clearEvents();
|
||||
|
||||
$.scrollTo().off( 'scroll.mmvp' );
|
||||
};
|
||||
|
||||
MPP.empty = function () {
|
||||
|
@ -79,6 +93,9 @@
|
|||
|
||||
this.$progress.addClass( 'empty' );
|
||||
|
||||
// need to remove this to avoid animating again when reopening lightbox on same page
|
||||
this.$container.removeClass( 'invite' );
|
||||
|
||||
this.fileReuse.empty();
|
||||
};
|
||||
|
||||
|
@ -675,6 +692,18 @@
|
|||
return date.format( 'LL' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Animates the metadata area when the viewer is first opened.
|
||||
*/
|
||||
MPP.animateMetadataOnce = function () {
|
||||
if ( !this.hasAnimatedMetadata ) {
|
||||
this.hasAnimatedMetadata = true;
|
||||
this.$container.addClass( 'invite' );
|
||||
} else {
|
||||
this.$container.addClass( 'invited' );
|
||||
}
|
||||
};
|
||||
|
||||
// ********************************
|
||||
// ******** Action methods ********
|
||||
// ********************************
|
||||
|
@ -766,5 +795,23 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Receives the window's scroll events and flips the chevron if necessary.
|
||||
*/
|
||||
MPP.scroll = function () {
|
||||
var scrolled = !!$.scrollTo().scrollTop();
|
||||
|
||||
this.$dragIcon.toggleClass( 'pointing-down', scrolled );
|
||||
|
||||
if (
|
||||
!this.savedHasOpenedMetadata &&
|
||||
scrolled &&
|
||||
window.localStorage !== undefined
|
||||
) {
|
||||
localStorage.setItem( 'mmv.hasOpenedMetadata', true );
|
||||
this.savedHasOpenedMetadata = true;
|
||||
}
|
||||
};
|
||||
|
||||
mw.mmv.ui.MetadataPanel = MetadataPanel;
|
||||
}( mediaWiki, jQuery, OO, moment ) );
|
||||
|
|
|
@ -218,8 +218,7 @@
|
|||
} );
|
||||
|
||||
QUnit.test( 'Metadata scrolling', 15, function ( assert ) {
|
||||
var lightbox = new mw.mmv.LightboxInterface(),
|
||||
viewer = new mw.mmv.MultimediaViewer(),
|
||||
var ui = new mw.mmv.LightboxInterface(),
|
||||
keydown = $.Event( 'keydown' ),
|
||||
$document = $( document ),
|
||||
scrollTopBeforeOpeningLightbox,
|
||||
|
@ -227,9 +226,6 @@
|
|||
memorizedScrollToScroll = 0,
|
||||
originalJQueryScrollTo = $.scrollTo;
|
||||
|
||||
// Pretend that we have things hooked up
|
||||
viewer.ui = lightbox;
|
||||
|
||||
// We need to set up a proxy on the jQuery scrollTop function
|
||||
// that will let us pretend that the document really scrolled
|
||||
// and that will return values as if the scroll happened
|
||||
|
@ -259,7 +255,7 @@
|
|||
|
||||
if ( scrollTo !== undefined ) {
|
||||
// Trigger event manually
|
||||
viewer.scroll();
|
||||
ui.panel.scroll();
|
||||
}
|
||||
|
||||
return $element;
|
||||
|
@ -267,17 +263,14 @@
|
|||
|
||||
// First phase of the test: up and down arrows
|
||||
|
||||
viewer.hasAnimatedMetadata = false;
|
||||
ui.panel.hasAnimatedMetadata = false;
|
||||
localStorage.removeItem( 'mmv.hasOpenedMetadata' );
|
||||
|
||||
// Attach lightbox to testing fixture to avoid interference with other tests.
|
||||
lightbox.attach( '#qunit-fixture' );
|
||||
|
||||
// Pretend that we have things hooked up
|
||||
viewer.currentIndex = 0;
|
||||
ui.attach( '#qunit-fixture' );
|
||||
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), 0, 'scrollTo scrollTop should be set to 0' );
|
||||
assert.ok( !lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
assert.ok( !ui.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing up' );
|
||||
|
||||
assert.ok( !localStorage.getItem( 'mmv.hasOpenedMetadata' ),
|
||||
|
@ -287,9 +280,9 @@
|
|||
$document.trigger( keydown );
|
||||
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ),
|
||||
lightbox.panel.$imageMetadata.outerHeight(),
|
||||
ui.panel.$imageMetadata.outerHeight(),
|
||||
'scrollTo scrollTop should be set to the metadata height after pressing up arrow' );
|
||||
assert.ok( lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
assert.ok( ui.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing down after pressing up arrow' );
|
||||
assert.ok( localStorage.getItem( 'mmv.hasOpenedMetadata' ),
|
||||
'localStorage knows that the metadata has been open' );
|
||||
|
@ -299,26 +292,26 @@
|
|||
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), 0,
|
||||
'scrollTo scrollTop should be set to 0 after pressing down arrow' );
|
||||
assert.ok( !lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
assert.ok( !ui.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing up after pressing down arrow' );
|
||||
|
||||
lightbox.panel.$dragIcon.click();
|
||||
ui.panel.$dragIcon.click();
|
||||
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ),
|
||||
lightbox.panel.$imageMetadata.outerHeight(),
|
||||
ui.panel.$imageMetadata.outerHeight(),
|
||||
'scrollTo scrollTop should be set to the metadata height after clicking the chevron once' );
|
||||
assert.ok( lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
assert.ok( ui.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing down after clicking the chevron once' );
|
||||
|
||||
lightbox.panel.$dragIcon.click();
|
||||
ui.panel.$dragIcon.click();
|
||||
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), 0,
|
||||
'scrollTo scrollTop should be set to 0 after clicking the chevron twice' );
|
||||
assert.ok( !lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
assert.ok( !ui.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing up after clicking the chevron twice' );
|
||||
|
||||
// Unattach lightbox from document
|
||||
lightbox.unattach();
|
||||
ui.unattach();
|
||||
|
||||
|
||||
// Second phase of the test: scroll memory
|
||||
|
@ -329,7 +322,7 @@
|
|||
scrollTopBeforeOpeningLightbox = $.scrollTo().scrollTop();
|
||||
|
||||
// Attach lightbox to testing fixture to avoid interference with other tests.
|
||||
lightbox.attach( '#qunit-fixture' );
|
||||
ui.attach( '#qunit-fixture' );
|
||||
|
||||
// To make sure that the details are out of view, the lightbox is supposed to scroll to the top when open
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), 0, 'Page scrollTop should be set to 0' );
|
||||
|
@ -338,13 +331,13 @@
|
|||
$.scrollTo( 20, 0 );
|
||||
|
||||
// This extra attach() call simulates the effect of prev/next seen in bug 59861
|
||||
lightbox.attach( '#qunit-fixture' );
|
||||
ui.attach( '#qunit-fixture' );
|
||||
|
||||
// The lightbox was already open at this point, the scrollTop should be left untouched
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), 20, 'Page scrollTop should be set to 20' );
|
||||
|
||||
// Unattach lightbox from document
|
||||
lightbox.unattach();
|
||||
ui.unattach();
|
||||
|
||||
// Lightbox is supposed to restore the document scrollTop value that was set prior to opening it
|
||||
assert.strictEqual( $.scrollTo().scrollTop(), scrollTopBeforeOpeningLightbox, 'document scrollTop value has been restored correctly' );
|
||||
|
|
|
@ -1,30 +1,6 @@
|
|||
( function ( mw, $ ) {
|
||||
QUnit.module( 'mmv', QUnit.newMwEnvironment() );
|
||||
|
||||
QUnit.test( 'Metadata div is only animated once', 4, function ( assert ) {
|
||||
localStorage.removeItem( 'mmv.hasOpenedMetadata' );
|
||||
|
||||
var viewer = new mw.mmv.MultimediaViewer(),
|
||||
backupAnimation = $.fn.animate,
|
||||
animationRan = false;
|
||||
|
||||
$.fn.animate = function () {
|
||||
animationRan = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
viewer.animateMetadataDivOnce();
|
||||
assert.strictEqual( viewer.hasAnimatedMetadata, true, 'The first call to animateMetadataDivOnce set hasAnimatedMetadata to true' );
|
||||
assert.strictEqual( animationRan, true, 'The first call to animateMetadataDivOnce led to an animation' );
|
||||
|
||||
animationRan = false;
|
||||
viewer.animateMetadataDivOnce();
|
||||
assert.strictEqual( viewer.hasAnimatedMetadata, true, 'The second call to animateMetadataDivOnce did not change the value of hasAnimatedMetadata' );
|
||||
assert.strictEqual( animationRan, false, 'The second call to animateMetadataDivOnce did not lead to an animation' );
|
||||
|
||||
$.fn.animate = backupAnimation;
|
||||
} );
|
||||
|
||||
QUnit.test( 'eachPrealoadableLightboxIndex()', 11, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer(),
|
||||
expectedIndices,
|
||||
|
@ -139,7 +115,9 @@
|
|||
setupForLoad : $.noop,
|
||||
canvas : { set : $.noop,
|
||||
getCurrentImageWidths : function () { return { real : 0 }; } },
|
||||
panel : { setImageInfo : $.noop,
|
||||
panel : {
|
||||
setImageInfo : $.noop,
|
||||
animateMetadataOnce : $.noop,
|
||||
percent : function ( percent ) {
|
||||
if ( i === 0 ) {
|
||||
assert.strictEqual( percent, 0,
|
||||
|
@ -265,7 +243,8 @@
|
|||
assert.ok( false, 'Progress of the first image should not be shown' );
|
||||
}
|
||||
},
|
||||
empty: $.noop
|
||||
empty: $.noop,
|
||||
animateMetadataOnce: $.noop
|
||||
},
|
||||
open : $.noop,
|
||||
empty: $.noop };
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
QUnit.module( 'mmv.ui.metadataPanel', QUnit.newMwEnvironment() );
|
||||
|
||||
QUnit.test( 'The panel is emptied properly when necessary', thingsShouldBeEmptied.length + thingsShouldHaveEmptyClass.length + 1, function ( assert ) {
|
||||
QUnit.test( 'The panel is emptied properly when necessary', thingsShouldBeEmptied.length + thingsShouldHaveEmptyClass.length + 2, function ( assert ) {
|
||||
var i,
|
||||
$qf = $( '#qunit-fixture' ),
|
||||
panel = new mw.mmv.ui.MetadataPanel( $qf, $( '<div>' ).appendTo( $qf ) );
|
||||
|
@ -36,7 +36,8 @@
|
|||
assert.strictEqual( panel[thingsShouldHaveEmptyClass[i]].hasClass( 'empty' ), true, 'We successfully applied the empty class to the ' + thingsShouldHaveEmptyClass[i] + ' element' );
|
||||
}
|
||||
|
||||
assert.strictEqual( panel.$dragIcon.hasClass( 'pointing-down' ), false, 'We successfully reset the chevron' );
|
||||
assert.ok( !panel.$dragIcon.hasClass( 'pointing-down' ), 'We successfully reset the chevron' );
|
||||
assert.ok( !panel.$container.hasClass( 'invite' ), 'We successfully reset the invite' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Setting repository information in the UI works as expected', 3, function ( assert ) {
|
||||
|
@ -240,4 +241,32 @@
|
|||
|
||||
$.fn.animate = oldAnimate;
|
||||
} );
|
||||
|
||||
QUnit.test( 'Metadata div is only animated once', 6, function ( assert ) {
|
||||
localStorage.removeItem( 'mmv.hasOpenedMetadata' );
|
||||
|
||||
var $qf = $( '#qunit-fixture' ),
|
||||
panel = new mw.mmv.ui.MetadataPanel( $qf, $( '<div>' ).appendTo( $qf ) );
|
||||
|
||||
panel.animateMetadataOnce();
|
||||
|
||||
assert.ok( panel.hasAnimatedMetadata,
|
||||
'The first call to animateMetadataOnce set hasAnimatedMetadata to true' );
|
||||
assert.ok( !$qf.hasClass( 'invited' ),
|
||||
'After the first call to animateMetadataOnce led to an animation' );
|
||||
assert.ok( $qf.hasClass( 'invite' ),
|
||||
'The first call to animateMetadataOnce led to an animation' );
|
||||
|
||||
$qf.removeClass( 'invite' );
|
||||
|
||||
panel.animateMetadataOnce();
|
||||
|
||||
assert.strictEqual( panel.hasAnimatedMetadata, true, 'The second call to animateMetadataOnce did not change the value of hasAnimatedMetadata' );
|
||||
assert.ok( $qf.hasClass( 'invited' ),
|
||||
'After the second call to animateMetadataOnce the div is shown right away' );
|
||||
assert.ok( !$qf.hasClass( 'invite' ),
|
||||
'The second call to animateMetadataOnce did not lead to an animation' );
|
||||
|
||||
$qf.removeClass( 'invited' );
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
Loading…
Reference in a new issue