Add disappearing circle element to click function - javascript

I have a page where circles randomly appear in a certain place.
When you click on the circle, it disappears.
So, I have another circle-smoke, and I want that when the clicked circle disappears, this circle with the circle-smoke class appears in its place, and also dissolves in just a second.
I tried to add new element creation to my click function, but it didn't work
let div1 = document.createElement('div');
div1.classList.add("circle-smoke");
Isn’t it possible for the smoky circle to hold on a little more and steam out? The first one quickly disappeared.
In general, the whole point is to remain like this in the place of the disappearing circle, and then also disappear
How can I best implement this?
//create circle
var clickEl = document.getElementById("clicks");
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 95; // Including borders
function createDiv(id, color) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#ebc6df', '#ebc6c9', '#e1c6eb', '#c6c9eb', '#c6e8eb', '#e373fb', '#f787e6', '#cb87f7', '#87a9f7', '#87f7ee'];
randomColor = colors[Math.floor(Math.random() * colors.length)];
div.style.borderColor = randomColor;
}
else {
div.style.borderColor = color;
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
div.addEventListener('click', (event) => {
if (clicked) { return; } // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => { spawnRadius.removeChild(div); }, 220);
});
spawnRadius.appendChild(div);
}
let i = 0;
const rate = 3000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, rate);
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.circle {
width: 80px;
height: 80px;
border-radius: 80px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#spawnRadius {
top: 55%;
height: 250px;
width: 500px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
.circle-smoke {
position: relative;
height: 500px;
width: 500px;
filter: url(#wave);
transform: scale(0.3);
}
.circle-smoke::before {
content: "";
position: absolute;
top: 100px;
left: 100px;
right: 100px;
bottom: 100px;
border: 10px solid #fff;
border-radius: 50%;
box-shadow: 0 0 50px #ebc6df, inset 0 0 50px #ebc6df;
filter: url(#wave) blur(10px);
}
svg {
display: none;
}
<html>
<body>
<div id="spawnRadius"></div>
</body>
</html>

My quick and dirty solution is here (the filters were disabled and the rate was increased for debugging purposes):
//create circle
var clickEl = document.getElementById("clicks");
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 95; // Including borders
function createDiv(id, color, isSmoke) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (!isSmoke) {
if (color === undefined) {
let colors = ['#ebc6df', '#ebc6c9', '#e1c6eb', '#c6c9eb', '#c6e8eb', '#e373fb', '#f787e6', '#cb87f7', '#87a9f7', '#87f7ee'];
randomColor = colors[Math.floor(Math.random() * colors.length)];
div.style.borderColor = randomColor;
} else {
div.style.borderColor = color;
}
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
if (!isSmoke) {
div.addEventListener('click', (event) => {
if (clicked) {
return;
} // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => {
spawnRadius.removeChild(div);
}, 220);
let newDiv = createDiv(id, color, true);
newDiv.style.top = div.style.top;
newDiv.style.left = div.style.left;
newDiv.classList.add("circle-smoke");
newDiv.classList.remove("circle");
newDiv.id = null;
});
} else {
div.style.animation = 'Animation 500ms linear forwards';
setTimeout(() => {
spawnRadius.removeChild(div);
}, 220);
}
spawnRadius.appendChild(div);
return div;
}
let i = 0;
const rate = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, rate);
html,
body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.circle {
width: 80px;
height: 80px;
border-radius: 80px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#spawnRadius {
top: 55%;
height: 250px;
width: 500px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
.circle-smoke {
position: absolute;
margin: -5px;
height: 80px;
width: 80px;
border: 10px solid rgba(255, 255, 255, .5);
border-radius: 50%;
box-shadow: 0 0 50px #ebc6df, inset 0 0 50px #ebc6df;
//filter: url(#wave) blur(10px);
}
svg {
display: none;
}
<html>
<body>
<div id="spawnRadius"></div>
</body>
</html>

We can animate box-shadow:
let noOfCircles = 0;
const totalCircles = 10;
const colors = ['#ebc6df', '#ebc6c9', '#e1c6eb', '#c6c9eb', '#c6e8eb', '#e373fb', '#f787e6', '#cb87f7', '#87a9f7', '#87f7ee'];
const circleSize = 95; // Including borders
let i = 0;
const rate = 1000;
const clickEl = document.getElementById("clicks");
const spawnRadius = document.getElementById("spawnRadius");
const spawnArea = spawnRadius.getBoundingClientRect();
function createDiv(id, color) {
let div = document.createElement('div');
div.classList.add("circle", id);
if (!color)
color = colors[Math.floor(Math.random() * colors.length)];
div.style.setProperty('--bStart', color);
// Randomly position circle within spawn area
div.style.top = `${(Math.floor(Math.random() * spawnArea.height + circleSize)) % (spawnArea.height - circleSize)}px`;
div.style.left = `${(Math.floor(Math.random() * spawnArea.width + circleSize)) % (spawnArea.width - circleSize)}px`;
// Add click handler
div.addEventListener('click', (event) => {
div.style.setProperty('border-color', color+'ee');
div.style.setProperty('pointer-events', 'none');
div.classList.add("circle-smoke");
setTimeout(() => {
spawnRadius.removeChild(div);
noOfCircles--;
}, 1500);
}, { once: true });
spawnRadius.appendChild(div);
noOfCircles++;
}
setInterval(() => {
if (noOfCircles >= totalCircles) return;
createDiv(`circle${++i}`);
}, rate);
*{
box-sizing:border-box;
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: #0f0f0f;
}
.circle {
--bStart: #fff;
width: 80px;
height: 80px;
border-radius: 50%;
background-color: transparent;
border: 3px solid var(--bStart);
position: absolute;
}
#spawnRadius {
height: 100vh;
width: 100vw;
background: #0f0f0f;
border: 2px solid #ebc6df;
}
#keyframes Animation {
0% {
box-shadow: 0 0 0px 0px var(--bStart), inset 0 0 0px 0px var(--bStart);
opacity: 1;
}
100% {
box-shadow: 0 0 30px 25px #0000, inset 0 0 30px 25px #0000;
border-color: #0000;
opacity: 0;
}
}
.circle-smoke {
animation: Animation 1.5s linear forwards;
}
<div id="spawnRadius"></div>
{ once: true } removes the click event handler after one click.
div.style.setProperty('pointer-events', 'none') this allows us to click on multiple circles that are on top of each other.
For the demo I am allowing only 10 circles at a time.

Related

How to set a duration for requestAnimationFrame

