diff --git a/README.md b/README.md index b7bbafc1..9694d522 100644 --- a/README.md +++ b/README.md @@ -624,12 +624,13 @@ All CSS color formats are supported, like rgba() or hsl(). #### Image Backgrounds By default, background images are resized to cover the full page. Available options: -| Attribute | Default | Description | -| :--------------------------- | :--------- | :---------- | -| data-background-image | | URL of the image to show. GIFs restart when the slide opens. | -| data-background-size | cover | See [background-size](https://developer.mozilla.org/docs/Web/CSS/background-size) on MDN. | -| data-background-position | center | See [background-position](https://developer.mozilla.org/docs/Web/CSS/background-position) on MDN. | -| data-background-repeat | no-repeat | See [background-repeat](https://developer.mozilla.org/docs/Web/CSS/background-repeat) on MDN. | +| Attribute | Default | Description | +| :------------------------------- | :--------- | :---------- | +| data-background-image | | URL of the image to show. GIFs restart when the slide opens. | +| data-background-size | cover | See [background-size](https://developer.mozilla.org/docs/Web/CSS/background-size) on MDN. | +| data-background-position | center | See [background-position](https://developer.mozilla.org/docs/Web/CSS/background-position) on MDN. | +| data-background-repeat | no-repeat | See [background-repeat](https://developer.mozilla.org/docs/Web/CSS/background-repeat) on MDN. | +| data-background-content-opacity | 1 | Opacity of the background image on a 0-1 scale. 0 is transparent and 1 is fully opaque. | ```html

Image

