All things Shopify and commerce
Join us March 21 for an AMA on planning your 2023 marketing budget with 2H Media co-owners, Matt and Aron
Hi,
We're using narrative theme and put video in the video slide section. It work on desktop mode but not working on the mobile mode.
Would appreciate any input on this.
TIA
Solved! Go to the solution
Apologies.
heres the password: ohtsew
Is there any update as the the issue still remains unsolved
Hi I have the same problem - Narrative Theme - Video is nor playing in mobile mode & I want to hide in the background the play button of the video slide
can you help?
Hi!
I am having the same problem, the video will not play on the mobile mode and I will be going live tomorrow.
Please can you help, thank you!!
braith
Hi,
I hope you are well!
I am having the same problem and I am set to go live in a couple of hours.
How can I make the video work on mobile mode?
Thank you!!
Hi,
I hope you are well!
I am having the same problem and I am set to go live in a couple of hours.
How can I make the video work on mobile mode?
Thank you!!
password: braith
You can remove the play/pause button simply by removing it from the slidewhow.liquid source file.
Hope this helps 🙂
P.S did you work out how to override the setting and get video play in mobile portrait mode?
how did you manage to get the video onto mobile view there isnt an option for me
I’m having the same issue
Same problem over here not sure how to solve it
I converted to Vimeo, compressed the video and also pasted the link from my video upload to my shopify. Auto play doesn’t appear.
Hey there- Ive the same problem but couldn't find a solution. Can you help me out?
Nicstand.com
pw: asdfghjk
Hey Chris,
I see you found a solution and playing video on your store now? How did you manage?
//Johan
I found a solution for this issue, it does require code changes in 3 files.
Couple of notes before making these changes:
Code Changes
In the theme.scss.liquid file comment out or delete the media query for the 'slideshow__video iframe'
.slideshow__video,
.slideshow__video iframe {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 101%;
visibility: hidden;
transition: $transition-slideshow;
pointer-events: none;
.slideshow--adapt & {
min-height: 56.25vw;
}
/* @include media-query($small) {
display: none;
}
*/
Next move to the theme.js file. This is where the heavy lifting is. What is happening is on mobile it is not initializing the call to load and play the video, so we need to add the code for that. All I did was grab the code that is loading and playing the video from the "Slideshow Desktop Extension" starting on line number 7173 and put it in the "Mobile Desktop Extension" starting on line number 7705. Below is the entire code block of the "Slideshow Mobile Extension" so you can copy this and completely replace that area.
Start on line number 7173 and go to 8195.
/*
Slideshow Mobile Extension
--------------------------------------------------------------------------------
Manages all the mobile behaviour of the home page slideshow
Events
------------
Name: slideshow_mobile_init_start
Description: Fired before the mobile slideshow begins to initialize
Payload: none
Name: slideshow_mobile_init_done
Description: Fired when the mobile slideshow is done initializing
Payload: none
Name: slideshow_mobile_destroy
Description: Fired when the mobile slideshow is destroyed
Payload: none
Name: slideshow_set_slide
Description: Fired when the user selects a specific slide
Payload: { number } Index of the slide being displayed
Name: slideshow_previous_slide
Description: Fired when the user selects the previous slide
Payload: { number } Index of the slide being displayed
Name: slideshow_next_slide
Description: Fired when the user selects the next slide
Payload: { number } Index of the slide being displayed
---Added the below for informational purposes---
Name: slideshow_video_load
Description: Fired when a video is being loaded in the slideshow
Payload: { object } Video player DOM object
Name: slideshow_video_loaded
Description: Fired when the video is done loading in the slideshow
Payload: { object } Video player DOM object
*/
var selectors$28 = {
button: '.slideshow__button',
buttons: '.slideshow__buttons',
ctaMultipleSlides: '.slideshow__button-cta--multiple',
ctaSingleSlide: '.slideshow__button-cta-single',
label: '.slideshow__button-label',
mobileTextContainer: '.slideshow__text-container-mobile',
mobileTextContent: '.slideshow__text-content-mobile',
navigationButtons: '[data-slider-navigation]',
nextButton: '[data-slider-navigation-next]',
previousButton: '[data-slider-navigation-previous]',
slide: '.slideshow__slide',
slideshow: '.slideshow',
indicatorDots: '.slideshow__indicator',
//Added video variable
video: '.slideshow__video'
};
var classes$25 = {
buttonActive: 'slideshow__button--active',
dotActive: 'slideshow__indicator--active',
linkActive: 'slideshow__button--link',
slideActive: 'slideshow__slide--active',
slideActiveTransitioning: 'slideshow__slide--transitioning',
navigationNoFocus: 'slideshow__navigation-item--no-focus',
//Added video classes
videoLoaded: 'slideshow__video--loaded',
videoPaused: 'slideshow__video--paused'
};
var slideshowMobile = {
initMobileSlideshow: function() {
this.trigger('slideshow_mobile_init_start');
this.$slideshow = $(selectors$28.slideshow, this.$container);
//Added slide for videos
this.$slide = $(selectors$28.slide, this.$container);
this.$buttons = $(selectors$28.buttons, this.$container);
this.$button = $(selectors$28.button, this.$container);
this.$navigationButtons = $(selectors$28.navigationButtons, this.$container);
this.$ctaMultipleSlides = $(selectors$28.ctaMultipleSlides, this.$container);
this.$ctaSingleSlide = $(selectors$28.ctaSingleSlide, this.$container);
this.$indicatorDots = $(selectors$28.indicatorDots, this.$container);
this.$mobileTextContainer = $(
selectors$28.mobileTextContainer,
this.$container
);
this.$mobileTextContent = $(selectors$28.mobileTextContent, this.$container);
this.mobileSlideshow = true;
this.currentMobileSlide = 0;
this.totalSlides = this.$buttons.data('count');
//Added players for videos
this.players = [];
this.xPosition = 0;
this.mobileSlideshowNamespace = '.mobileSlideshow';
// The header is above the slideshow in the iOS editor, so we need to
// reduce it's height by the height of the header.
if ($('html').hasClass('is-ios') && Shopify.designMode) {
this.$slideshow.css('height', '-=60px');
}
this.on(
'click keyup' + this.mobileSlideshowNamespace,
selectors$28.indicatorDots,
this._onClickIndicatorDot.bind(this)
);
this.on(
'click keyup' + this.mobileSlideshowNamespace,
selectors$28.previousButton,
this._previousSlideMobile.bind(this)
);
this.on(
'click keyup' + this.mobileSlideshowNamespace,
selectors$28.nextButton,
this._nextSlideMobile.bind(this)
);
this.on(
'keydown' + this.mobileSlideshowNamespace,
this._addKeyBindingsMobile.bind(this)
);
if (this.totalSlides > 1) {
this.hammertime = new Hammer(this.$container[0]);
// Import swipe gestures and only allow these two events
this.hammertime
.on('swipeleft', this._nextSlideMobile.bind(this))
.on('swiperight', this._previousSlideMobile.bind(this));
}
this.$button.first().addClass(classes$25.buttonActive);
utils.promiseStylesheet().then(
function() {
this._setSlideMobile(0);
this._setMobileText(0);
this._setSlideshowA11y();
this.trigger('slideshow_mobile_init_done');
}.bind(this)
);
},
destroyMobileSlideshow: function() {
this.trigger('slideshow_mobile_destroy');
this.mobileSlideshow = false;
this.$container.off(this.mobileSlideshowNamespace);
if (this.totalSlides > 1) {
this.hammertime.destroy();
}
//Added the loop for videos
// Loop over every video slide that is found as part of this.players
// and explicitly call the YouTube and/or Vimeo destroy method
// depending on the type of video player.
for (var key in this.players) {
if (!this.players.hasOwnProperty(key)) return;
var player = this.players[key];
if (typeof player.destroy === 'function') {
player.destroy();
} else if (typeof player.unload === 'function') {
player.unload();
}
}
//This is part of the loop code added
this.players = [];
},
_onClickIndicatorDot: function(evt) {
var $indicatorDot = $(evt.target);
var index = $indicatorDot.data('slide-index');
evt.preventDefault();
if (
evt.type === 'keyup' &&
!(
evt.keyCode === utils.keyboardKeys.ENTER ||
evt.keyCode === utils.keyboardKeys.SPACE
)
)
return;
this._setSlideMobile(index);
if (evt.type === 'keyup' || evt.detail === 0) {
this.$slideshow.focus();
}
},
_addKeyBindingsMobile: function(evt) {
if (evt.which === utils.keyboardKeys.LEFTARROW) {
this._previousSlideMobile(evt);
} else if (evt.which === utils.keyboardKeys.RIGHTARROW) {
this._nextSlideMobile(evt);
}
},
_previousSlideMobile: function(evt) {
if (evt.type === 'click') {
$(evt.target).addClass(classes$25.navigationNoFocus);
}
if (
(evt.type === 'keyup' &&
!(
evt.keyCode === utils.keyboardKeys.ENTER ||
evt.keyCode === utils.keyboardKeys.SPACE
)) ||
this.currentMobileSlide === 0
) {
return;
}
this._setSlideMobile(this.currentMobileSlide - 1);
},
_nextSlideMobile: function(evt) {
if (evt.type === 'click') {
$(evt.target).addClass(classes$25.navigationNoFocus);
}
if (
(evt.type === 'keyup' &&
!(
evt.keyCode === utils.keyboardKeys.ENTER ||
evt.keyCode === utils.keyboardKeys.SPACE
)) ||
this.currentMobileSlide === this.totalSlides - 1
) {
return;
}
this._setSlideMobile(this.currentMobileSlide + 1);
},
_setSlideMobile: function(slideIndex) {
//Added these variables and the if statement
var $currentSlide = this.$slide.eq(this.currentDesktopSlide);
var $nextSlide = this.$slide.eq(slideIndex);
var $video = $nextSlide.find(selectors$27.video);
// We call _loadVideo() before we check to see if
// this.currentDesktopSlide === slideIndex (below). This would never fire
// on initial load if it was after the condition below since 0 === 0
// would return true.
if ($video.length) {
this._loadVideo($video, $nextSlide);
}
//Everything above this was added
if (this.currentMobileSlide === slideIndex) return;
this.xPosition = slideIndex * 50;
this.$buttons.css({
transform: 'translate3d(-' + this.xPosition + '%, 0, 0)'
});
this._setActiveStates(slideIndex);
this._setSlideA11y(slideIndex);
this._setMobileText(slideIndex);
this.currentMobileSlide = slideIndex;
this.trigger('slideshow_set_slide', [slideIndex]);
this.$navigationButtons.attr('disabled', false);
if (this.currentMobileSlide === 0) {
this.$navigationButtons
.filter(selectors$28.previousButton)
.attr('disabled', true);
}
if (this.currentMobileSlide === this.totalSlides - 1) {
this.$navigationButtons
.not(selectors$28.previousButton)
.attr('disabled', true);
}
if (this.currentMobileSlide - 1 >= 0) {
this.trigger('slideshow_previous_slide', [slideIndex - 1]);
}
if (this.currentMobileSlide + 1 < this.totalSlides) {
this.trigger('slideshow_next_slide', [slideIndex + 1]);
}
},
//Added _loadVideo method
_loadVideo: function($video, $slide) {
this.$video = $video;
this.trigger('slideshow_video_load', [$video[0]]);
return this._promiseVideo().then(
function() {
$slide.addClass(classes$24.videoLoaded);
$slide.find(selectors$27.pauseButton).prop('disabled', false);
this.trigger('slideshow_video_loaded', [$video[0]]);
}.bind(this)
);
},
//Added _promiseVideo method
_promiseVideo: function() {
var playerType = this.$video.attr('data-video-type');
var promiseVideoPlayer;
if (playerType === 'youtube') {
promiseVideoPlayer = this._loadYoutubePlayer();
this.$video.attr('tabindex', '-1');
} else if (playerType === 'vimeo') {
promiseVideoPlayer = this._loadVimeoPlayer();
this.$video.find('iframe').attr('tabindex', '-1');
}
return promiseVideoPlayer;
},
//Added _loadYoutubePlayer method
_loadYoutubePlayer: function() {
var blockId = this.$video.attr('data-block-id');
var videoId = this.$video.attr('data-video-id');
return youtube
.promisePlayer(this.$video[0], {
videoId: videoId,
ratio: 16 / 9,
playerVars: {
// eslint-disable-next-line camelcase
iv_load_policy: 3,
modestbranding: 1,
autoplay: 0,
controls: 0,
showinfo: 0,
wmode: 'opaque',
branding: 0,
autohide: 0,
rel: 0
},
events: {
onStateChange: function(evt) {
// Video has ended, loop back to beginning
if (evt.data === 0) {
this.players[blockId].seekTo(0);
}
}.bind(this)
}
})
.then(
function(player) {
this.players[blockId] = player;
player.playVideo().mute();
// The video will not play if the iframe is set to visibility: hidden
// Need to set it seperately from other styles in order to resolve the promise
$(player.a).css('visibility', 'visible');
// set player to visible
return $.Deferred(function(defer) {
player.addEventListener('onStateChange', function(evt) {
// Only resolve the promise if the video is playing
if (evt.data === 1) {
defer.resolve();
}
});
});
}.bind(this)
);
},
//Added _loadVimeoPlayer method
_loadVimeoPlayer: function() {
var blockId = this.$video.attr('data-block-id');
var videoId = this.$video.attr('data-video-id');
return vimeo
.promisePlayer(this.$video[0], {
id: videoId,
loop: true,
// This property isn't reliable. The user might see the Vimeo playbar flash
// as the video begins to play.
playbar: false,
background: true
})
.then(
function(player) {
this.players[blockId] = player;
player.play();
player.setVolume(0);
return $.Deferred(function(defer) {
player.on('loaded', function() {
defer.resolve();
});
});
}.bind(this)
);
},
_setActiveStates: function(slideIndex) {
this.$slide = this.$slide || $(selectors$28.slide, this.$container); // eslint-disable-line shopify/jquery-dollar-sign-reference
this.$button = this.$button || $(selectors$28.button, this.$container); // eslint-disable-line shopify/jquery-dollar-sign-reference
this.$dot = this.$dot || $(selectors$28.indicatorDots, this.$container); // eslint-disable-line shopify/jquery-dollar-sign-reference
var $currentSlide = this.$slide.eq(this.currentMobileSlide);
var $nextSlide = this.$slide.eq(slideIndex);
$nextSlide.addClass(classes$25.slideActive).attr('aria-hidden', false);
$currentSlide.addClass(classes$25.slideActiveTransitioning);
utils.promiseTransitionEnd($nextSlide).then(function() {
$currentSlide
.removeClass(classes$25.slideActive)
.removeClass(classes$25.slideActiveTransitioning)
.attr('aria-hidden', true);
});
this.$button.removeClass(classes$25.buttonActive);
this.$button.eq(slideIndex).addClass(classes$25.buttonActive);
this.$dot.removeClass(classes$25.dotActive);
this.$dot.eq(slideIndex).addClass(classes$25.dotActive);
},
_setSlideshowA11y: function() {
this.$labels = this.$labels || this.$button.find(selectors$28.label); // eslint-disable-line shopify/jquery-dollar-sign-reference
this.$ctaSingleSlide =
this.$ctaSingleSlide || this.$button.find(selectors$28.ctaSingleSlide); // eslint-disable-line shopify/jquery-dollar-sign-reference
this.$ctaSingleSlide.attr('tabindex', '0');
this.$labels.attr('tabindex', '-1');
this._setSlideA11y(0);
$.each(
this.$indicatorDots,
function(index, indicatorDot) {
$(indicatorDot).attr({
'aria-controls': 'Slide' + index
});
}.bind(this)
);
},
_setSlideA11y: function(slideIndex) {
var $button = this.$button.eq(slideIndex);
this.$ctasMultipleSlides =
this.$ctasMultipleSlides ||
this.$button.find(selectors$28.ctaMultipleSlides); // eslint-disable-line shopify/jquery-dollar-sign-reference
if (this.$ctasMultipleSlides) {
this.$ctasMultipleSlides.attr('tabindex', '-1');
// All slide titles are tabbable. If the currently active button has a CTA
// link, the CTA link becomes tabbable as well.
if ($button.hasClass(classes$25.linkActive)) {
this.$ctasMultipleSlides.eq(slideIndex).attr('tabindex', '0');
}
}
$.each(
this.$indicatorDots,
function(index, indicatorDot) {
$(indicatorDot).attr({
'aria-label': this._slideLabel(slideIndex, index),
'aria-current': slideIndex === index ? true : false
});
}.bind(this)
);
},
_setMobileText: function(slideIndex) {
var $currentTextContent = this.$mobileTextContent.eq(slideIndex);
this.$ctaSingleSlide =
this.$ctaSingleSlide || this.$button.find(selectors$28.ctaSingleSlide); // eslint-disable-line shopify/jquery-dollar-sign-reference
if (this.$ctaSingleSlide.length) {
// Adjust for buttons with labels on multiple lines.
var paddingAdjustment =
(this.$ctaSingleSlide.outerHeight() - 50) / 2 + 40;
this.$mobileTextContent.css('padding-top', paddingAdjustment + 'px');
}
this.$mobileTextContent.hide();
$currentTextContent.show();
},
_slideLabel: function(activeSlideIndex, currentIndex) {
var label =
activeSlideIndex === currentIndex
? theme.strings.slideshow.activeSlideA11yString
: theme.strings.slideshow.loadSlideA11yString;
return label.replace('[slide_number]', currentIndex + 1);
}
};
Finally, we need to actually use the theme.js file. Right now your website is using the minimized version of theme.js called theme.min.js. The minimized version doesn't have the changes we just made. In your theme.liquid file go to line 90 and replace 'theme.min.js' with 'theme.js'. Line 90 should look like this:
<script src="{{ 'theme.js' | asset_url }}" defer="defer"></script>
Your featured slider video should now be working on mobile.
After these changes I did notice that on desktop the play/pause button always showed and we did not want that as we wanted the video to be on repeat. So if this happens to you here is how to fix it. Go to the theme.scss.liquid file and add a 'display:none' to the 'slideshow__play-button'. Starting at line 6115 should look like the below:
.slideshow__play-button {
display: none;
opacity: 0;
position: relative;
margin: 0 auto;
height: 50px;
width: 50px;
padding: 0;
transition: transform 0.5s ease;
I hope this helps. I tried to make it as straightforward as possible.
-hellquake
i have the same problem,
can someone please help me with that ??
Did you find a fix for auto-play and looping for the video in the narrative theme? I'm trying to get my product videos to auto-play + loop but haven't found a result.
I posted exact steps and code of how to get a movie working on mobile. I made it as straight forward as I could to fix the issue. You can find the solution here: Solved: Re: Narrative Theme - Video is not playing in mobile mode - Page 4 - Shopify Community
User | RANK |
---|---|
31 | |
22 | |
20 | |
18 | |
17 |
Thanks to all Community members that participated in our inaugural 2 week AMA on the new E...
By Jacqui Mar 10, 2023Upskill and stand out with the new Shopify Foundations Certification program
By SarahF_Shopify Mar 6, 2023One of the key components to running a successful online business is having clear and co...
By Ollie Mar 6, 2023