requestAnimationFrame is good solution to creating javascript based animations. But i can not set a duration for this function. I want to play animations for a certain time. I tried some fps solutions but these are not smooth.
How can i fill this water in x seconds?
const water = document.querySelector('.water')
let scale = 0
const fillGlass = () => {
scale += 0.01
water.style.transform = `scaleY(${scale})`
if (scale <= 1) {
requestAnimationFrame(fillGlass)
}
}
requestAnimationFrame(fillGlass)
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<div class="glass">
<div class="water"></div>
</div>
Simply, you can control it with animation-duration. If you want to add dynamic value, I have just added a parameter to pass value to CSS
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform-origin: bottom center;
animation-name: animation;
animation-duration: var(--value);
}
#keyframes animation {
from { transform:scaleY(0) }
to { transform:scaleY(1) }
}
<div class="glass">
<div class="water" style='--value:20s'></div>
</div>
you can change --value in HTML, it will affect on CSS
As per your need, in JS
const water = document.querySelector('.water')
let scale = 0
let time = 2000 // 20 second * 100
const myInterval = setInterval(()=> {
scale += 1/time
water.style.transform = `scaleY(${scale})`
if (scale >= 1) {
clearInterval(myInterval);
}
}, 10);
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<div class="glass">
<div class="water"></div>
</div>
More Functions
const start = document.querySelector('.start')
const stop = document.querySelector('.stop')
const fill = document.querySelector('.fill')
const empty = document.querySelector('.empty')
const water = document.querySelector('.water')
let scale = 0
let time = 2000 // 20 second * 100
let myInterval;
start.addEventListener('click', () => {
myInterval = setInterval(() => {
scale += 1 / time
water.style.transform = `scaleY(${scale})`
if (scale >= 1) {
clearInterval(myInterval);
}
}, 10);
})
stop.addEventListener('click', () => {
clearInterval(myInterval);
})
fill.addEventListener('click', () => {
scale = 1
water.style.transform = `scaleY(${scale})`
})
empty.addEventListener('click', () => {
scale = 0
water.style.transform = `scaleY(${scale})`
})
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<button type="button" class="start">Start</button>
<button type="button" class="stop">Stop</button>
<button type="button" class="fill">Instant Fill</button>
<button type="button" class="empty">Instant Empty</button>
<div class="glass">
<div class="water"></div>
</div>
Defining simple animation like this is always better with pure CSS, also in this case, it gives you more control over the animation itself.
.water {
height: 100%;
background-color: #BBDEFB;
transform-origin: bottom center;
animation-name: animation;
animation-duration: 1s;
}
#keyframes animation {
from { transform:scaleY(0) }
to { transform:scaleY(1) }
}
For animation and smoothness you can divide the scale value by duration you want
const water = document.querySelector('.water')
let scale = 0
let time = 5000 //in seconds 1000=1sec
let duration = 100/time;
const fillGlass = () => {
scale += duration;
if(scale > 1){
scale = 1;
}
water.style.transform = `scaleY(${scale})`
if (scale <= 1) {
requestAnimationFrame(fillGlass)
}
}
requestAnimationFrame(fillGlass)

Making a Javascript music app based on tutorial, hit roadblock

I am currently trying to create a music app based on the following video: https://youtu.be/OafpiyPa63I?t=13127
I am at the linked timestamp, and I have written the code exactly as shown in the video, but for some reason, when I try to seek, the input is set back to 0 and therefore so is the song. My code for this part:
let currentStart = document.getElementById('currentStart');
let currentEnd = document.getElementById('currentEnd');
let seek = document.getElementById('seek');
let bar2 = document.getElementById('bar2');
let dot = document.getElementsByClassName('dot')[0];
music.addEventListener('timeupdate', () => {
let music_curr = music.currentTime;
let music_dur = music.duration;
let min1 = Math.floor(music_dur / 60);
let sec1 = Math.floor(music_dur % 60);
if (sec1 < 10) {
sec1 = `0${sec1}`;
};
currentEnd.innerText = `${min1}:${sec1}`;
let min2 = Math.floor(music_curr / 60);
let sec2 = Math.floor(music_curr % 60);
if (sec2 < 10) {
sec2 = `0${sec2}`;
};
currentStart.innerText = `${min2}:${sec2}`;
let progressBar = parseInt((music_curr / music_dur) * 100);
seek.value = progressBar;
let seekbar = seek.value;
bar2.style.width = `${seekbar}%`;
dot.style.left = `${seekbar}%`;
});
seek.addEventListener('change', () => {
music.currentTime = seek.value * music.duration / 100;
});
<div class="bar">
<input type="range" id="seek" min="0" max="100">
<div class="bar2" id="bar2"></div>
<div class="dot"></div>
</div>
header .master_play .bar {
position: relative;
width: 43%;
height: 2px;
background: rgb(105,105,170,.1);
margin: 0px 15px 0px 10px;
}
header .master_play .bar .bar2 {
position: absolute;
background: #36e2ec;
width: 0%;
height: 100%;
top: 0;
transition: 1s linear;
}
header .master_play .bar .dot {
position: absolute;
width: 5px;
height: 5px;
background: #36e2ec;
border-radius: 50%;
left: 0%;
top: -1.5px;
transition: 1s linear;
}
header .master_play .bar .dot::before {
content: '';
position: absolute;
width: 15px;
height: 15px;
border: 1px solid #36e2ec;
border-radius: 50%;
left: -6.5px;
top: -6.5px;
box-shadow: inset 0px 0px 3px #36e2ec;
}
header .master_play .bar input {
position: absolute;
width: 100%;
top: -7px;
left: 0;
cursor: pointer;
z-index: 999999999999;
opacity: .5;
}
I'm not sure if I am in the wrong, or if the tutorial is outdated.
I figured out it was the platform I was running the code on. Somewhere it had clashed, so I was able to move it to a new host and it worked.

memory js game pictures won't close