@@ -642,12 +643,13 @@ By default, background images are resized to cover the full page. Available opti #### Video Backgrounds Automatically plays a full size video behind the slide. -| Attribute | Default | Description | -| :--------------------------- | :------ | :---------- | -| data-background-video | | A single video source, or a comma separated list of video sources. | -| data-background-video-loop | false | Flags if the video should play repeatedly. | -| data-background-video-muted | false | Flags if the audio should be muted. | -| data-background-size | cover | Use `cover` for full screen and some cropping or `contain` for letterboxing. | +| Attribute | Default | Description | +| :--------------------------- | :------ | :---------- | +| data-background-video | | A single video source, or a comma separated list of video sources. | +| data-background-video-loop | false | Flags if the video should play repeatedly. | +| data-background-video-muted | false | Flags if the audio should be muted. | +| data-background-size | cover | Use `cover` for full screen and some cropping or `contain` for letterboxing. | +| data-background-content-opacity | 1 | Opacity of the background video on a 0-1 scale. 0 is transparent and 1 is fully opaque. | ```html
diff --git a/css/reveal.css b/css/reveal.css index 05c2e8d9..ac095f47 100644 --- a/css/reveal.css +++ b/css/reveal.css @@ -1015,10 +1015,15 @@ body { visibility: hidden; overflow: hidden; background-color: transparent; + transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; background-position: 50% 50%; background-repeat: no-repeat; - background-size: cover; - transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + background-size: cover; } .reveal .slide-background.stack { display: block; } diff --git a/css/reveal.scss b/css/reveal.scss index 065a0a1a..efb4114d 100644 --- a/css/reveal.scss +++ b/css/reveal.scss @@ -1091,11 +1091,18 @@ $controlsArrowAngleActive: 36deg; overflow: hidden; background-color: rgba( 0, 0, 0, 0 ); + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + background-position: 50% 50%; background-repeat: no-repeat; background-size: cover; - - transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } .reveal .slide-background.stack { diff --git a/js/reveal.js b/js/reveal.js index ebdeb9f6..ae1c4ae5 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -933,14 +933,18 @@ backgroundColor: slide.getAttribute( 'data-background-color' ), backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), backgroundPosition: slide.getAttribute( 'data-background-position' ), - backgroundTransition: slide.getAttribute( 'data-background-transition' ) + backgroundTransition: slide.getAttribute( 'data-background-transition' ), + backgroundContentOpacity: slide.getAttribute( 'data-background-content-opacity' ) }; + // Main slide background element var element = document.createElement( 'div' ); - - // Carry over custom classes from the slide to the background element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + // Inner background element that wraps images/videos/iframes + var contentElement = document.createElement( 'div' ); + contentElement.className = 'slide-background-content'; + if( data.background ) { // Auto-wrap image urls in url(...) if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) { @@ -963,17 +967,22 @@ data.backgroundColor + data.backgroundRepeat + data.backgroundPosition + - data.backgroundTransition ); + data.backgroundTransition + + data.backgroundContentOpacity ); } // Additional and optional background properties - if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize; if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; - if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat; - if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition; if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); + // Background image options are set on the content wrapper + if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; + if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundContentOpacity ) contentElement.style.opacity = data.backgroundContentOpacity; + + element.appendChild( contentElement ); container.appendChild( element ); // If backgrounds are being recreated, clear old classes @@ -981,6 +990,7 @@ slide.classList.remove( 'has-light-background' ); slide.slideBackgroundElement = element; + slide.slideBackgroundContentElement = contentElement; // If this slide has a background color, add a class that // signals if it is light or dark. If the slide has no background @@ -3311,10 +3321,12 @@ // Show the corresponding background element - var background = getSlideBackground( slide ); + var background = slide.slideBackgroundElement; if( background ) { background.style.display = 'block'; + var backgroundContent = slide.slideBackgroundContentElement; + // If the background contains media, load it if( background.hasAttribute( 'data-loaded' ) === false ) { background.setAttribute( 'data-loaded', 'true' ); @@ -3327,7 +3339,7 @@ // Images if( backgroundImage ) { - background.style.backgroundImage = 'url('+ encodeURI( backgroundImage ) +')'; + backgroundContent.style.backgroundImage = 'url('+ encodeURI( backgroundImage ) +')'; } // Videos else if ( backgroundVideo && !isSpeakerNotes() ) { @@ -3355,7 +3367,7 @@ video.innerHTML += ''; } ); - background.appendChild( video ); + backgroundContent.appendChild( video ); } // Iframes else if( backgroundIframe && options.excludeIframes !== true ) { @@ -3378,7 +3390,7 @@ iframe.style.maxHeight = '100%'; iframe.style.maxWidth = '100%'; - background.appendChild( iframe ); + backgroundContent.appendChild( iframe ); } } diff --git a/test/test.js b/test/test.js index 042e4e81..f8515a0c 100644 --- a/test/test.js +++ b/test/test.js @@ -130,8 +130,8 @@ Reveal.addEventListener( 'ready', function() { QUnit.test( 'Reveal.getSlideBackground', function( assert ) { assert.equal( Reveal.getSlideBackground( 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:first-child' ), 'gets correct first background' ); assert.equal( Reveal.getSlideBackground( 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2)' ), 'no v index returns stack' ); - assert.equal( Reveal.getSlideBackground( 1, 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(1)' ), 'v index 0 returns first vertical child' ); - assert.equal( Reveal.getSlideBackground( 1, 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(2)' ), 'v index 1 returns second vertical child' ); + assert.equal( Reveal.getSlideBackground( 1, 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(2)' ), 'v index 0 returns first vertical child' ); + assert.equal( Reveal.getSlideBackground( 1, 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(3)' ), 'v index 1 returns second vertical child' ); assert.strictEqual( Reveal.getSlideBackground( 100 ), undefined, 'undefined when out of horizontal bounds' ); assert.strictEqual( Reveal.getSlideBackground( 1, 100 ), undefined, 'undefined when out of vertical bounds' ); @@ -523,8 +523,8 @@ Reveal.addEventListener( 'ready', function() { var imageSource2 = Reveal.getSlide( 1, 0 ).getAttribute( 'data-background' ); // check that the images are applied to the background elements - assert.ok( Reveal.getSlideBackground( 0 ).style.backgroundImage.indexOf( imageSource1 ) !== -1, 'data-background-image worked' ); - assert.ok( Reveal.getSlideBackground( 1, 0 ).style.backgroundImage.indexOf( imageSource2 ) !== -1, 'data-background worked' ); + assert.ok( Reveal.getSlideBackground( 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource1 ) !== -1, 'data-background-image worked' ); + assert.ok( Reveal.getSlideBackground( 1, 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource2 ) !== -1, 'data-background worked' ); });