diff --git a/README.md b/README.md
index 5887015..d8f93a9 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,9 @@
- [API methods](#api-methods)
- [Videos](#videos)
- [HTML5 video player](#html5-video-player)
+ - [Video controls](#video-controls)
+ - [Video preloading](#video-preloading)
+ - [Fullscreen video](#fullscreen-video)
- [Multiple video sources](#multiple-video-sources)
- [YouTube](#youtube)
- [Vimeo](#vimeo)
@@ -44,7 +47,6 @@
- [Browser support](#browser-support)
- [License](#license)
- [Credits](#credits)
-- [Notable forks](#notable-forks)
## Description
@@ -165,8 +167,8 @@ the images in the Gallery lightbox on click of one of those links:
### Controls
-To initialize the Gallery with visible controls, add the CSS class
-`blueimp-gallery-controls` to the Gallery widget:
+To initialize the Gallery with visible controls (previous slide, next slide,
+etc.), add the CSS class `blueimp-gallery-controls` to the Gallery widget:
```html
```
+Please also note that by default, a click on an image slide or any Gallery
+widget element with the `toggle` class will toggle the display of the Gallery
+controls.
+
### Contain
To stretch smaller images to the dimensions of the Gallery container while
@@ -590,6 +596,12 @@ var videoFactoryOptions = {
videoLoadingClass: 'video-loading',
// The class for video when it is playing:
videoPlayingClass: 'video-playing',
+ // The class for video content displayed in an iframe:
+ videoIframeClass: 'video-iframe',
+ // The class for the video cover element:
+ videoCoverClass: 'video-cover',
+ // The class for the video play control:
+ videoPlayClass: 'video-play',
// Play videos inline by default:
videoPlaysInline: true,
// The list object property (or data attribute) for video preload:
@@ -828,10 +840,87 @@ if the browser supports the video content type.
For videos, the `poster` property defines the URL of the poster image to
display, before the video is started.
+#### Video controls
+
+To start video playback, you can either click on the video play icon or on the
+video slide itself.
+Starting the video playback enables the native HTML5 video
+[controls](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-controls).
+
+To toggle the Gallery controls (previous slide, next slide, etc.) instead of
+starting video playback on click of a video slide, add the `toggle` class to the
+video cover element using the `videoCoverClass` Gallery option:
+
+```js
+blueimp.Gallery(
+ [
+ {
+ title: 'Fruits',
+ type: 'video/mp4',
+ href: 'https://example.org/videos/fruits.mp4',
+ poster: 'https://example.org/images/fruits.jpg'
+ }
+ ],
+ {
+ videoCoverClass: 'video-cover toggle'
+ }
+)
+```
+
+#### Video preloading
+
+You can set the `preload` property of a Gallery video object to a valid value
+defined by the HTML5 video
+[preload](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-preload)
+attribute:
+
+- `none`: Indicates that the video should not be preloaded.
+- `metadata`: Indicates that only video metadata (e.g. length) is fetched.
+- `auto`: Indicates that the whole video file can be preloaded.
+
+```js
+blueimp.Gallery([
+ {
+ title: 'Fruits',
+ type: 'video/mp4',
+ href: 'https://example.org/videos/fruits.mp4',
+ preload: 'auto'
+ }
+])
+```
+
+#### Fullscreen video
+
+By default, videos are displayed with the HTML5 video
+[playsinline](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-playsinline)
+attribute set, which indicates that the video is to be played inline.
+To disable this behavior, you can set the Gallery option `videoPlaysInline` to
+`false`:
+
+```js
+blueimp.Gallery(
+ [
+ {
+ title: 'Fruits',
+ type: 'video/mp4',
+ href: 'https://example.org/videos/fruits.mp4',
+ poster: 'https://example.org/images/fruits.jpg'
+ }
+ ],
+ {
+ videoPlaysInline: false
+ }
+)
+```
+
+Please note that this attribute only has an effect on some browsers, e.g. Safari
+on iOS 10 and later.
+However, most browser provide video controls to switch to fullscreen mode.
+
#### Multiple video sources
To provide multiple video formats, the `sources` property of a list object can
-be set to an array of objects with `href` and `type` properties for each video
+be set to an array of objects with `type` and `src` properties for each video
source. The first video format in the list that the browser can play will be
displayed:
diff --git a/css/blueimp-gallery-video.css b/css/blueimp-gallery-video.css
index 8dc10e9..e8f4191 100644
--- a/css/blueimp-gallery-video.css
+++ b/css/blueimp-gallery-video.css
@@ -10,26 +10,25 @@
* https://opensource.org/licenses/MIT
*/
-.blueimp-gallery > .slides > .slide > .video-content > video {
+.blueimp-gallery > .slides > .slide > .video-content > video,
+.blueimp-gallery > .slides > .slide > .video-content > iframe,
+.blueimp-gallery > .slides > .slide > .video-content > .video-cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
- display: none;
-}
-.blueimp-gallery > .slides > .slide > .video-content > iframe {
- position: absolute;
- top: 100%;
- left: 0;
- width: 100%;
- height: 100%;
border: none;
}
-.blueimp-gallery > .slides > .slide > .video-playing > iframe {
- top: 0;
+.blueimp-gallery > .slides > .slide > .video-content > .video-cover {
+ background: center no-repeat;
+ background-size: contain;
+}
+.blueimp-gallery > .slides > .slide > .video-iframe > .video-cover {
+ background-color: #000;
+ background-color: rgba(0, 0, 0, 0.7);
}
-.blueimp-gallery > .slides > .slide > .video-content > a {
+.blueimp-gallery > .slides > .slide > .video-content > .video-play {
position: absolute;
top: 50%;
right: 0;
@@ -41,21 +40,18 @@
opacity: 0.8;
cursor: pointer;
}
-.blueimp-gallery-svgasimg > .slides > .slide > .video-content > a {
+.blueimp-gallery-svgasimg > .slides > .slide > .video-content > .video-play {
background-image: url(../img/video-play.svg);
}
-.blueimp-gallery > .slides > .slide > .video-playing > a,
-.blueimp-gallery > .slides > .slide > .video-playing > img {
+.blueimp-gallery > .slides > .slide > .video-playing > .video-play,
+.blueimp-gallery > .slides > .slide > .video-playing > .video-cover {
display: none;
}
-.blueimp-gallery > .slides > .slide > .video-playing > video {
- display: block;
-}
-.blueimp-gallery > .slides > .slide > .video-loading > a {
+.blueimp-gallery > .slides > .slide > .video-loading > .video-play {
background: url(../img/loading.gif) center no-repeat;
background-size: 64px 64px;
}
-.blueimp-gallery-smil > .slides > .slide > .video-loading > a {
+.blueimp-gallery-smil > .slides > .slide > .video-loading > .video-play {
background-image: url(../img/loading.svg);
}
@@ -63,19 +59,11 @@
* + html .blueimp-gallery > .slides > .slide > .video-content {
height: 100%;
}
-* + html .blueimp-gallery > .slides > .slide > .video-content > a {
+* + html .blueimp-gallery > .slides > .slide > .video-content > .video-play {
left: 50%;
margin-left: -64px;
}
-.blueimp-gallery > .slides > .slide > .video-content > a:hover {
+.blueimp-gallery > .slides > .slide > .video-content > .video-play:hover {
opacity: 1;
}
-
-@supports (object-fit: contain) {
- .blueimp-gallery > .slides > .slide > .video-content > img {
- width: 100%;
- height: 100%;
- object-fit: contain;
- }
-}
diff --git a/js/blueimp-gallery-video.js b/js/blueimp-gallery-video.js
index f001d98..d8b287c 100644
--- a/js/blueimp-gallery-video.js
+++ b/js/blueimp-gallery-video.js
@@ -32,6 +32,12 @@
videoLoadingClass: 'video-loading',
// The class for video when it is playing:
videoPlayingClass: 'video-playing',
+ // The class for video content displayed in an iframe:
+ videoIframeClass: 'video-iframe',
+ // The class for the video cover element:
+ videoCoverClass: 'video-cover',
+ // The class for the video play control:
+ videoPlayClass: 'video-play',
// Play videos inline by default:
videoPlaysInline: true,
// The list object property (or data attribute) for video preload:
@@ -45,9 +51,11 @@
$.extend(galleryPrototype, {
handleSlide: function (oldIndex, newIndex) {
handleSlide.call(this, oldIndex, newIndex)
- if (this.playingVideo) {
- this.playingVideo.pause()
- }
+ this.setTimeout(function () {
+ if (this.activeVideo) {
+ this.activeVideo.pause()
+ }
+ })
},
videoFactory: function (obj, callback, videoInterface) {
@@ -62,34 +70,42 @@
}
]
var video = videoInterface || document.createElement('video')
- var playMediaControl = document.createElement('a')
+ var coverElement = this.elementPrototype.cloneNode(false)
+ var playElement = document.createElement('a')
var url = this.getItemProperty(obj, options.urlProperty)
var sources = this.getItemProperty(obj, options.sourcesProperty)
var title = this.getItemProperty(obj, options.titleProperty)
var posterUrl = this.getItemProperty(obj, options.videoPosterProperty)
- var posterImage
+ var playControls = [playElement]
+ var hasGalleryControls
var isLoading
- var hasControls
var i
videoContainer.addClass(options.videoContentClass)
+ $(playElement).addClass(options.videoPlayClass)
+ if (
+ !$(coverElement)
+ .addClass(options.videoCoverClass)
+ .hasClass(options.toggleClass)
+ ) {
+ playControls.push(coverElement)
+ }
+ coverElement.draggable = false
if (title) {
videoContainerNode.title = title
- playMediaControl.setAttribute('aria-label', title)
+ playElement.setAttribute('aria-label', title)
}
if (posterUrl) {
- video.poster = posterUrl
- posterImage = this.imagePrototype.cloneNode(false)
- $(posterImage).addClass(options.toggleClass)
- posterImage.src = posterUrl
- posterImage.draggable = false
- posterImage.alt =
- this.getItemProperty(obj, this.options.altTextProperty) || title
- videoContainerNode.appendChild(posterImage)
+ // Set as background image instead of as poster video element property:
+ // - Is accessible for browsers that do not support the video element
+ // - Is accessible for both video element and iframe video players
+ // - Avoids visual artifacts in IE with the poster property set
+ coverElement.style.backgroundImage = 'url("' + posterUrl + '")'
}
- if (video.setAttribute && options.videoPlaysInline) {
- video.setAttribute('playsinline', '')
+ if (video.setAttribute) {
+ if (options.videoPlaysInline) video.setAttribute('playsinline', '')
+ } else {
+ videoContainer.addClass(options.videoIframeClass)
}
- video.controls = true
video.preload =
this.getItemProperty(obj, options.videoPreloadProperty) || 'none'
if (this.support.source && sources) {
@@ -100,8 +116,7 @@
}
}
if (url) video.src = url
- playMediaControl.href =
- url || (sources && sources.length && sources[0].src)
+ playElement.href = url || (sources && sources.length && sources[0].src)
if (video.play && video.pause) {
;(videoInterface || $(video))
.on('error', function () {
@@ -113,34 +128,40 @@
videoContainer
.removeClass(that.options.videoLoadingClass)
.removeClass(that.options.videoPlayingClass)
- if (hasControls) {
+ if (hasGalleryControls) {
that.container.addClass(that.options.controlsClass)
}
- delete that.playingVideo
+ video.controls = false
+ if (video === that.activeVideo) delete that.activeVideo
if (that.interval) {
+ // Continue slideshow interval
that.play()
}
})
.on('playing', function () {
isLoading = false
+ coverElement.removeAttribute('style')
videoContainer
.removeClass(that.options.videoLoadingClass)
.addClass(that.options.videoPlayingClass)
- if (that.container.hasClass(that.options.controlsClass)) {
- hasControls = true
- that.container.removeClass(that.options.controlsClass)
- } else {
- hasControls = false
- }
})
.on('play', function () {
+ // Clear slideshow timeout:
window.clearTimeout(that.timeout)
isLoading = true
videoContainer.addClass(that.options.videoLoadingClass)
- that.playingVideo = video
+ if (that.container.hasClass(that.options.controlsClass)) {
+ hasGalleryControls = true
+ that.container.removeClass(that.options.controlsClass)
+ } else {
+ hasGalleryControls = false
+ }
+ video.controls = true
+ that.activeVideo = video
})
- $(playMediaControl).on('click', function (event) {
+ $(playControls).on('click', function (event) {
that.preventDefault(event)
+ that.activeVideo = video
if (isLoading) {
video.pause()
} else {
@@ -151,7 +172,8 @@
(videoInterface && videoInterface.element) || video
)
}
- videoContainerNode.appendChild(playMediaControl)
+ videoContainerNode.appendChild(coverElement)
+ videoContainerNode.appendChild(playElement)
this.setTimeout(callback, [
{
type: 'load',