I found one game on js, and I want to add it to myself by redoing it a bit
https://jsfiddle.net/a7Lx1c98/
So, I want to replace emoji with pictures here, I do this
const emojis = ['https://i.imgur.com/GLS9S5f.jpg', 'https://i.imgur.com/IN9C2qz.jpg', 'https://i.imgur.com/Ke2ubzv.jpg', 'https://i.imgur.com/PbvJDyR.jpg', 'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg', 'https://i.imgur.com/aksV9O3.jpg', 'https://i.imgur.com/gYsZdE4.jpg', 'https://i.imgur.com/LXo6iW3.jpg', 'https://i.imgur.com/wYrEwNR.jpg']
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
const items = shuffle([...picks, ...picks])
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`
As a result, the pictures appear, but the game itself does not work, when you select two pictures and they are different, they do not close, but you can choose everything in a row
What could be wrong?
const selectors = {
boardContainer: document.querySelector('.board-container'),
board: document.querySelector('.board'),
moves: document.querySelector('.moves'),
timer: document.querySelector('.timer'),
start: document.querySelector('button'),
win: document.querySelector('.win')
}
const state = {
gameStarted: false,
flippedCards: 0,
totalFlips: 0,
totalTime: 0,
loop: null
}
const shuffle = array => {
const clonedArray = [...array]
for (let index = clonedArray.length - 1; index > 0; index--) {
const randomIndex = Math.floor(Math.random() * (index + 1))
const original = clonedArray[index]
clonedArray[index] = clonedArray[randomIndex]
clonedArray[randomIndex] = original
}
return clonedArray
}
const pickRandom = (array, items) => {
const clonedArray = [...array]
const randomPicks = []
for (let index = 0; index < items; index++) {
const randomIndex = Math.floor(Math.random() * clonedArray.length)
randomPicks.push(clonedArray[randomIndex])
clonedArray.splice(randomIndex, 1)
}
return randomPicks
}
const generateGame = () => {
const dimensions = selectors.board.getAttribute('data-dimension')
if (dimensions % 2 !== 0) {
throw new Error("The dimension of the board must be an even number.")
}
const emojis = ['https://i.imgur.com/GLS9S5f.jpg', 'https://i.imgur.com/IN9C2qz.jpg', 'https://i.imgur.com/Ke2ubzv.jpg', 'https://i.imgur.com/PbvJDyR.jpg', 'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg', 'https://i.imgur.com/aksV9O3.jpg', 'https://i.imgur.com/gYsZdE4.jpg', 'https://i.imgur.com/LXo6iW3.jpg', 'https://i.imgur.com/wYrEwNR.jpg']
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
const items = shuffle([...picks, ...picks])
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`
const parser = new DOMParser().parseFromString(cards, 'text/html')
selectors.board.replaceWith(parser.querySelector('.board'))
}
const startGame = () => {
state.gameStarted = true
selectors.start.classList.add('disabled')
state.loop = setInterval(() => {
state.totalTime++
selectors.moves.innerText = `${state.totalFlips} moves`
selectors.timer.innerText = `time: ${state.totalTime} sec`
}, 1000)
}
const flipBackCards = () => {
document.querySelectorAll('.card:not(.matched)').forEach(card => {
card.classList.remove('flipped')
})
state.flippedCards = 0
}
const flipCard = card => {
state.flippedCards++
state.totalFlips++
if (!state.gameStarted) {
startGame()
}
if (state.flippedCards <= 2) {
card.classList.add('flipped')
}
if (state.flippedCards === 2) {
const flippedCards = document.querySelectorAll('.flipped:not(.matched)')
if (flippedCards[0].innerText === flippedCards[1].innerText) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
setTimeout(() => {
flipBackCards()
}, 1000)
}
// If there are no more cards that we can flip, we won the game
if (!document.querySelectorAll('.card:not(.flipped)').length) {
setTimeout(() => {
selectors.boardContainer.classList.add('flipped')
selectors.win.innerHTML = `
<span class="win-text">
You won!<br />
with <span class="highlight">${state.totalFlips}</span> moves<br />
under <span class="highlight">${state.totalTime}</span> seconds
</span>
`
clearInterval(state.loop)
}, 1000)
}
}
const attachEventListeners = () => {
document.addEventListener('click', event => {
const eventTarget = event.target
const eventParent = eventTarget.parentElement
if (eventTarget.className.includes('card') && !eventParent.className.includes('flipped')) {
flipCard(eventParent)
} else if (eventTarget.nodeName === 'BUTTON' && !eventTarget.className.includes('disabled')) {
startGame()
}
})
}
generateGame()
attachEventListeners()
html {
width: 100%;
height: 100%;
background: linear-gradient(325deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
font-family: Fredoka;
}
.game {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 5px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
.disabled {
color: #757575;
}
.stats {
color: #FFF;
font-size: 14pt;
}
.board-container {
position: relative;
}
.board,
.win {
border-radius: 5px;
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
background: linear-gradient(135deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.board {
padding: 20px;
display: grid;
grid-template-columns: repeat(4, auto);
grid-gap: 20px;
}
.board-container.flipped .board {
transform: rotateY(180deg) rotateZ(50deg);
}
.board-container.flipped .win {
transform: rotateY(0) rotateZ(0);
}
.card {
position: relative;
width: 100px;
height: 100px;
cursor: pointer;
}
.card-front,
.card-back {
position: absolute;
border-radius: 5px;
width: 100%;
height: 100%;
background: #282A3A;
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg) rotateZ(50deg);
font-size: 28pt;
user-select: none;
text-align: center;
line-height: 100px;
background: #FDF8E6;
}
.card.flipped .card-front {
transform: rotateY(180deg) rotateZ(50deg);
}
.card.flipped .card-back {
transform: rotateY(0) rotateZ(0);
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #FDF8E6;
transform: rotateY(180deg) rotateZ(50deg);
}
.win-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 21pt;
color: #282A3A;
}
.highlight {
color: #6f00fc;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🧠 Memory Game in JavaScript</title>
<link rel="stylesheet" href="assets/styles.css" />
<script src="assets/game.js" defer></script>
</head>
<body>
<div class="game">
<div class="controls">
<button>Start</button>
<div class="stats">
<div class="moves">0 moves</div>
<div class="timer">time: 0 sec</div>
</div>
</div>
<div class="board-container">
<div class="board" data-dimension="4"></div>
<div class="win">You won!</div>
</div>
</div>
</body>
</html>
Because every pair is a match:
if (flippedCards[0].innerText === flippedCards[1].innerText)
Your elements have no text, so innerText is always an empty string. So any two cards, regardless of their images, match.
Probably the quickest solution is to compare the HTML instead:
if (flippedCards[0].innerHTML === flippedCards[1].innerHTML)
Assuming the rest of the HTML is always the same, the only different should be the src on the <img> element(s).
As an added exercise, you could also look into being more explicit in that comparison. Perhaps give each element a data-* property and compare those instead of relying on the text or HTML. Or perhaps specifically read the src property and compare those values instead of comparing the entire contents of the element(s).
The issue is comparing the innerText of the two cards. There is no inner text, so you can compare the card back image URLs.
const selected = [...flippedCards].map(card => card.querySelector('.card-back img').src);
if (allEqual(selected)) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
I also added an allEqual function to make sure all the items match:
const allEqual = arr => arr.every(v => v === arr[0]);
Working example
const allEqual = arr => arr.every(v => v === arr[0]);
const selectors = {
boardContainer: document.querySelector('.board-container'),
board: document.querySelector('.board'),
moves: document.querySelector('.moves'),
timer: document.querySelector('.timer'),
start: document.querySelector('button'),
win: document.querySelector('.win')
};
const state = {
gameStarted: false,
flippedCards: 0,
totalFlips: 0,
totalTime: 0,
loop: null
};
const shuffle = array => {
const clonedArray = [...array];
for (let index = clonedArray.length - 1; index > 0; index--) {
const randomIndex = Math.floor(Math.random() * (index + 1));
const original = clonedArray[index];
clonedArray[index] = clonedArray[randomIndex];
clonedArray[randomIndex] = original;
}
return clonedArray;
}
const pickRandom = (array, items) => {
const clonedArray = [...array];
const randomPicks = [];
for (let index = 0; index < items; index++) {
const randomIndex = Math.floor(Math.random() * clonedArray.length);
randomPicks.push(clonedArray[randomIndex]);
clonedArray.splice(randomIndex, 1);
}
return randomPicks;
}
const generateGame = () => {
const dimensions = selectors.board.getAttribute('data-dimension');
if (dimensions % 2 !== 0) {
throw new Error("The dimension of the board must be an even number.");
}
const emojis = [
'https://i.imgur.com/GLS9S5f.jpg',
'https://i.imgur.com/IN9C2qz.jpg',
'https://i.imgur.com/Ke2ubzv.jpg',
'https://i.imgur.com/PbvJDyR.jpg',
'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg',
'https://i.imgur.com/aksV9O3.jpg',
'https://i.imgur.com/gYsZdE4.jpg',
'https://i.imgur.com/LXo6iW3.jpg',
'https://i.imgur.com/wYrEwNR.jpg'
];
const picks = pickRandom(emojis, (dimensions * dimensions) / 2);
const items = shuffle([...picks, ...picks]);
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`;
const parser = new DOMParser().parseFromString(cards, 'text/html');
selectors.board.replaceWith(parser.querySelector('.board'));
}
const startGame = () => {
state.gameStarted = true
selectors.start.classList.add('disabled');
state.loop = setInterval(() => {
state.totalTime++;
selectors.moves.innerText = `${state.totalFlips} moves`;
selectors.timer.innerText = `time: ${state.totalTime} sec`;
}, 1000);
}
const flipBackCards = () => {
document.querySelectorAll('.card:not(.matched)').forEach(card => {
card.classList.remove('flipped');
})
state.flippedCards = 0;
}
const flipCard = card => {
state.flippedCards++;
state.totalFlips++;
if (!state.gameStarted) {
startGame();
}
if (state.flippedCards <= 2) {
card.classList.add('flipped');
}
if (state.flippedCards === 2) {
const flippedCards = document.querySelectorAll('.flipped:not(.matched)')
const selected = [...flippedCards].map(card => card.querySelector('.card-back img').src);
if (allEqual(selected)) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
setTimeout(() => {
flipBackCards();
}, 1000);
}
// If there are no more cards that we can flip, we won the game
if (!document.querySelectorAll('.card:not(.flipped)').length) {
setTimeout(() => {
selectors.boardContainer.classList.add('flipped');
selectors.win.innerHTML = `
<span class="win-text">
You won!<br />
with <span class="highlight">${state.totalFlips}</span> moves<br />
under <span class="highlight">${state.totalTime}</span> seconds
</span>
`;
clearInterval(state.loop);
}, 1000);
}
}
const attachEventListeners = () => {
document.addEventListener('click', event => {
const eventTarget = event.target;
const eventParent = eventTarget.parentElement;
if (eventTarget.className.includes('card') && !eventParent.className.includes('flipped')) {
flipCard(eventParent);
} else if (eventTarget.nodeName === 'BUTTON' && !eventTarget.className.includes('disabled')) {
startGame();
}
})
}
generateGame();
attachEventListeners();
html {
width: 100%;
height: 100%;
background: linear-gradient(325deg, #6f00fc 0%, #fc7900 50%, #fcc700 100%);
font-family: Fredoka;
}
.game {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 5px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
.disabled {
color: #757575;
}
.stats {
color: #FFF;
font-size: 14pt;
}
.board-container {
position: relative;
}
.board,
.win {
border-radius: 5px;
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
background: linear-gradient(135deg, #6f00fc 0%, #fc7900 50%, #fcc700 100%);
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.board {
padding: 20px;
display: grid;
grid-template-columns: repeat(4, auto);
grid-gap: 20px;
}
.board-container.flipped .board {
transform: rotateY(180deg) rotateZ(50deg);
}
.board-container.flipped .win {
transform: rotateY(0) rotateZ(0);
}
.card {
position: relative;
width: 100px;
height: 100px;
cursor: pointer;
}
.card-front,
.card-back {
position: absolute;
border-radius: 5px;
width: 100%;
height: 100%;
background: #282A3A;
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg) rotateZ(50deg);
font-size: 28pt;
user-select: none;
text-align: center;
line-height: 100px;
background: #FDF8E6;
}
.card.flipped .card-front {
transform: rotateY(180deg) rotateZ(50deg);
}
.card.flipped .card-back {
transform: rotateY(0) rotateZ(0);
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #FDF8E6;
transform: rotateY(180deg) rotateZ(50deg);
}
.win-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 21pt;
color: #282A3A;
}
.highlight {
color: #6f00fc;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="game">
<div class="controls">
<button>Start</button>
<div class="stats">
<div class="moves">0 moves</div>
<div class="timer">time: 0 sec</div>
</div>
</div>
<div class="board-container">
<div class="board" data-dimension="4"></div>
<div class="win">You won!</div>
</div>
</div>

How do I stop elements from moving when the media query is triggered?

I'm a complete novice at coding.
I am styling a recent project for a course I have worked on. I have put in a media query to change the properties of the H1 and Controls class. However, when I resize the browser to trigger the media query, it is also moving the button and score out of place. Is there a reason it is doing this and how do I fix it?
Many thanks in advance!
Ray
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class='mobile'>
<div class="info">
<h1>Snake Game</h1>
<button id="start">Lets go!</button>
<h2>Score <span id="score"></span></h2>
</div>
<div class="grid"></div>
<div class="nokia"></div>
<div class="controls">
<h3>Controls</h3>
<ul><span class="direction">Up</span> - Up arrow key</ul>
<ul><span class="direction">Right</span> - Right arrow key</ul>
<ul><span class="direction">Down</span> - Down arrow key</ul>
<ul><span class="direction">Left</span> - Left arrow key</ul>
</div>
</div>
<script src="main.js"></script>
</body>
.mobile {
display: flex;
justify-content: center;
}
.nokia {
position: absolute;
top: 190px;
display: block;
width: 700px;
height: 983px;
background-image: url("https://i.imgur.com/3SeVxgS.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
}
.controls {
position: absolute;
top: 100px;
display: flex;
}
#media (max-width: 930px) {
.controls {
top: 50px;
display: block;
font-size: 70%;
}
h1 {
font-size: 20px;
}
}
.grid {
position: absolute;
top: 420px;
z-index: 9999;
display: flex;
flex-wrap: wrap;
width: 200px;
height: 200px;
border: 2px solid rgba(0, 0, 0, 0.5);
background-color: white;
}
.info {
position: absolute;
z-index: 9999;
top: 0;
text-align: center;
}
h2 {
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
position: absolute;
top: 750px;
left: 40px;
}
button {
position: absolute;
top: 663px;
left: -5px;
height: 64px;
width: 172px;
border-style: solid;
border-bottom: 50px;
border-radius: 50%;
text-align: center;
display: inline-block;
font-size: 20px;
outline: none;
}
button:active {
transform: translateY(2px);
}
.square {
width: 20px;
height: 20px;
}
.snake {
background-color:#12c258
}
.apple {
background-color: red;
border-radius: 20%;
height: 20px;
width: 20px;
box-shadow: 0 0 0 0 rgba(0, 0, 0, 1);
transform: scale(1);
animation: pulse 2s infinite;
}
#keyframes pulse {
0% {
transform: scale(0.35);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
}
50% {
transform: scale(1);
/* box-shadow: 0 0 0 10px #12c258; */
}
100% {
transform: scale(0.35);
/* box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); */
}
}
const grid = document.querySelector(".grid");
const startButton = document.getElementById("start");
const scoreDisplay = document.getElementById("score");
let squares = [];
let currentSnake = [2, 1, 0];
let direction = 1;
const width = 10;
let appleIndex = 0;
let score = 0;
let intervalTime = 1000;
let speed = 0.9;
let timerId = 0;
function createGrid() {
//create 100 of these elements with a for loop
for (let i = 0; i < width * width; i++) {
//create element
const square = document.createElement("div");
//add styling to the element
square.classList.add("square");
//put the element into our grid
grid.appendChild(square);
//push it into a new squares array
squares.push(square);
}
}
createGrid();
currentSnake.forEach(index => squares[index].classList.add("snake"));
function startGame() {
//remove the snake
currentSnake.forEach(index => squares[index].classList.remove("snake"));
//remove the apple
squares[appleIndex].classList.remove("apple");
clearInterval(timerId);
currentSnake = [2, 1, 0];
score = 0;
//re add new score to browser
scoreDisplay.textContent = score;
direction = 1;
intervalTime = 1000;
generateApple();
//readd the class of snake to our new currentSnake
currentSnake.forEach(index => squares[index].classList.add("snake"));
timerId = setInterval(move, intervalTime);
}
function move() {
if (
(currentSnake[0] + width >= width * width && direction === width) || //if snake has hit bottom
(currentSnake[0] % width === width - 1 && direction === 1) || //if snake has hit right wall
(currentSnake[0] % width === 0 && direction === -1) || //if snake has hit left wall
(currentSnake[0] - width < 0 && direction === -width) || //if snake has hit top
squares[currentSnake[0] + direction].classList.contains("snake")
)
return clearInterval(timerId);
//remove last element from our currentSnake array
const tail = currentSnake.pop();
//remove styling from last element
squares[tail].classList.remove("snake");
//add square in direction we are heading
currentSnake.unshift(currentSnake[0] + direction);
//add styling so we can see it
//deal with snake head gets apple
if (squares[currentSnake[0]].classList.contains("apple")) {
//remove the class of apple
squares[currentSnake[0]].classList.remove("apple");
//grow our snake by adding class of snake to it
squares[tail].classList.add("snake");
console.log(tail);
//grow our snake array
currentSnake.push(tail);
console.log(currentSnake);
//generate new apple
generateApple();
//add one to the score
score++;
//display our score
scoreDisplay.textContent = score;
//speed up our snake
clearInterval(timerId);
console.log(intervalTime);
intervalTime = intervalTime * speed;
console.log(intervalTime);
timerId = setInterval(move, intervalTime);
}
squares[currentSnake[0]].classList.add("snake");
}
function generateApple() {
do {
appleIndex = Math.floor(Math.random() * squares.length);
} while (squares[appleIndex].classList.contains("snake"));
squares[appleIndex].classList.add("apple");
}
generateApple();
// 39 is right arrow
// 38 is for the up arrow
// 37 is for the left arrow
// 40 is for the down arrow
function control(e) {
if (e.keyCode === 39) {
console.log("right pressed");
direction = 1;
} else if (e.keyCode === 38) {
console.log("up pressed");
direction = -width;
} else if (e.keyCode === 37) {
console.log("left pressed");
direction = -1;
} else if (e.keyCode === 40) {
console.log("down pressed");
direction = +width;
}
}
document.addEventListener("keyup", control);
startButton.addEventListener("click", startGame);
The button and the score were in that "out of place" position by default but the "Snake Game" text was pushing it to the left, you can solve this issue by putting the "Snake Game" text out of the div that has the button in it.

Custom Media player design to scale when width changed

I am having a Trouble with this custom media player, Media player not functioning properly.
I have 2 problems:
When full-screen it goes back to the default HTML5 player
When i adjust the width of the video tag it throws all of the spacing out and ruins the player.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Video/Audio</title>
<link rel='stylesheet' type='text/css' href='style.css' />
<style type="text/css">
</style>
<script src='jquery.js'></script>
<script src='javascript.js'></script>
<script type='text/javascript'>
$(document).ready(function() {
$('video').videoPlayer({
'playerWidth' : 1,
'videoClass' : 'video'
});
});
</script>
</head>
<body>
<div class="container" class="player">
<video width="700" height="400">
<source src="https://s3-eu-west-1.amazonaws.com/icevideos/151014+Cathodic+Protection+of+Highways/151014.PETERBOROUGH.CATHODICPROTECTION.HIGH1.mp4" type="video/mp4">
<source src="movie.webm" type="video/webm">
</video>
</div>
</body>
</html>
CSS
body {
font-size: 62.5%;
padding: 0;
margin: 0;
}
.player {
background: grey;
box-sizing: border-box;
height: 40px;
-moz-box-sizing: border-box;
float: left;
font-family: Arial, sans-serif;
position: absolute;
padding: 0;
bottom: 4px;
z-index: 2;
opacity: 1;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
-webkit-transition: opacity 0.3s ease-in;
transition: opacity 0.3s ease-in;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
width: 100%;
}
.video {
position: relative;
margin: 0px auto;
}
.video:hover .player {
opacity: 1;
}
.player .progress {
width: 60%;
height: 20px;
border-radius: 5px;
background: #000;
box-shadow: inset 0 -5px 10px rgba(0,0,0,0.1);
float: left;
cursor: pointer;
margin: 12px 0 0 0;
padding: 0;
position: relative;
font-variant: normal;
margin-left: 20px;
}
.player .progress-bar {
background: #FF6600;
border-radius: 5px;
height: 100%;
position: relative;
z-index: 999;
width: 0;
}
.player .button-holder {
position: relative;
left: 10px;
}
.player .progress-button {
background: #00bdff;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
border-radius: 30px;
width: 20px;
height: 20px;
position: absolute;
left: -20px;
text-decoration: overline;
}
.player [class^="buffered"] {
background: rgba(255,255,255,0.1);
position: absolute;
top: 0;
left: 30px;
height: 100%;
border-radius: 5px;
z-index: 1;
}
.player .play-pause {
display: inline-block;
font-size: 3em;
float: left;
text-shadow: 0 0 0 #fff;
color: #00bdff;
width: 4%;
padding: 4px 0 0 0;
margin-left: 15px;
cursor: pointer;
font-variant: small-caps;
}
.player .play, .player .pause-button {
-webkit-transition: all 0.2s ease-out;
}
.player .play .pause-button, .player .pause .play-button {
display: none;
}
.player .pause-button {
padding: 5px 2px;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 34px;
}
.player .pause-button span {
background: #FF6600;
width: 8px;
height: 24px;
float: left;
display: block;
}
.player .pause-button span:first-of-type {
margin: 0 4px 0 0;
}
.player .time {
color: #fff;
font-weight: bold;
font-size: 1.2em;
position: absolute;
width: 150px;
margin-left: 425px;
bottom: 3px;
}
.player .stime, .ttime {
color: #fff;
}
.player .play:hover {
text-shadow: 0 0 5px #fff;
}
.player .play:active, .pause-button:active span {
text-shadow: 0 0 7px #fff;
}
.player .pause-button:hover span {
box-shadow: 0 0 5px #fff;
} .player .pause-button:active span {
box-shadow: 0 0 7px #fff;
}
.player .volume {
position: relative;
float: left;
width: 7%;
height: 100%;
margin-left: 70px;
}
.player .volume-icon {
padding: 1.5%;
height: 100%;
cursor: pointer;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-transition: all 0.15s linear;
}
.player .volume-icon-hover {
background-color: grey;
}
.player .volume-holder {
height: 100px;
width: 80%;
position: absolute;
display: none;
background: grey;
left: 0;
border-radius: 5px 5px 0 0;
top: -100px;
}
.player .volume-bar-holder {
background: black;
width: 20px;
box-shadow: inset 0px 0px 5px rgba(0,0,0,0.3);
margin: 15px auto;
height: 80px;
border-radius: 5px;
position: relative;
cursor: pointer;
}
.player .volume-button {
background: #00bdff;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
border-radius: 30px;
width: 20px;
height: 20px;
}
.player .volume-button-holder {
position: relative;
top: -10px;
}
.player .volume-bar {
background: #FF6600;
border-radius: 5px;
width: 100%;
height: 100%;
position: absolute;
bottom: 0;
}
.player .fullscreen {
width: 5%;
cursor: pointer;
float: left;
height: 100%;
}
.player .fullscreen a {
width: 25px;
height: 20px;
border-radius: 3px;
background: #00bdff;
display: block;
position: relative;
top: 10px;
margin: 0px auto;
}
.player .fullscreen a:hover {
background: #FF6600;
}
.player .volume-icon span {
width: 20%;
height: 23%;
background-color: #00bdff;
display: block;
position: relative;
z-index: 1;
font-weight: bold;
top: 40%;
color: #fff;
left: 22%;
}
.player .volume-icon span:before,
.player .volume-icon span:after {
content: '';
position: absolute;
}
.player .volume-icon span:before {
width: 0;
height: 0;
border: 1em solid transparent;
border-left: none;
border-right-color: #00bdff;
z-index: 2;
top: -2px;
left: 10%;
margin-top: -40%;
}
.player .volume-icon span:after {
width: 10%;
height: 4%;
border: 1px solid #00bdff;
left: 150%;
border-width: 0px 0px 0 0;
border-radius: 0 50px 0 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
font-variant: small-caps;
}
.player .v-change-11 span:after { border-width: 10px 10px 0 0; top: 0; }
.player .v-change-10 span:after { border-width: 9px 9px 0 0; top: 1px; }
.player .v-change-9 span:after { border-width: 8px 8px 0 0; top: 1px; }
.player .v-change-8 span:after { border-width: 7px 7px 0 0; top: 2px; }
.player .v-change-7 span:after { border-width: 6px 6px 0 0; top: 2px; }
.player .v-change-6 span:after { border-width: 5px 5px 0 0; top: 3px; }
.player .v-change-5 span:after { border-width: 4px 4px 0 0; top: 3px; }
.player .v-change-4 span:after { border-width: 3px 3px 0 0; top: 4px; }
.player .v-change-3 span:after { border-width: 2px 2px 0 0; top: 4px; }
.player .v-change-2 span:after { border-width: 1px 1px 0 0; top: 5px; }
.player .v-change-1 span:after { border-width: 0px 0px 0 0; top: 5px; }
.player .v-change-1 span:after {
content: '+';
-webkit-transform: rotate(45deg);
font-size: 20px;
top: -6px;
left: 25px;
color: #00bdff;
}
/* ------- IGNORE */
#header {
width: 100%;
margin: 0px auto;
}
#header #center {
text-align: center;
}
#header h1 span {
color: #000;
display: block;
font-size: 50px;
}
#header p {
font-family: 'Georgia', serif;
}
#header h1 {
color: #892dbf;
font: bold 40px 'Bree Serif', serif;
}
#travel {
padding: 10px;
background: rgba(0,0,0,0.6);
border-bottom: 2px solid rgba(0,0,0,0.2);
font-variant: normal;
text-decoration: none;
}
#travel a {
font-family: 'Georgia', serif;
text-decoration: none;
border-bottom: 1px solid #f9f9f9;
font-size: 20px;
color: #f9f9f9;
}
.container {
padding: 40px 0 0 0;
}
.logo {
margin-top: 9px;
float: left;
margin-left: 6px;
}
JS
(function($) {
$.fn.videoPlayer = function(options) {
var settings = {
playerWidth : '0.95', // Default is 95%
videoClass : 'video' // Video Class
}
// Extend the options so they work with the plugin
if(options) {
$.extend(settings, options);
}
// For each so that we keep chainability.
return this.each(function() {
$(this)[0].addEventListener('loadedmetadata', function() {
// Basic Variables
var $this = $(this);
var $settings = settings;
// Wrap the video in a div with the class of your choosing
$this.wrap('<div class="'+$settings.videoClass+'"></div>');
// Select the div we just wrapped our video in for easy selection.
var $that = $this.parent('.'+$settings.videoClass);
// The Structure of our video player
{
$( '<div class="player">'
+'<img class="logo" src="http://www.cpdonline.tv/ice-events/mediaplayer/icelogo.png" height="20px">'
+ '<div class="play-pause play">'
+ '<span class="play-button">►</span>'
+ '<div class="pause-button">'
+ '<span> </span>'
+ '<span> </span>'
+ '</div>'
+ '</div>'
+ '<div class="progress">'
+ '<div class="progress-bar">'
+ '<div class="button-holder">'
+ '<div class="progress-button"> </div>'
+ '</div>'
+ '</div>'
+ '<div class="time">'
+ '<span class="ctime">00:00</span>'
+ '<span class="stime"> / </span>'
+ '<span class="ttime">00:00</span>'
+ '</div>'
+ '</div>'
+ '<div class="volume">'
+ '<div class="volume-holder">'
+ '<div class="volume-bar-holder">'
+ '<div class="volume-bar">'
+ '<div class="volume-button-holder">'
+ '<div class="volume-button"> </div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '<div class="volume-icon v-change-0">'
+ '<span> </span>'
+ '</div>'
+ '</div>'
+ '<div class="fullscreen"> '
+ ' '
+ '</div>'
+ '</div>').appendTo($that);
}
// Width of the video
$videoWidth = $this.width();
$that.width($videoWidth+'px');
// Set width of the player based on previously noted settings
$that.find('.player').css({'width' : ($settings.playerWidth*100)+'%', 'left' : ((100-$settings.playerWidth*100)/2)+'%'});
// Video information
var $spc = $(this)[0], // Specific video
$duration = $spc.duration, // Video Duration
$volume = $spc.volume, // Video volume
currentTime;
// Some other misc variables to check when things are happening
var $mclicking = false,
$vclicking = false,
$vidhover = false,
$volhover = false,
$playing = false,
$drop = false,
$begin = false,
$draggingProgess = false,
$storevol,
x = 0,
y = 0,
vtime = 0,
updProgWidth = 0,
volume = 0;
// Setting the width, etc of the player
var $volume = $spc.volume;
// So the user cant select text in the player
$that.bind('selectstart', function() { return false; });
// Set some widths
var progWidth = $that.find('.progress').width();
var bufferLength = function() {
// The buffered regions of the video
var buffered = $spc.buffered;
// Rest all buffered regions everytime this function is run
$that.find('[class^=buffered]').remove();
// If buffered regions exist
if(buffered.length > 0) {
// The length of the buffered regions is i
var i = buffered.length;
while(i--) {
// Max and min buffers
$maxBuffer = buffered.end(i);
$minBuffer = buffered.start(i);
// The offset and width of buffered area
var bufferOffset = ($minBuffer / $duration) * 100;
var bufferWidth = (($maxBuffer - $minBuffer) / $duration) * 100;
// Append the buffered regions to the video
$('<div class="buffered"></div>').css({"left" : bufferOffset+'%', 'width' : bufferWidth+'%'}).appendTo($that.find('.progress'));
}
}
}
// Run the buffer function
bufferLength();
// The timing function, updates the time.
var timeUpdate = function($ignore) {
// The current time of the video based on progress bar position
var time = Math.round(($('.progress-bar').width() / progWidth) * $duration);
// The 'real' time of the video
var curTime = $spc.currentTime;
// Seconds are set to 0 by default, minutes are the time divided by 60
// tminutes and tseconds are the total mins and seconds.
var seconds = 0,
minutes = Math.floor(time / 60),
tminutes = Math.round($duration / 60),
tseconds = Math.round(($duration) - (tminutes*60));
// If time exists (well, video time)
if(time) {
// seconds are equal to the time minus the minutes
seconds = Math.round(time) - (60*minutes);
// So if seconds go above 59
if(seconds > 59) {
// Increase minutes, reset seconds
seconds = Math.round(time) - (60*minutes);
if(seconds == 60) {
minutes = Math.round(time / 60);
seconds = 0;
}
}
}
// Updated progress width
updProgWidth = (curTime / $duration) * progWidth
// Set a zero before the number if its less than 10.
if(seconds < 10) { seconds = '0'+seconds; }
if(tseconds < 10) { tseconds = '0'+tseconds; }
// A variable set which we'll use later on
if($ignore != true) {
$that.find('.progress-bar').css({'width' : updProgWidth+'px'});
$that.find('.progress-button').css({'left' : (updProgWidth-$that.find('.progress-button').width())+'px'});
}
// Update times
$that.find('.ctime').html(minutes+':'+seconds)
$that.find('.ttime').html(tminutes+':'+tseconds);
// If playing update buffer value
if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) {
bufferLength();
}
}
// Run the timing function twice, once on init and again when the time updates.
timeUpdate();
$spc.addEventListener('timeupdate', timeUpdate);
// When the user clicks play, bind a click event
$that.find('.play-pause').bind('click', function() {
// Set up a playing variable
if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) {
$playing = false;
} else { $playing = true; }
// If playing, etc, change classes to show pause or play button
if($playing == false) {
$spc.pause();
$(this).addClass('play').removeClass('pause');
bufferLength();
} else {
$begin = true;
$spc.play();
$(this).addClass('pause').removeClass('play');
}
});
// Bind a function to the progress bar so the user can select a point in the video
$that.find('.progress').bind('mousedown', function(e) {
// Progress bar is being clicked
$mclicking = true;
// If video is playing then pause while we change time of the video
if($playing == true) {
$spc.pause();
}
// The x position of the mouse in the progress bar
x = e.pageX - $that.find('.progress').offset().left;
// Update current time
currentTime = (x / progWidth) * $duration;
$spc.currentTime = currentTime;
});
// When the user clicks on the volume bar holder, initiate the volume change event
$that.find('.volume-bar-holder').bind('mousedown', function(e) {
// Clicking of volume is true
$vclicking = true;
// Y position of mouse in volume slider
y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top);
// Return false if user tries to click outside volume area
if(y < 0 || y > $(this).height()) {
$vclicking = false;
return false;
}
// Update CSS to reflect what's happened
$that.find('.volume-bar').css({'height' : y+'px'});
$that.find('.volume-button').css({'top' : (y-($that.find('.volume-button').height()/2))+'px'});
// Update some variables
$spc.volume = $that.find('.volume-bar').height() / $(this).height();
$storevol = $that.find('.volume-bar').height() / $(this).height();
$volume = $that.find('.volume-bar').height() / $(this).height();
// Run a little animation for the volume icon.
volanim();
});
// A quick function for binding the animation of the volume icon
var volanim = function() {
// Check where volume is and update class depending on that.
for(var i = 0; i < 1; i += 0.1) {
var fi = parseInt(Math.floor(i*10)) / 10;
var volid = (fi * 10)+1;
if($volume == 1) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11');
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-11');
}
}
else if($volume == 0) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1');
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-1');
}
}
else if($volume > (fi-0.1) && volume < fi && !$that.find('.volume-icon').hasClass('v-change-'+volid)) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-'+volid);
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-'+volid);
}
}
}
}
// Run the volanim function
volanim();
// Check if the user is hovering over the volume button
$that.find('.volume').hover(function() {
$volhover = true;
}, function() {
$volhover = false;
});
// For usability purposes then bind a function to the body assuming that the user has clicked mouse
// down on the progress bar or volume bar
$('body, html').bind('mousemove', function(e) {
// Hide the player if video has been played and user hovers away from video
if($begin == true) {
$that.hover(function() {
$that.find('.player').stop(true, false).animate({'opacity' : '1'}, 0.5);
}, function() {
$that.find('.player').stop(true, false).animate({'opacity' : '0'}, 0.5);
});
}
// For the progress bar controls
if($mclicking == true) {
// Dragging is happening
$draggingProgress = true;
// The thing we're going to apply to the CSS (changes based on conditional statements);
var progMove = 0;
// Width of the progress button (a little button at the end of the progress bar)
var buttonWidth = $that.find('.progress-button').width();
// Updated x posititon the user is at
x = e.pageX - $that.find('.progress').offset().left;
// If video is playing
if($playing == true) {
// And the current time is less than the duration
if(currentTime < $duration) {
// Then the play-pause icon should definitely be a pause button
$that.find('.play-pause').addClass('pause').removeClass('play');
}
}
if(x < 0) { // If x is less than 0 then move the progress bar 0px
progMove = 0;
$spc.currentTime = 0;
}
else if(x > progWidth) { // If x is more than the progress bar width then set progMove to progWidth
$spc.currentTime = $duration;
progMove = progWidth;
}
else { // Otherwise progMove is equal to the mouse x coordinate
progMove = x;
currentTime = (x / progWidth) * $duration;
$spc.currentTime = currentTime;
}
// Change CSS based on previous conditional statement
$that.find('.progress-bar').css({'width' : $progMove+'px'});
$that.find('.progress-button').css({'left' : ($progMove-buttonWidth)+'px'});
}
// For the volume controls
if($vclicking == true) {
// The position of the mouse on the volume slider
y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top);
// The position the user is moving to on the slider.
var volMove = 0;
// If the volume holder box is hidden then just return false
if($that.find('.volume-holder').css('display') == 'none') {
$vclicking = false;
return false;
}
// Add the hover class to the volume icon
if(!$that.find('.volume-icon').hasClass('volume-icon-hover')) {
$that.find('.volume-icon').addClass('volume-icon-hover');
}
if(y < 0 || y == 0) { // If y is less than 0 or equal to 0 then volMove is 0.
$volume = 0;
volMove = 0;
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11');
} else if(y > $(this).find('.volume-bar-holder').height() || (y / $that.find('.volume-bar-holder').height()) == 1) { // If y is more than the height then volMove is equal to the height
$volume = 1;
volMove = $that.find('.volume-bar-holder').height();
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1');
} else { // Otherwise volMove is just y
$volume = $that.find('.volume-bar').height() / $that.find('.volume-bar-holder').height();
volMove = y;
}
// Adjust the CSS based on the previous conditional statmeent
$that.find('.volume-bar').css({'height' : volMove+'px'});
$that.find('.volume-button').css({'top' : (volMove+$that.find('.volume-button').height())+'px'});
// Run the animation function
volanim();
// Change the volume and store volume
// Store volume is the volume the user last had in place
// in case they want to mute the video, unmuting will then
// return the user to their previous volume.
$spc.volume = $volume;
$storevol = $volume;
}
// If the user hovers over the volume controls, then fade in or out the volume
// icon hover class
if($volhover == false) {
$that.find('.volume-holder').stop(true, false).fadeOut(100);
$that.find('.volume-icon').removeClass('volume-icon-hover');
}
else {
$that.find('.volume-icon').addClass('volume-icon-hover');
$that.find('.volume-holder').fadeIn(100);
}
})
// When the video ends the play button becomes a pause button
$spc.addEventListener('ended', function() {
$playing = false;
// If the user is not dragging
if($draggingProgress == false) {
$that.find('.play-pause').addClass('play').removeClass('pause');
}
});
// If the user clicks on the volume icon, mute the video, store previous volume, and then
// show previous volume should they click on it again.
$that.find('.volume-icon').bind('mousedown', function() {
$volume = $spc.volume; // Update volume
// If volume is undefined then the store volume is the current volume
if(typeof $storevol == 'undefined') {
$storevol = $spc.volume;
}
// If volume is more than 0
if($volume > 0) {
// then the user wants to mute the video, so volume will become 0
$spc.volume = 0;
$volume = 0;
$that.find('.volume-bar').css({'height' : '0'});
volanim();
}
else {
// Otherwise user is unmuting video, so volume is now store volume.
$spc.volume = $storevol;
$volume = $storevol;
$that.find('.volume-bar').css({'height' : ($storevol*100)+'%'});
volanim();
}
});
// If the user lets go of the mouse, clicking is false for both volume and progress.
// Also the video will begin playing if it was playing before the drag process began.
// We're also running the bufferLength function
$('body, html').bind('mouseup', function(e) {
$mclicking = false;
$vclicking = false;
$draggingProgress = false;
if($playing == true) {
$spc.play();
}
bufferLength();
});
// Check if fullscreen supported. If it's not just don't show the fullscreen icon.
if(!$spc.requestFullscreen && !$spc.mozRequestFullScreen && !$spc.webkitRequestFullScreen) {
$('.fullscreen').hide();
}
// Requests fullscreen based on browser.
$('.fullscreen').click(function() {
if ($spc.requestFullscreen) {
$spc.requestFullscreen();
}
else if ($spc.mozRequestFullScreen) {
$spc.mozRequestFullScreen();
}
else if ($spc.webkitRequestFullScreen) {
$spc.webkitRequestFullScreen();
}
});
});
});
}
})(jQuery);
I would also like to point out that this is someone else source code and cannot find where i got this from.
https://jsfiddle.net/f39huqpv/

Categories