I'm making a video carousel which loops each time it progressive bar reaches it end,the video plays perfectly, but the loop attribute in swiper doesn't seem to work, Here is the code
import { useRef, useEffect } from 'react';
import { Pagination, A11y, type Swiper as SwiperRef } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import ship from '../assets/videos/ship.mp4'
import highway from '../assets/videos/pexels-kelly-lacy-5473765.mp4'
import 'swiper/css';
import 'swiper/css/pagination';
const NewHome = () => {
return (
<Swiper
modules={[ Pagination , A11y]}
autoplay={{ delay: 5000}}
pagination={{ clickable: true }}
loop
className="w-1/2"
>
<SwiperSlide>
<video width="900" height="800" autoPlay muted loop>
<source src={ship} type='video/mp4'/>
</video>
</SwiperSlide>
<SwiperSlide>
<video src={highway} width="900" height="800"/>
</SwiperSlide>
</Swiper>
)
}
export default NewHome
CSS
.swiper-container {
width: 100%;
height: 200px;
}
.swiper-container + .swiper-container {
margin-top: 30px;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: flex;
justify-content: center;
align-items: center;
}
.swiper-pagination-bullet {
width: 10rem;
height: 2px;
border-radius: 0;
position: relative;
overflow: hidden;
}
.swiper-pagination-bullet::before {
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.swiper-pagination-bullet-active {
background: rgba(0, 0, 0, 0.4);
/* outline: 1px red solid; */
}
.swiper-pagination-bullet-active::before {
background-color: red;
-webkit-animation: slide-progress 2s cubic-bezier(0.3, 0, 0.3, 1) forwards;
animation: slide-progress 2s cubic-bezier(0.3, 0, 0.3, 1) forwards;
}
.swiper-paused .swiper-pagination-bullet-active::before {
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
#-webkit-keyframes slide-progress {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0);
}
}
#keyframes slide-progress {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0);
}
}
I tried adding a delay option to the autoplay but its still didn't work, I expect it switch to the next video after the progress bar of each slide reaches it end
The loop property of the Swiper component may not be working because the videos have their own loop attribute.
If you loop your videos they cannot trigger the Swiper component to move to the next slide.
Try removing loop from video and rely on Swiper component's loop:
<SwiperSlide>
<video width="900" height="800" autoPlay muted> // removed loop
<source src={ship} type='video/mp4'/>
</video>
</SwiperSlide>
Found the solution, so I forgot to import the Autoplay module from swiper and now it works perfectly fine
Note:
You'll also need to import the css file for the autoplay module in order to make it work
Related
I managed to achieve this animation through CSS animations and React. but go stuck in flickering problem. I don't know why this is happening maybe 'cause I used setInterval or there is some problem with my css animations keyframes. help me to solve this problem. the flicker only occurs after some time. and after refreshing the page the animation works perfectly fine without flicker problem.
this is how the animation looks after refreshing the page and this is what I want too. (ignore the screen recorder watermark).
animation I want
but after sometime the animation starts flickering like this.
Flickering problem
here are the code snippets I wrote
jsx snippet
<div className="relative w-[280px] md:w-[350px] lg:w-[500px]">
<span>{"[ "}</span>
<p className="text_animate ml-2">
{dev ? "for" : "by"} Developers
</p>
<span className="absolute right-0 ">{" ]"}</span>
</div>
css snippet
.text_animate {
color: orange;
margin: 0 auto;
display: inline-block;
position: relative;
letter-spacing: .15em;
text-align: start;
animation: text-up 6s linear infinite;
cursor: none;
}
#keyframes text-up {
0% {
top:45px;
opacity: 0;
}
20% {
top:0;
opacity: 1;
}
35% {
top: 0;
opacity: 1;
}
50% {
top: -45px;
opacity: 0;
}
52% {
top: 45px;
opacity: 0;
}
70% {
top: 0;
opacity: 1;
}
85% {
top: 0;
opacity: 1;
}
100% {
top: -45px;
opacity: 0;
}
}
useState changing text
const [dev, setDev] = useState(true);
setInterval(() => {
setDev(!dev);
}, 3000);
If there is any better way to achieve this I would really love to learn so let me know that too.
Maybe you should put setInterval to useEffect, and remember to clear timer. Like this:
useEffect(() => {
const timer = setInterval(() => {
setDev(!dev);
}, 3000);
return () => {
clearInterval(timer);
}
}, []);
And there is a solution only using css to implement this, I will write a demo later.
EDIT:
Explain the above code:
useEffect with [] as second param will make sure run setInterval once when mount the component.
The clearInterval in return function will make sure engine GC the variables in setInterval callback functions when unmount the component, or the setInterval will still work even though you needn't it.
CSS only solution:
ul {
margin: 0;
padding: 0;
list-style-type: none;
}
li {
margin: 0;
padding: 0;
}
.scroll-container {
overflow: hidden;
height: calc(var(--line-h) * 1px);
line-height: calc(var(--line-h) * 1px);
font-size: 18px;
}
.scroll-container ul {
animation-name: move;
animation-duration: calc(var(--speed) * var(--lines));
animation-timing-function: steps(var(--lines));
animation-iteration-count: infinite;
}
.scroll-container ul li {
animation-name: li-move;
animation-duration: var(--speed);
animation-iteration-count: infinite;
}
#keyframes move {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(0, calc(var(--lines) * var(--line-h) * -1px));
}
}
#keyframes li-move {
0% {
transform: translate(0, 0);
}
50%,
100% {
transform: translate(0, calc(var(--line-h) * -1px));
}
}
<div
class="scroll-container"
style="--lines: 2; --line-h: 26; --speed: 3s"
>
<ul>
<li>For Developers</li>
<li>By Developers</li>
<!-- repeat first in tail for infinity -->
<li>For Developers</li>
</ul>
</div>
I leaned this from Chokcoco on CodePen but forget which post.
Rows Image
In the above picture, I want the rows to move to the left infinitely.
Below is the component code:
<div id={id} className="row__posters">
{movies.map((movie) => (
<img
key={movie.id}
onClick={() => handleClick(movie)}
className={`row__poster ${isLargeRow && "row__posterLarge"}`}
src={`${base_url}${
isLargeRow ? movie.poster_path : movie.backdrop_path
}`}
loading="lazy"
alt={movie.name}
/>
))}
</div>
And here is the css for the component code:
.row {
color: white;
margin-left: 20px;
}
.row__posters {
display: flex;
overflow-y: hidden;
overflow-x: scroll;
padding: 20px;
}
.row__posters::-webkit-scrollbar {
display: none;
}
.row__poster {
max-height: 100px;
object-fit: contain;
margin-right: 10px;
transition: transform 450ms;
}
.row__poster:hover {
transform: scale(1.08);
opacity: 1;
}
.row__posterLarge {
/* overflow-x: scroll; */
max-height: 250px;
/* width: calc(250px * 20); */
animation: scroll 40% linear =infinite;
}
.row__posterLarge:hover {
transform: scale(1.09);
opacity: 1;
}
#keyframes scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(calc(-250px * 10));
}
}
I want the large row to have an infinite autoplay slider and the other rows shouldn't be affected.
I tried (https://www.youtube.com/watch?v=3Z780EOzIQs) but nothing happened.
Then I tried react-slick third party (https://react-slick.neostack.com/docs/example/pause-on-hover) and it only broke the rows, it turned them into one column(left-aligned).
Do you have any ideas on how to implement this feature?
You can use react-slick for that. If you want to make it infinite scroll. You have to change the settings object. Which you pass inside the Slider Component.
Add these two inside the setting -
autoplay: true,
autoplaySpeed: 2000,
to make it infinite scroll you can use -
infinite: true,
You should also mention how many slides to show.
slidesToShow: 3,
slidesToScroll: 1,
Make sure those properties are these. And you have imported css file inside you component.
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
I want to create a simple line through animation and so far I'm here:
.strikethrough {
display: inline-block;
position: relative;
line-height: 1.5em;
}
.strikethrough:after {
content: '';
position: absolute;
display: block;
width: 100%;
height: 1px;
box-shadow: 0 1px rgba(252, 3, 3,0.7);
margin-top: -0.7em;
background: rgba(252, 3, 3,0.8);
transform-origin: center left;
animation: strikethrough 1s 0.5s cubic-bezier(.55, 0, .1, 1) 1;
}
#keyframes strikethrough {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
<span class="strikethrough">Favor packaging over toy</span>
As you see everything works fine except two things:
Now we can see the line at first then it hides and starts the animation, I want to see only the animated line.
I want to initiate the animation using javascript... but with this pseudo-element (after) it seems complicated!
You can fix your animation using animation-fill-mode:
animation-fill-mode: backwards;
To trigger your animation, just add the strikethrough class. The thing where I think this won't work is when you have a multiline text, as your ::after won't cover that.
document.querySelector( '.strikethrough' ).addEventListener( 'click', event => { event.target.classList.toggle( 'strikethrough' ); });
.strikethrough {
display: inline-block;
position: relative;
line-height: 1.5em;
}
.strikethrough:after {
content: '';
position: absolute;
display: block;
width: 100%;
height: 1px;
box-shadow: 0 1px rgba(252, 3, 3,0.7);
margin-top: -0.7em;
background: rgba(252, 3, 3,0.8);
transform-origin: center left;
animation: strikethrough 1s 0.5s cubic-bezier(.55, 0, .1, 1) 1;
animation-fill-mode: backwards;
}
#keyframes strikethrough {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
<span class="strikethrough">Favor packaging over toy</span>
Personally, I would take this simpler tack to reduce the amount of actual objects on screen, and the amount of code, by using a background image to scale. If cleverly set up, you could even multi-line this (by making the background the line height and having a middle pixel in it - and with SVGs you could ensure it was only 1px regardless of the stretching etc...).
document.querySelector( 'p' ).addEventListener( 'click', event => {
event.target.classList.toggle( 'strikethrough' );
})
#keyframes stretch {
to { background-size: 100% var(--line-height); }
}
:root {
--line-height: 1.2em;
}
p {
line-height: var(--line-height);
}
.strikethrough {
line-height: 1.2em;
background: url('data:image/svg+xml;charset=utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none"><line x1="0" y1="50%" x2="100%" y2="50%" stroke="black" stroke-width="1px" /></svg>') repeat-y 0 0 / 0 var(--line-height);
animation: stretch 4s;
animation-fill-mode: forwards;
}
<p>Hello World!<br />Another line, does it work?</p>
After the slide animation (scale(1.3) to scale(1)) finishes and moving back to the slide I get this annoying jump.
I want that each active slide will be already in scale(1.3) without the jump.
$('.home-slider').slick({
slidesToScroll: 1,
slidesToShow: 1,
//arrows: true,
autoplay: true,
autoplaySpeed: 7000,
dots: false
})
body,
html {
height: 100%;
background: #333;
font-family: 'Roboto', sans-serif;
}
.slideshow {
position: relative;
z-index: 1;
height: 100%;
max-width: 700px;
margin: 50px auto;
}
.slideshow .item {
height: 100%;
position: relative;
z-index: 1;
}
.slideshow .item img {
width: 100%;
}
.slideshow .item.slick-active img {
-webkit-animation: ken-burns-out 8s 1 ease-in-out forwards;
animation: ken-burns-out 8s 1 ease-in-out forwards;
}
/*//The animation: from 1.3 scale to 1*/
#-webkit-keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3)
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
#keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3)
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.js"></script>
<div class="slideshow">
<div class="home-slider">
<div class="item">
<img src="https://images.unsplash.com/photo-1532386236358-a33d8a9434e3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=978&q=80" />
</div>
<div class="item">
<img src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80" />
</div>
</div>
</div>
The problem happens because all of the images gets the same z-index.
My code doesn't work when you navigate from first slide to last slide (slide right), but it's something to start with.
first of all, delete the z-index from this selector: .slideshow .item.
Then, add transform to every image in order every image to start with this scale (maybe add class name to each image and then change the selector):
img {
transform: scale(1.3);
}
Add a function to get slick by index (there are cloned slicks elements so I use the selector that gets un-cloned slicks):
function getSlickByIndex(index) {
return $('.item:not(.slick-cloned)[data-slick-index="' + index + '"]');
}
Then, add init event (the logic makes the first slide to get the bigger zIndex=1 of all of his siblings that have zIndex=0), and beforeChange (the logic makes the next slick to get the bigger index and the others to get the smaller index):
$('.home-slider').on('init', function(event, slick){
getSlickByIndex(0).css('zIndex', '1');
}).on('beforeChange', function(event, slick, currentSlide, nextSlide){
getSlickByIndex(currentSlide).css('zIndex', '0');
getSlickByIndex(nextSlide).css('zIndex', '1');
});
Demo
img {
transform: scale(1.3);
}
body,
html {
height: 100%;
background: #333;
font-family: 'Roboto', sans-serif;
}
.slideshow {
position: relative;
z-index: 1;
height: 100%;
max-width: 700px;
margin: 50px auto;
}
.slideshow .item {
height: 100%;
position: relative;
z-index: 1;
}
.slideshow .item img {
width: 100%;
}
.slideshow .item.slick-active img {
-webkit-animation: ken-burns-out 8s 1 ease-in-out forwards;
animation: ken-burns-out 8s 1 ease-in-out forwards;
}
/*//The animation: from 1.3 scale to 1*/
#-webkit-keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3)
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
#keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3)
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.js"></script>
<div class="slideshow">
<div class="home-slider">
<div class="item">
<img src="https://images.unsplash.com/photo-1532386236358-a33d8a9434e3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=978&q=80" />
</div>
<div class="item">
<img src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80" />
</div>
<div class="item">
<img src="https://images.unsplash.com/photo-1532386236358-a33d8a9434e3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=978&q=80" />
</div>
<div class="item">
<img src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80" />
</div>
</div>
</div>
<script>
function getSlickByIndex(index) {
return $('.item:not(.slick-cloned)[data-slick-index="' + index + '"]');
}
$('.home-slider').on('init', function(event, slick){
getSlickByIndex(0).css('zIndex', '5555');
}).on('beforeChange', function(event, slick, currentSlide, nextSlide){
getSlickByIndex(currentSlide).css('zIndex', '0');
getSlickByIndex(nextSlide).css('zIndex', '5555');
});
$('.home-slider').slick({
slidesToScroll: 1,
slidesToShow: 1,
//arrows: true,
autoplay: true,
autoplaySpeed: 7000,
dots: false
});
</script>
This is a working fiddle: https://codepen.io/alonsh/pen/JQmZjj
Your images are starting with a scale of 1, then when they become active the animation starts them off at 1.3 which is what causes the jump. What you need to do is start all the images off at a scale(1.3) by adding this to the .item img css definition.
Doing only that causes the images to overflow onto one another. To combat that make sure the contents of each .item don't overflow by adding overflow: hidden which results in:
EDIT
Which results in code which still doesn't quite work correctly. We still need everything from the previous answer but we end up with the opposite issue where the images jump to the fully zoomed size when they're switched off.
This became a bit of a rabbit hole, but there is an animation-play-state property in css which we can use to pause the animation at the state it is in when an item becomes inactive, so I made the animation play on every .item element, which means that when it's scrolled away and the active class is lost, it will pause the animation where it is.
This almost gets us to the desired result, but then when an image is brought back into view, it resumes from where it has left off. It turns out there isn't a pretty way to get a css animation to restart, so what I'm doing is I've bound to the slide change event for slick and when a slide has changed (which I believe guarantees only the active slide is visible) I reset the .item animation. In order to do this in a way which doesn't interrupt the active slide I've added a class .animated which I can remove, then add back later in a setTimeout. The delay is needed in order for the animation to restart.
You'd hope that was the end, but you'd be wrong. THEN Slick considers a started swipe, which then lands back on the same image to be a slide change ("change") so I added a current slide variable to keep track of which slide we are on and only reset the .animated class if the slide did actually change. Important Note: in a real project I would wrap this currentSlide in some sort of class/object to track the state of an individual slideshow, because as it stands you couldn't have more than one slideshow on a single page without them interfering. But I wasn't going to write all of that for a simple example.
After all of this, we end up with slightly different CSS and a bit more JS to get a working result:
$('.home-slider').slick({
slidesToScroll: 1,
slidesToShow: 1,
//arrows: true,
autoplay: true,
autoplaySpeed: 7000,
dots: false
});
var currentSlide = 0;
$('.home-slider').on('afterChange', function(event, slick, newSlide){
var items = $('.home-slider .item');
if(currentSlide != newSlide){
currentSlide = newSlide;
items.removeClass('animated');
setTimeout(function(){
items.addClass('animated');
});
}
});
body,
html {
height: 100%;
background: #333;
font-family: 'Roboto', sans-serif;
}
.slideshow {
position: relative;
z-index: 1;
height: 100%;
max-width: 700px;
margin: 50px auto;
}
.slideshow .item {
height: 100%;
position: relative;
overflow: hidden;
}
.slideshow .item img {
width: 100%;
-webkit-transform: scale(1.3);
transform: scale(1.3);
}
.slideshow .item.animated img {
-webkit-animation: ken-burns-out 8s 1 ease-in-out forwards;
animation: ken-burns-out 8s 1 ease-in-out forwards;
animation-play-state: paused;
}
.slideshow .item.slick-active img {
animation-play-state: running;
}
/*//The animation: from 1.3 scale to 1*/
#-webkit-keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3);
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
#keyframes ken-burns-out {
0% {
-webkit-transform: scale(1.3);
transform: scale(1.3)
}
to {
-webkit-transform: scale(1);
transform: scale(1)
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.js"></script>
<div class="slideshow">
<div class="home-slider">
<div class="item animated">
<img src="https://images.unsplash.com/photo-1532386236358-a33d8a9434e3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=978&q=80" />
</div>
<div class="item animated">
<img src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80" />
</div>
</div>
</div>
I have a video and i have a loader for the buffer. i cannot seem to center it. Iv tried countless methods iv found online and nothing seems to center it.
I have tried every single method from here: http://vanseodesign.com/css/vertical-centering/
I have tried every method from here: https://www.w3.org/Style/Examples/007/center.en.html
I have tried every method on here: https://www.w3schools.com/css/css_align.asp
No joke im not kidding. i have tried everything.
Nothing is working....
Also on mobile that blue loader ends up at the bottom of the video and not centered meaning i cannot use margins because it needs to be centered for every single screen size. Though the spinner class requires this margin margin: 100px auto; or the loader wont show up.....
It needs to have Position: absolute; on the parent div.
It also needs to have display: none; on the parent div so it is hidden until the javascript calls it. Now No matter what i do i keep getting this:
Desktop Problem image <--
Mobile problem image <--
The "Hello how are you" is centered. The blue loader is not. The blue loader needs to be centered.
It must work in a grid similar to what i made here: https://jsfiddle.net/9faxe587/2/
var video = document.getElementById("video_1");
var placeholder = document.getElementById("placeholder_1");
placeholder_1.style.top = video_1.offsetTop + "px";
placeholder_1.style.left = video_1.offsetLeft + "px";
video_1.onwaiting = function() {
showPlaceholder(placeholder_1, this);
};
video_1.onplaying = function() {
hidePlaceholder(placeholder_1, this);
};
function showPlaceholder(img, vid) {
img.style.height = vid.scrollHeight + "px";
img.style.width = vid.scrollWidth + "px";
img.style.display = "block";
}
function hidePlaceholder(img, vid) {
img.style.display = "none";
}
.spinner {
margin: 100px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
}
.spinner>div {
background-color: #0080ff;
height: 100%;
width: 5.5px;
display: inline-block;
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
#-webkit-keyframes sk-stretchdelay {
0%,
40%,
100% {
-webkit-transform: scaleY(0.4)
}
20% {
-webkit-transform: scaleY(1.0)
}
}
#keyframes sk-stretchdelay {
0%,
40%,
100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
.THG-placeholder {
display: none;
position: absolute;
z-index: 1;
}
.THG-video {
width: 100% !important;
height: auto !important;
max-height: 380px;
max-width: 512px;
z-index: 1;
}
<video class="THG-video" id="video_1" controls preload="none">
<source src="http://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4" />
</video>
<div id="placeholder_1" class="THG-placeholder">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
I have updated #DanteTheSmith's fiddle with my solution using Flexbox.
Fiddle: https://jsfiddle.net/Ldhoo0f6/5/
.THG-placeholder {
top: 0;
left: 0;
width: 100%;
display: none; // show with display: flex;
align-items: center;
justify-content: center;
height: Calc(100% - 36px);
position: absolute;
z-index: 1;
}
video {
width: 100%;
}
.wrapper {
position: relative;
max-width: 460px; // or a percentage or whatever you like
}
Explanation
There is a wrapper div with position: relative around the whole thing, so the absolutely positioned element will be within that div's constraints.
The video is 100% width so it is responsive.
The wrapper div determines the size of the video with max-width
The placeholder element has display: flex added by js.
It is centred with Flexbox align-items and justify-content.
The height uses Calc to adjust for the height of the video controls - though this might differ depending on the browser (I used Chrome). You could just use 100% if you're not too pedantic.
Flexbox is pretty well supported these days: https://caniuse.com/#feat=flexbox
.spinner {
margin: 55px auto; // (video height / 2) - (spinner height / 2)
height: 40px;
text-align: center;
font-size: 10px;
}
If you wanna center the element in entire video box (including the controls)
.spinner {
margin: 32px auto; // (video height - controls height - 45px / 2) - (spinner height / 2)
height: 40px;
text-align: center;
font-size: 10px;
}
Working with second CSS:
fidd