how to stop and start AMcharts globe animation with intersectionObserver? - javascript

I have this spinning globe animation from AMcharts on the front page of my site. but it eats a LOT of system resources, even when its scrolled off screen, any more animated buttons added to the site will make chrome slow to a crawl. I have done some research over the last couple of days about how to stop and start three.js animations, and canvas animations and found out about intersection observer, and I copied and pasted some code from places that I've found it and added it in, it seems like I'm still missing the actual stop and start functions from AMcharts that intersectionobserver can hook on to to control the animation while intersecting with my div with the id="status"
I've studied the AMcharts docs but I can't make any sense of it, is there a stop and start or pause function in amcharts?
/**
* ---------------------------------------
* This demo was created using amCharts 4.
*
* For more information visit:
* https://www.amcharts.com/
*
* Documentation is available at:
* https://www.amcharts.com/docs/v4/
* ---------------------------------------
*/
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
am4core.options.queue = true;
am4core.options.onlyShowOnViewport = true;
var chart = am4core.create("chartdiv", am4maps.MapChart);
// Set map definition
chart.geodata = am4geodata_worldLow;
// Set projection
chart.projection = new am4maps.projections.Orthographic();
chart.panBehavior = "none";
chart.deltaLatitude = -20;
chart.padding(20,20,20,20);
// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
// Make map load polygon (like country names) data from GeoJSON
polygonSeries.useGeodata = true;
//polygonSeries.include = ["BR", "UA", "MX", "CI"];
// Configure series
var polygonTemplate = polygonSeries.mapPolygons.template;
polygonTemplate.fill = am4core.color("#d5ebfe");
polygonTemplate.stroke = am4core.color("#fff");
polygonTemplate.strokeWidth = 0.0;
var graticuleSeries = chart.series.push(new am4maps.GraticuleSeries());
graticuleSeries.mapLines.template.line.stroke = am4core.color("#00000");
graticuleSeries.mapLines.template.line.strokeOpacity = 0.00;
graticuleSeries.fitExtent = false;
chart.maxZoomLevel = 1;
chart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 0.0;
chart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#ffffff");
// Create hover state and set alternative fill color
let animation;
setTimeout(function(){
animation = chart.animate({property:"deltaLongitude", to:100000}, 20000000);
}, 3000)
chart.seriesContainer.events.on("down", function(){
// animation.stop();
})
////////////////My added intersectionObserver code////////
function start() {
create();
}
// stop render
function stop() {
window.cancelAnimationFrame(requestId);
requestId = undefined;
}
const statusElem = document.querySelector('.status');
const onScreen = new Set();
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
onScreen.add(entry.target);
start();
console.log('render has been started');
} else {
onScreen.delete(entry.target);
stop();
console.log('render has been halted');
}
});
statusElem.textContent = onScreen.size
? `on screen: ${[...onScreen].map(e => e.textContent).join(', ')}`
: 'none';
});
document.querySelectorAll('#chartdiv').forEach(elem => intersectionObserver.observe(elem));
.status {
position: fixed;
background: rgba(0,0,0,0.8);
color: white;
padding: 1em;
font-size: medium;
top: 50%;
left: 0;
z-index: 998;
}
#header{
position:fixed;
top: 0px;
right:0px;
z-index: 998;
height: 5em;
width: 100%;
background-color: white;
opacity: 80%;
}
#chartdiv {
width: 100%;
height: 20em;
max-width:100%;
}
#section0{
background-image: linear-gradient(128deg,#340191,#000);
height: 300vh;
}
<body>
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/maps.js"></script>
<script src="https://www.amcharts.com/lib/4/geodata/worldLow.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div class="status"></div>
<header id="header">
</header>
<div class="status">status</div>
<div class="section" id="section0">
<div class="intro">
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<div id="chartdiv"></div>
<script src="js/globe.js"></script>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</div>
</div>
</body>
There is the minimum reproduceable example. when #status intersects with #chartdiv the animation should run, when it doesn't the animation should stop.

Animation Class have two methods that you can utilize here: pause() and resume()
More information in Docs:
https://www.amcharts.com/docs/v4/reference/animation/
/**
* ---------------------------------------
* This demo was created using amCharts 4.
*
* For more information visit:
* https://www.amcharts.com/
*
* Documentation is available at:
* https://www.amcharts.com/docs/v4/
* ---------------------------------------
*/
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
am4core.options.queue = true;
am4core.options.onlyShowOnViewport = true;
var chart = am4core.create("chartdiv", am4maps.MapChart);
// Set map definition
chart.geodata = am4geodata_worldLow;
// Set projection
chart.projection = new am4maps.projections.Orthographic();
chart.panBehavior = "none";
chart.deltaLatitude = -20;
chart.padding(20,20,20,20);
// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
// Make map load polygon (like country names) data from GeoJSON
polygonSeries.useGeodata = true;
//polygonSeries.include = ["BR", "UA", "MX", "CI"];
// Configure series
var polygonTemplate = polygonSeries.mapPolygons.template;
polygonTemplate.fill = am4core.color("#d5ebfe");
polygonTemplate.stroke = am4core.color("#fff");
polygonTemplate.strokeWidth = 0.0;
var graticuleSeries = chart.series.push(new am4maps.GraticuleSeries());
graticuleSeries.mapLines.template.line.stroke = am4core.color("#00000");
graticuleSeries.mapLines.template.line.strokeOpacity = 0.00;
graticuleSeries.fitExtent = false;
chart.maxZoomLevel = 1;
chart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 0.0;
chart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#ffffff");
// Create hover state and set alternative fill color
let animation;
setTimeout(function(){
animation = chart.animate({property:"deltaLongitude", to:100000}, 20000000);
}, 3000)
chart.seriesContainer.events.on("down", function(){
// animation.stop();
})
////////////////My added intersectionObserver code////////
function start() {
animation.resume();
}
// stop render
function stop() {
animation.pause();
}
const statusElem = document.querySelector('.status');
const onScreen = new Set();
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
onScreen.add(entry.target);
start();
console.log('render has been started');
} else {
onScreen.delete(entry.target);
stop();
console.log('render has been halted');
}
});
statusElem.textContent = onScreen.size
? `on screen: ${[...onScreen].map(e => e.textContent).join(', ')}`
: 'none';
});
document.querySelectorAll('#chartdiv').forEach(elem => intersectionObserver.observe(elem));
.status {
position: fixed;
background: rgba(0,0,0,0.8);
color: white;
padding: 1em;
font-size: medium;
top: 50%;
left: 0;
z-index: 998;
}
#header{
position:fixed;
top: 0px;
right:0px;
z-index: 998;
height: 5em;
width: 100%;
background-color: white;
opacity: 80%;
}
#chartdiv {
width: 100%;
height: 20em;
max-width:100%;
}
#section0{
background-image: linear-gradient(128deg,#340191,#000);
height: 300vh;
}
<body>
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/maps.js"></script>
<script src="https://www.amcharts.com/lib/4/geodata/worldLow.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div class="status"></div>
<header id="header">
</header>
<div class="status">status</div>
<div class="section" id="section0">
<div class="intro">
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<div id="chartdiv"></div>
<script src="js/globe.js"></script>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</div>
</div>
</body>

Related

Dynamic PageNumbers for Flipbook using turn.js

So, I was given a task of creating a Custom Flipbook, where using images in div tags, I was successful in creating one. Users could turn pages using prev/next buttons or flipping through them by the corners like shown for other Flipbooks.
My concern is Displaying PageNumbers. Changing pages through the buttons, pages change dynamically but when flipping through turn.js the page number does not update.
I am providing the snippet of the code that I have used. Any kind of help and guidance is appreciated !!
<!DOCTYPE html>
<head>
<title>Flipbook Demo</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<script type="text/javascript" src="turn.min.js"></script>
</head>
<style>
body{
background-color: #313131;
}
#flipbook {
margin-top: 1.5%;
margin-left: 6%;
width: 1130px;
height: 800px;
position: relative;
overflow: hidden;
}
#nav_controls{
margin: 1.5%;
margin-left: 44%;
}
</style>
<body>
<h1 style="color: white; margin-left: 43%">FITI5 WHITEPAPER</h1>
<div id="flipbook">
<!-- Include Pages into div that you want to include -->
</div>
<div id="nav_controls">
<button id="startdoc"><-</button>
<button id="prev_page"> PREV </button>
<span id="pgnos" style="margin-left: 2%; color: white;">1</span>
<button id="next_page" style="margin-left: 2%;"> NEXT </button>
<button id="enddoc">-></button>
<!--
<button id="zoom-in">+</button>
<buton id="zoom-out">-</button>-->
</div>
<script type="text/javascript">
const startButton = document.querySelector("#startdoc");
const endButton = document.querySelector("#enddoc");
const prevButton = document.querySelector("#prev_page");
const nextButton = document.querySelector("#next_page");
const showPG = document.querySelector("#pgnos");
//magnify = document.querySelector("#zoom-in");
//minify = document.querySelector("#zoom-out");
/*
magnify.addEventListener('click', function() {
$("#flipbook").turn("zoom", 1.1, 1);
});
minify.addEventListener('click', function() {
$("#flipbook").turn("zoom", 1, 1.1);
})
*/
$("#flipbook").turn({
gradients: true,
page: 1,
duration: 2000
});
const first_page = $("#flipbook").turn("page");
const last_page = $("#flipbook").turn("pages");
startButton.addEventListener('click', function() {
$("#flipbook").turn("page", first_page);
showPG.innerHTML = first_page;
});
endButton.addEventListener('click', function() {
$('#flipbook').turn("page", last_page);
showPG.innerHTML = last_page;
});
nextButton.addEventListener('click', function() {
$("#flipbook").turn("next");
showPG.innerHTML = $("#flipbook").turn("page");
});
prevButton.addEventListener('click', function() {
$("#flipbook").turn("previous");
showPG.innerHTML = $("#flipbook").turn("page");
});
if ( (($("#flipbook").turn("page") == first_page)) ) {
$(nextButton).click(function() {
$("#flipbook").animate({left: "275"});
});
$(endButton).click(function() {
$("#flipbook").animate({left: "565"});
});
$(prevButton).click(function() {
$("#flipbook").animate({left: "275"});
});
$(startButton).click(function() {
$("#flipbook").animate({left: "0"});
});
}
if ( (($("#flipbook").turn("page") == last_page)) ) {
$(prevButton).click(function() {
$("#flipbook").animate({left: "300"});
});
}
</script>
</body>
</html>

Update all Object key values with the most recent DOM value after user presses a button

Context: I have a web page that allows the user to add assets by clicking on a button. When the user presses this button, the main goal is that the Quota get's divided by the number of assets added (so that the quota % is distributed equally)
Problem: Everything is working as planned, however, I would want to update the values of the Quota as new assets are added. What I want is the following: we start with 1 asset which takes 100%. Then, by adding a new asset, we would have 2 assets, both with 50%. Adding a 3rd asset would update the Quota values to 33.33% for all 3 assets, etc. The only way I can draw the graph is if I delete all the Potential Loss values (literally delete the 0 that is set to each row), which then updates to the correct Quota values. As of now, the code With 3 assets added will retrieve 100% for the 1st, 50% for the 2nd, and 33.33% for the 3rd asset (in this situation, all should change their quotas to 33.33% automatically).
Question: Is there a way to automatically update the Quota values as the user presses the Add Asset button or Draw Graph button?
Failed Implementations: I was thinking of taking the latest quota index value and then update the previous indices with this key values of quota so that whenever the user presses "draw graph", the previous quota values are updated and I tried doing that with Object.keys, but this will create conflict with the data since changing the values of one, will change the values of the other DOM elements (since they share the same index) - meaning if we change the name of the last "NEW STOCK" added, this will change the others as well.
Thank you in advance!
Original JS
<!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>Document</title>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body style="background-color:#DFCFBE">
<div class="row">
<div class="column left">
<form id=" form">
<div class="tr">
<div class="td">Asset </div>
<div class="td">Quota % </div>
<div class="td">Potencial Loss % </div>
</div>
<div class="tr" data-type="wrapper" data-index="1">
<div class="td" data-type="field_div"><input type="text" size="5" value="NEW STOCK" class="stock"
onchange="drawChart();" /> </div>
<div class="td"><input type="number" min="0" max="100" value="100" class="quota"
onchange="drawChart();" /> </div>
<div class="td"><input type="number" min="0" max="100" value="0" class="perda" />
</div>
</div>
<p onload="addNumAtivo();" id="indexSum">Number of Assets: 1</p>
<p onload="addNumAtivo();" id="indexQuota">Quota per asset: </p>
<p id="indexLoss">Total Loss:</p>
<button class="button2" type="button" onclick="addStock(event), addNumAtivo();">Add
Asset</button>
<button class="button1" type="button" onclick="drawChart();">Draw Graph</button>
</form>
</div>
</div>
<div class="column right" style="position:relative;width:70%">
<div class=" piechart" id="piechart" style="position:absolute;right:0px;top:0px;width: 900; height: 590px;">
</div>
</div>
</body>
<script>
//Define variables
let perda; //This value is calculated on line 154
let indexSum = 1;
let valuePercentage = 100; //This value percentage is essentially the "Quota". This value will be 100% divided by the amount of indexSum (essentialy, divided by ativo1, ativo2, ativo3... ativo(i))
let indexSumRatio = 1;
let numQuota = 100;
let numAtivo = 1;
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
//Functions that will increase the number of Assets and change the quota % as the user adds new assets (Add Stock) on the HTML
function addNumAtivo() {
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
}
document.getElementsByClassName("button2").onclick = numAtivo;
document.getElementsByClassName("button2").onclick = valuePercentage;
////////////////////////////////////////////////////////////////////////////////////
//Load google chart graphs
google.charts.load("current", {
packages: ["corechart"]
});
google.charts.setOnLoadCallback(drawChart);
// Function that will add new "Ativo", a new "Quota%" and a new "Perda potencial%" as the user presses the button "Add Stock".
function addStock(event) {
// find the last Wrapper [data-set="wrapper"]
let lastWrapper = event.currentTarget.form.querySelector(`[data-type="wrapper"]:last-of-type`);
// get the index of the last Wrapper
let lastIndex = parseInt(lastWrapper.dataset.index);
//clone the wrapper
let clonerNode = lastWrapper.cloneNode(true);
//change index of cloner Wrapper
indexSum = clonerNode.dataset.index = parseInt(lastIndex) + 1;
//changes values;
clonerNode.querySelector(`.stock`).value = "NEW STOCK";
valuePercentage = clonerNode.querySelector(`.quota`).value = (100 / indexSum);
//append clonernode after lastWrapper
lastWrapper.insertAdjacentElement('afterend', clonerNode);
indexSumRatio = 1 / indexSum;
console.log(`This is the sum of the indices (number of stocks = indexSum) ${indexSum}`)
console.log(`This is the valuePercentage (quota) ${valuePercentage}`)
console.log(`This is the indexSumRatio (perda potencial Ratio) ${indexSumRatio}`)
}
////////////////////////////////////////////////////////////////////////////////////
//Function that will be passed by the "Draw Graph" button so that everytime the user clicks the button, it takes the new input values and updates the pie chart
function drawChart() {
let slices = []; //the amount of "slices" on the pie chart is also the number of indices added
for (let i = 1; i <= indexSum; i++) { //i <= slices.length
slices.push({
ativo: document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .stock`).value,
quota: parseFloat(document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .quota`).value)
})
}
//Calculate the Loss (Perda) - this value is essentially the total amount of loss that the assets have taken when the user changes the "Perda potencial". This value is calculated by doing quota (valuePercentage) - perda potencial (perda) * (1/amount of assets)
document.querySelector(".row").addEventListener("input", function (e) {
const tgt = e.target;
if (tgt.classList.contains("perda")) {
perda = Number(tgt.closest(".tr").querySelector(".quota").value =
valuePercentage - tgt.value * indexSumRatio)
}
});
let sum = 0.0;
for (let i = 0; i < slices.length; i++) {
sum += slices[i].quota;
};
perda = 100.0 - sum;
console.log(`This is the Total Loss (perda) ${perda}`)
document.getElementById("indexLoss").innerHTML = `Total Loss: ${perda}%`;
document.getElementsByClassName("button2").onclick = perda;
//Create data table with input data
var data = new google.visualization.DataTable();
data.addColumn("string", "Ativo");
data.addColumn("number", "Quota");
data.addRows([
...slices.map(slice => ([slice.ativo, slice.quota])), ["Loss", perda],
]);
//Styling
let options = {
'legend': 'right',
pieHole: 0.3,
pieSliceText: 'value',
is3D: true,
colors: ['#FBB117', '#6AA121', '#728FCE', '#4863A0', '#7D0552', '#FF0000'],
backgroundColor: '#DFCFBE',
chartArea: {
left: 50,
right: 50,
top: 70
}
};
var chart = new google.visualization.PieChart(
document.getElementById("piechart")
);
chart.draw(data, options);
}
//adicionar for loop para ter o valor da quota personalizada e fazer os calculos da perda consoante esse valor
////////////////////////////////////////////////////////////////////////////////////
//RESOURCES
//https://www.youtube.com/watch?v=y17RuWkWdn8&ab_channel=WebDevSimplified
// usar este video para criar uma função que faça append de um novo div com atributos semelhantes aos outros para permitir ao usuario adicionar novos ativos com o cloque de um butao
//isEmpty() -> para fazer reset dos valores ao adicionar new stock
//
//
//
//
//
</script>
<style>
{
box-sizing: content-box;
}
/* Set additional styling options for the columns */
.column {
float: left;
}
/* Set width length for the left, right and middle columns */
.left {
width: 30%;
}
.right {
width: 80%;
}
.row:after {
content: "";
display: table;
clear: both;
height: 100%;
}
.td {
display: inline-block;
width: 120px;
text-align: center;
font-family: 'Trebuchet MS', sans-serif;
}
.tr {
font-family: 'Trebuchet MS', sans-serif;
}
.button1 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.button2 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.piechart {
width: 100%;
height: 100%;
}
</style>
</body>
</html>
I've also tried using Object.keys to update all the quota key values to the latest index value, but it isn't working as expected
2nd JS with the object.keys implementation (line 125)
<!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>Document</title>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body style="background-color:#DFCFBE">
<div class="row">
<div class="column left">
<form id=" form">
<div class="tr">
<div class="td">Asset </div>
<div class="td">Quota % </div>
<div class="td">Potencial Loss % </div>
</div>
<div class="tr" data-type="wrapper" data-index="1">
<div class="td" data-type="field_div"><input type="text" size="5" value="NEW STOCK" class="stock"
onchange="drawChart();" /> </div>
<div class="td"><input type="number" min="0" max="100" value="100" class="quota"
onchange="drawChart();" /> </div>
<div class="td"><input type="number" min="0" max="100" value="0" class="perda" />
</div>
</div>
<p onload="addNumAtivo();" id="indexSum">Number of Assets: 1</p>
<p onload="addNumAtivo();" id="indexQuota">Quota per asset: </p>
<p id="indexLoss">Total Loss:</p>
<button class="button2" type="button" onclick="addStock(event), addNumAtivo();">Add
Asset</button>
<button class="button1" type="button" onclick="drawChart();">Draw Graph</button>
</form>
</div>
</div>
<div class="column right" style="position:relative;width:70%">
<div class=" piechart" id="piechart" style="position:absolute;right:0px;top:0px;width: 900; height: 590px;">
</div>
</div>
</body>
<script>
//Define variables
let perda; //This value is calculated on line 154
let indexSum = 1, indexSumRatio = 1;
let valuePercentage = 100; //This value percentage is essentially the "Quota". This value will be 100% divided by the amount of indexSum (essentialy, divided by ativo1, ativo2, ativo3... ativo(i))
//valuePercentage.toFixed(2)
//let indexSumRatio = 1;
let numQuota = 100;
let numAtivo = 1;
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
//Functions that will increase the number of Assets and change the quota % as the user adds new assets (Add Stock) on the HTML
function addNumAtivo() {
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
}
document.getElementsByClassName("button2").onclick = numAtivo;
document.getElementsByClassName("button2").onclick = valuePercentage;
////////////////////////////////////////////////////////////////////////////////////
//Load google chart graphs
google.charts.load("current", {
packages: ["corechart"]
});
google.charts.setOnLoadCallback(drawChart);
// Function that will add new "Ativo", a new "Quota%" and a new "Perda potencial%" as the user presses the button "Add Stock".
function addStock(event) {
// find the last Wrapper [data-set="wrapper"]
let lastWrapper = event.currentTarget.form.querySelector(`[data-type="wrapper"]:last-of-type`);
// get the index of the last Wrapper
let lastIndex = parseInt(lastWrapper.dataset.index);
//clone the wrapper
let clonerNode = lastWrapper.cloneNode(true);
//change index of cloner Wrapper
indexSum = clonerNode.dataset.index = parseInt(lastIndex) + 1;
//changes values;
clonerNode.querySelector(`.stock`).value = "NEW STOCK";
valuePercentage = clonerNode.querySelector(`.quota`).value = (100 / indexSum);
//append clonernode after lastWrapper
lastWrapper.insertAdjacentElement('afterend', clonerNode);
indexSumRatio = 1 / indexSum;
console.log(`This is the sum of the indices (number of stocks = indexSum) ${indexSum}`)
console.log(`This is the valuePercentage (quota) ${valuePercentage}`)
console.log(`This is the indexSumRatio (perda potencial Ratio) ${indexSumRatio}`)
//colocar algo que faça reset aos valores
}
////////////////////////////////////////////////////////////////////////////////////
//Function that will be passed by the "Draw Graph" button so that everytime the user clicks the button, it takes the new input values and updates the pie chart
function drawChart() {
let ativo;
let quota;
let updateQuota;
let slices = []; //the amount of "slices" on the pie chart is also the number of indices added
for (let i = 1; i <= indexSum; i++) { //i <= slices.length
ativo = document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .stock`).value;
quota = parseFloat(document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .quota`).value);
slices.push({
ativo: ativo,
quota: quota
})
//updateQuota = slices[slices.length - 1]
}
Object.keys(slices).forEach(quota => { slices[quota] = slices[slices.length - 1] });
console.log(slices)
// //get last index - video 39 JS
//console.log(friends[friends.length-1])
//https://www.udemy.com/course/the-complete-javascript-course/learn/lecture/22648249#overview
// for (let j = 1; j <= indexSum - 1; j++) {
// slices.push({
// ativo: ativo,
// quota: quota
// })
// }
//Calculate the Loss (Perda) - this value is essentially the total amount of loss that the assets have taken when the user changes the "Perda potencial". This value is calculated by doing quota (valuePercentage) - perda potencial (perda) * (1/amount of assets)
document.querySelector(".row").addEventListener("input", function (e) {
const tgt = e.target;
if (tgt.classList.contains("perda")) {
perda = Number(tgt.closest(".tr").querySelector(".quota").value =
valuePercentage - tgt.value * indexSumRatio)
}
});
let sum = 0.0;
for (let i = 0; i < slices.length; i++) {
sum += slices[i].quota;
};
perda = Math.round(100.0 - sum);
console.log(`This is the Total Loss (perda) ${perda}`)
document.getElementById("indexLoss").innerHTML = `Total Loss: ${perda}%`;
document.getElementsByClassName("button2").onclick = perda;
//Create data table with input data
var data = new google.visualization.DataTable();
data.addColumn("string", "Ativo");
data.addColumn("number", "Quota");
data.addRows([
...slices.map(slice => ([slice.ativo, slice.quota])), ["Loss", perda],
]);
//Styling
let options = {
'legend': 'right',
pieHole: 0.3,
pieSliceText: 'value',
is3D: true,
colors: ['#FBB117', '#6AA121', '#728FCE', '#4863A0', '#7D0552', '#FF0000'],
backgroundColor: '#DFCFBE',
chartArea: {
left: 50,
right: 50,
top: 70
}
};
var chart = new google.visualization.PieChart(
document.getElementById("piechart")
);
chart.draw(data, options);
}
//adicionar for loop para ter o valor da quota personalizada e fazer os calculos da perda consoante esse valor
////////////////////////////////////////////////////////////////////////////////////
//RESOURCES
//https://www.youtube.com/watch?v=y17RuWkWdn8&ab_channel=WebDevSimplified
// usar este video para criar uma função que faça append de um novo div com atributos semelhantes aos outros para permitir ao usuario adicionar novos ativos com o cloque de um butao
//isEmpty() -> para fazer reset dos valores ao adicionar new stock
//
//
//
//
//
</script>
<style>
{
box-sizing: content-box;
}
/* Set additional styling options for the columns */
.column {
float: left;
}
/* Set width length for the left, right and middle columns */
.left {
width: 30%;
}
.right {
width: 80%;
}
.row:after {
content: "";
display: table;
clear: both;
height: 100%;
}
.td {
display: inline-block;
width: 120px;
text-align: center;
font-family: 'Trebuchet MS', sans-serif;
}
.tr {
font-family: 'Trebuchet MS', sans-serif;
}
.button1 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.button2 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.piechart {
width: 100%;
height: 100%;
}
</style>
</body>
</html>
Alright I'll take a shot at this one. You do have a lot of complex selectors that could probably be reduced and data should be contained in some sort of object or array instead of relaying on the UI elements for data.
With that aside I think you just need to loop through each stock's quota input and update it to whatever the valuePercentage is you calculate.
This can be done with
document.querySelectorAll('.quota').forEach(function(quota) {
quota.value = valuePercentage;
});
I also removed the ["Loss", perda] piece from the data.addRows since it eventually goes negative and you can't pie chart negative data.
//Define variables
let perda; //This value is calculated on line 154
let indexSum = 1;
let valuePercentage = 100; //This value percentage is essentially the "Quota". This value will be 100% divided by the amount of indexSum (essentialy, divided by ativo1, ativo2, ativo3... ativo(i))
let indexSumRatio = 1;
let numQuota = 100;
let numAtivo = 1;
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
//Functions that will increase the number of Assets and change the quota % as the user adds new assets (Add Stock) on the HTML
function addNumAtivo() {
document.getElementById("indexSum").innerHTML = `Number of Assets: ${numAtivo++}`;
document.getElementById("indexQuota").innerHTML = `Quota per asset: ${valuePercentage}%`;
}
document.getElementsByClassName("button2").onclick = numAtivo;
document.getElementsByClassName("button2").onclick = valuePercentage;
////////////////////////////////////////////////////////////////////////////////////
//Load google chart graphs
google.charts.load("current", {
packages: ["corechart"]
});
google.charts.setOnLoadCallback(drawChart);
// Function that will add new "Ativo", a new "Quota%" and a new "Perda potencial%" as the user presses the button "Add Stock".
function addStock(event) {
// find the last Wrapper [data-set="wrapper"]
let lastWrapper = event.currentTarget.form.querySelector(`[data-type="wrapper"]:last-of-type`);
// get the index of the last Wrapper
let lastIndex = parseInt(lastWrapper.dataset.index);
//clone the wrapper
let clonerNode = lastWrapper.cloneNode(true);
//change index of cloner Wrapper
indexSum = clonerNode.dataset.index = parseInt(lastIndex) + 1;
//changes values;
clonerNode.querySelector(`.stock`).value = "NEW STOCK";
valuePercentage = clonerNode.querySelector(`.quota`).value = (100 / indexSum);
//append clonernode after lastWrapper
lastWrapper.insertAdjacentElement('afterend', clonerNode);
indexSumRatio = 1 / indexSum;
document.querySelectorAll('.quota').forEach(function(quota) {
quota.value = valuePercentage;
});
//console.log(`This is the sum of the indices (number of stocks = indexSum) ${indexSum}`)
//console.log(`This is the valuePercentage (quota) ${valuePercentage}`)
// console.log(`This is the indexSumRatio (perda potencial Ratio) ${indexSumRatio}`)
}
////////////////////////////////////////////////////////////////////////////////////
//Function that will be passed by the "Draw Graph" button so that everytime the user clicks the button, it takes the new input values and updates the pie chart
function drawChart() {
let slices = []; //the amount of "slices" on the pie chart is also the number of indices added
for (let i = 1; i <= indexSum; i++) { //i <= slices.length
slices.push({
ativo: document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .stock`).value,
quota: parseFloat(document.querySelector(`div [data-type="wrapper"][data-index="${i}"] .quota`).value)
})
}
//Calculate the Loss (Perda) - this value is essentially the total amount of loss that the assets have taken when the user changes the "Perda potencial". This value is calculated by doing quota (valuePercentage) - perda potencial (perda) * (1/amount of assets)
document.querySelector(".row").addEventListener("input", function(e) {
const tgt = e.target;
if (tgt.classList.contains("perda")) {
perda = Number(tgt.closest(".tr").querySelector(".quota").value =
valuePercentage - tgt.value * indexSumRatio)
}
});
let sum = 0.0;
for (let i = 0; i < slices.length; i++) {
sum += slices[i].quota;
};
perda = 100.0 - sum;
console.log(`This is the Total Loss (perda) ${perda}`)
document.getElementById("indexLoss").innerHTML = `Total Loss: ${perda}%`;
document.getElementsByClassName("button2").onclick = perda;
//Create data table with input data
var data = new google.visualization.DataTable();
data.addColumn("string", "Ativo");
data.addColumn("number", "Quota");
data.addRows([
...slices.map(slice => ([slice.ativo, slice.quota])),
]);
//Styling
let options = {
'legend': 'right',
pieHole: 0.3,
pieSliceText: 'value',
is3D: true,
colors: ['#FBB117', '#6AA121', '#728FCE', '#4863A0', '#7D0552', '#FF0000'],
backgroundColor: '#DFCFBE',
chartArea: {
left: 50,
right: 50,
top: 70
}
};
var chart = new google.visualization.PieChart(
document.getElementById("piechart")
);
chart.draw(data, options);
}
{
box-sizing: content-box;
}
/* Set additional styling options for the columns */
.column {
float: left;
}
/* Set width length for the left, right and middle columns */
.left {
width: 30%;
}
.right {
width: 80%;
}
.row:after {
content: "";
display: table;
clear: both;
height: 100%;
}
.td {
display: inline-block;
width: 120px;
text-align: center;
font-family: 'Trebuchet MS', sans-serif;
}
.tr {
font-family: 'Trebuchet MS', sans-serif;
}
.button1 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.button2 {
width: 20%;
margin-left: 35%;
margin-right: 25%;
margin-top: 10px;
font-family: 'Trebuchet MS', sans-serif;
}
.piechart {
width: 100%;
height: 100%;
}
<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>Document</title>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body style="background-color:#DFCFBE">
<div class="row">
<div class="column left">
<form id=" form">
<div class="tr">
<div class="td">Asset </div>
<div class="td">Quota % </div>
<div class="td">Potencial Loss % </div>
</div>
<div class="tr" data-type="wrapper" data-index="1">
<div class="td" data-type="field_div">
<input type="text" size="5" value="NEW STOCK" class="stock" onchange="drawChart();" />
</div>
<div class="td">
<input type="number" min="0" max="100" value="100" class="quota" onchange="drawChart();" />
</div>
<div class="td">
<input type="number" min="0" max="100" value="0" class="perda" />
</div>
</div>
<p onload="addNumAtivo();" id="indexSum">Number of Assets: 1</p>
<p onload="addNumAtivo();" id="indexQuota">Quota per asset: </p>
<p id="indexLoss">Total Loss:</p>
<button class="button2" type="button" onclick="addStock(event), addNumAtivo();">Add
Asset</button>
<button class="button1" type="button" onclick="drawChart();">Draw Graph</button>
</form>
</div>
</div>
<div class="column right" style="position:relative;width:70%">
<div class=" piechart" id="piechart" style="position:absolute;right:0px;top:0px;width: 900; height: 590px;">
</div>
</div>
</body>

JS Image Slider with specific text to each image

I've followed a tutorial about JS image sliders. I'm trying to have a text box display on each image (figured that out) but I need the text to be specific for each image. The images being grabbed from an img folder and are in order (image-0, image-1, etc). I'm guessing I'll need some array but I can't figure out how to do this in JS and have the corresponding text display on each correct image. Code provided. Any help?
HTML
<body>
<div class="images">
<div id="btns">
<button type="button" class="btn prevBtn">↩</button>
<button type="button" class="btn nextBtn">↪</button>
</div>
<div id="textBlock">
<h4>This is the image</h4>
</div>
</div>
<script src="script.js"></script>
</body>
JS
const nextBtn = document.querySelector(".nextBtn");
const prevBtn = document.querySelector(".prevBtn");
const container = document.querySelector(".images");
let counter = 0;
nextBtn.addEventListener("click",nextSlide);
prevBtn.addEventListener("click",prevSlide);
function nextSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 4){
counter = -1;
}
counter++;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
function prevSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 0){
counter = 5;
}
counter--;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
Since you counter is indexed 0 and goes up to 𝑛 all you need is an array:
const descriptions = [
"A nice walk in the park", // for the image counter 0
"My dog and me", // for the image counter 1
// etc.
];
than all you need to do is:
textBlock.textContent = descriptions[counter];
But...
I don't know where you found that toturial but it's a really a great example on how not to build a gallery. The animation is odd, it's overly simplistic and cannot account for multiple galleries. It's repetitive and unmodular. And the total number of slides should never be hardcoded, that's why we use a programming language after all. And yes, it can count the number of items using .length.
Code should be reusable:
class Gallery {
constructor(id, slides) {
this.slides = slides || [];
this.total = this.slides.length;
this.curr = 0;
this.EL = document.querySelector(id);
this.EL_area = this.EL.querySelector(".Gallery-area");
this.EL_prev = this.EL.querySelector(".Gallery-prev");
this.EL_next = this.EL.querySelector(".Gallery-next");
this.EL_desc = this.EL.querySelector(".Gallery-desc");
const NewEL = (tag, prop) => Object.assign(document.createElement(tag), prop);
// Preload images
this.ELs_items = this.slides.reduce((DF, item) => (DF.push(NewEL("img", item)), DF), []);
this.EL_area.append(...this.ELs_items);
// Events
this.EL_prev.addEventListener("click", () => this.prev());
this.EL_next.addEventListener("click", () => this.next());
// Init
this.anim();
}
// Methods:
anim() {
this.curr = this.curr < 0 ? this.total - 1 : this.curr >= this.total ? 0 : this.curr;
this.ELs_items.forEach((EL, i) => EL.classList.toggle("is-active", i === this.curr));
this.EL_desc.textContent = this.slides[this.curr].alt;
}
prev() {
this.curr -= 1;
this.anim();
}
next() {
this.curr += 1;
this.anim();
}
}
// Use like:
new Gallery("#gallery-one", [
{alt: "My fluffy dog and me", src: "https://picsum.photos/400/300"},
{alt: "Here, we seem happy!", src: "https://picsum.photos/300/300"},
{alt: "We are making pizza?", src: "https://picsum.photos/600/300"},
]);
.Gallery {
position: relative;
height: 300px;
max-height: 100vh;
}
.Gallery-area > * {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s;
opacity: 0;
}
.Gallery-area > *.is-active {
opacity: 1;
}
.Gallery-btns {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.Gallery-desc {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
font-size: 3em;
}
<div class="Gallery" id="gallery-one">
<div class="Gallery-area"></div>
<div class="Gallery-btns">
<button type="button" class="btn Gallery-prev">←</button>
<button type="button" class="btn Gallery-next">→</button>
</div>
<div class="Gallery-desc"></div>
</div>

Whack a mole game with jQuery

I am working on a whack-a-mole game for a school assignment and I can't get it to work.
The full code can be found on jsfiddle (https://jsfiddle.net/Lc30u5h7/5/) to make this question shorter.
Whenever I load the code my "moles" (black square divs) disappear. By turning parts of my code in to comments I nailed down the error to this part of the code:
function showMole(tile) {
if (gameRunning) {
$(this).css({"background-color": "green"});
$(tile).data('mole', true);
randomInt(400, 1200) {hideMole(this)};
};
};
function hideMole(tile) {
$(tile).css({"background-color": "black"});
$(tile).data('mole', false);
randomInt(4000,48000) {showMole(this)};
};
More specifically the error is located witht the function randomInt() which uses the following code:
function randomInt(min, max){
return Math.ceil(Math.random() * (max - min) ) + min;
};
The functions are supposed to represent the different states of the moles. After a random interval a mole will switch from hideMole() to showMole() and it will turn green and reward a point if clicked (which is handled by another piece of code).
I appreciate any help I can get.
I have tried to get you started with some example code. This code uses a handful of times to help manage the game.
var game = function() {
var gameBoard_Selector = "#gameBoard";
var tile_Selector = ".tile";
var mole_ClassName = "mole";
var $gameBoard = $(gameBoard_Selector);
var $gameTiles = $gameBoard.find(tile_Selector);
var gameTime = 20 * 1000;
var turnTime = 1000;
var moleInterval;
var moleLifeMin = 1000;
var moleLifeMax = 3 * 1000;
var gameScore = 0;
var getRandomIntInclusive = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// ------------------------
// Add a click handler to the board
// This could be added and removed at start and end as an alternative.
// or added to each individual tile if you liked that strategy.
// ------------------------
$gameBoard.on("click", ".tile", function() {
var $this = $(this);
// -------------------------
// if this is not a mole do nothing...
// -------------------------
if (!$this.hasClass(mole_ClassName)) { return; }
// -------------------------
// -------------------------
// "hide" this mole and increment the score
// -------------------------
$this.removeClass(mole_ClassName);
gameScore += 1;
// -------------------------
});
// ------------------------
var startGame = function() {
gameScore = 0;
// -------------------------
// Every turnTime, spawn a new mole.
// Record moleInterval so we can cancel it when the game ends.
// -------------------------
moleInterval = setInterval(spawnMole, turnTime);
// -------------------------
// -------------------------
// The game ends in gameTime
// -------------------------
setTimeout(endGame, gameTime);
// -------------------------
}
var endGame = function() {
// -------------------------
// Stop spawning new moles
// -------------------------
clearInterval(moleInterval);
// -------------------------
// -------------------------
// "hide" any existing moles.
// -------------------------
$gameTiles.removeClass(mole_ClassName);
// -------------------------
alert("Game Over! Score: " + gameScore);
}
var spawnMole = function(timeToLive) {
// -------------------------
// Select a random tile to set as a mole.
// You might adjust to only spawn moles where there are none already.
// -------------------------
var $targetTile = $($gameTiles[getRandomIntInclusive(0, $gameTiles.length - 1)]);
// -------------------------
// -------------------------
// Moles shall live for a random amount of time
// -------------------------
var timeToLive = getRandomIntInclusive(moleLifeMin , moleLifeMax);
// -------------------------
// -------------------------
// "show" the mole
// -------------------------
$targetTile.addClass(mole_ClassName);
// -------------------------
// -------------------------
// after timeToLive, automatically "hide" the mole
// -------------------------
setTimeout(function() { $targetTile.removeClass(mole_ClassName); }, timeToLive);
// -------------------------
}
return {
startGame
};
}();
game.startGame();
#gameBoard {
margin: auto;
margin-top: 75px;
width: 250px;
height: 250px;
}
#gameBoard .tile {
background-color: black;
height: 20%;
width: 20%;
margin: 2px;
display: inline-block;
}
#gameBoard .tile.mole {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="gameBoard">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
I created my own version of this game to help you out.
Breakdown
Start by creating variables to store the user's score, level and how many lives they have. Also create a boolean variable for whether or not the game is in progress. This prevents accidental re-clicks of the start game button.
We will be incrementing the score quite a bit, so it is best to create a re-usable function for that. In my example, this is the displayScore() function. In my game, as you gain points, you will level up, so the view needs to be updated to relect that. That's where the levelUp() function comes in handy.
In the game board there are 16 cells, arranged in a 4 x 4 matrix. We can query the collection of all the cells with document.querySelectorAll(".cell"). To make the game fun, it's better not to know which cell will pop-up. That's why a cell will randomly light up in the randomCell() function.
Throughout the game, there are multiple times when the script needs to check if the player has lost. Because of that, I created a reusable gameOver() function that counts the player's lives. If the player has no lives remaining, the interval will be cleared, the playing status will be reset to not playing and the variables will be reset.
Try it out here.
var score = 0;
var level = 1;
var lives = 5;
var playing = false;
var start = document.getElementById("start");
var scoreDisplay = document.getElementById("score-display");
var cells = document.querySelectorAll(".cell");
function displayScore() {
levelUp();
scoreDisplay.innerHTML = "Score: " + score + "<span id='level-display'> Level: " + level + "</span><span id='lifes-display'> Lives: " + lives + "</span>";
}
function levelUp() {
level = Math.max(Math.floor(score / 10), 1);
}
function randomCell() {
return Math.floor(Math.random() * 16);
}
function gameOver() {
if (lives === 0) {
clearInterval(getCells);
score = 0;
level = 1;
lives = 5;
playing = false;
}
}
function highlightCell() {
var target = randomCell();
var prevScore = score;
cells[target].style.background = "green";
setTimeout(function() {
cells[target].style.background = "red";
if (score === prevScore) {
lives--;
displayScore();
gameOver();
}
}, 1000)
}
start.addEventListener("click", function() {
if (!playing) {
playing = true;
displayScore();
getCells = setInterval(function() {
highlightCell();
}, 1500);
}
});
for (var i = 0; i < cells.length; i++) {
cells[i].addEventListener("click", function() {
if (playing) {
var cell = this;
if (this.style.background === "green") {
score++;
}
else {
lives--;
gameOver();
}
displayScore();
}
})
}
#game-board {
height: 330px;
width: 330px;
margin: 0 auto;
border: 1px solid black;
}
.cell {
display: inline-block;
width: 21%;
margin: 4px;
height: 21%;
border: 1px solid black;
background: red;
}
#game-info {
height: 40px;
width: 330px;
margin: 0 auto;
background: lightblue;
}
#level-display, #lifes-display {
margin-left: 30px;
}
#start {
margin: 10px 37%;
}
<div id="game-info">
<p id="score-display"></p>
</div>
<div id="game-board">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
<button id="start">Start Game</button>

HTML5 audio clicking the progress bar to move to a different time

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var counter = 0;
var numOfTracks = $(".audio-player").length;
$("#play-bt").click(function(){
$(".audio-player")[counter].play();
$("#message").text("Music started");
})
$("#pause-bt").click(function(){
$(".audio-player")[counter].pause();
$("#message").text("Music paused");
})
$("#stop-bt").click(function(){
$(".audio-player")[counter].pause();
$(".audio-player")[counter].currentTime = 0;
$("#message").text("Music Stopped");
})
$("#next-bt").click(function(){
$(".audio-player")[counter].pause();
$(".audio-player")[counter].currentTime = 0;
counter++;
if(counter > numOfTracks - 1){
counter = 0 ;
}
$(".audio-player")[counter].play();
$("#message").text("Next Track started");
})
$("#prev-bt").click(function(){
$(".audio-player")[counter].pause();
$(".audio-player")[counter].currentTime = 0;
counter--;
$(".audio-player")[counter].play();
$("#message").text("Previous Track");
})
$(".audio-player").bind('timeupdate', function(){
//Gets the whole duration of the track.
//No idea kung saan ko ilalagay sa UI**IMPLEMENT LATER**
var track_length = $(".audio-player")[counter].duration;
var secs = $(".audio-player")[counter].currentTime;
var progress = (secs/track_length) * 100;
$('#progressbar').css({'width' : progress * 2});
//Will Use these later on production
//NOTE DO NOT DELETE
//Track Minutes
var tcMins = parseInt(secs/60);
//Track Seconds
var tcSecs = parseInt(secs - (tcMins * 60));
if (tcSecs < 10) { tcSecs = '0' + tcSecs; }
// Display the time. REMEMBER
$('#timecode').html(tcMins + ':' + tcSecs);
})
})
</script>
<style>
/*Seperate this some time in the development*/
#playerContainer{
background-color: #A8A8A8 ;
width: 260px;
height: 55px;
padding: 8px;
border: 1px solid #d0d0d0;
}
/* Player Controls */
/*list items of controls */
#playerControls li {
display: block;
width: 32px;
height: 32px;
padding: 0px;
float: left;
cursor: pointer;
}
#playerControls { list-style: none; padding: 0px; margin: 0px;}
/*Images for each li items items */
#play-bt { background: url('icons/glyphicons_173_play.png'); background-repeat:no-repeat }
#pause-bt {background: url('icons/glyphicons_174_pause.png'); background-repeat:no-repeat;}
#next-bt { background: url('icons/glyphicons_176_forward.png'); background-repeat:no-repeat}
#prev-bt {background: url('icons/glyphicons_172_rewind.png'); background-repeat:no-repeat;}
/*Progress Stuff*/
/*Remember to manipulate its width via javascript later*/
#progressContainer
{
background-color:#e0e0e0;
height: 14px;
width: 256px;
float: left;
margin-left: 0px;
}
#progressbar {background-color: #1384bb; height:14px; width:0%; }
</style>
</head>
<body>
<audio class ="audio-player" name= "audio-player" src="04-zedd-stars_come_out_(terravita_remix).ogg" >
<p>Sorry your file doesn't support html5</p>
</audio>
<!--Second Track For Testing Purposes-->
<audio class ="audio-player" name= "audio-player" src="01-hard_rock_sofa-quasar.mp3" ></audio>
<div id="message"></div>
<div id = "playerContainer">
<ul id = "playerControls" >
<li id = "prev-bt"></li>
<li id= "play-bt"></li>
<li id= "pause-bt"></li>
<li id = "next-bt"></li>
<li id= "stop-bt" ></li>
<li><span id ="timecode"></span></li>
</ul>
<div id="progressContainer"><!-- Progess bars container //-->
<div id="progressbar"></div>
</div>
</div>
</div>
</body>
</html>
How can I click a certain part of my progress bar so that I can move it to a different time in the track? I have no idea on how I am going to do that. any ideas ?
Given some html that looks like this:
<div class="container">
<video class="video">
<source></source>
</video>
<progress min="0" max="100" value="0"></progress>
<div class="controls"></div>
</div>
In order to seek to a specific time in the video as a result of a click event the js would look like this:
var player = document.querySelector("video");
var progressBar = document.querySelector("progress");
progressBar.addEventListener("click", seek);
function seek(e) {
var percent = e.offsetX / this.offsetWidth;
player.currentTime = percent * player.duration;
progressBar.value = percent / 100;
}
However, this doesn't address how to seek on a click/drag (like most video players do). include script
I came across this question today because I am creating a custom HTML5 video player and had the same question. Just in regards to video instead of audio. The process should work the same though.
I found this article and was able to incorporate the progress bar part of it into my player. https://msdn.microsoft.com/en-us/library/ie/gg589528%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
Instead of using a progressbar element, like I was doing, or a div element, like you're doing, the trick here is to use a canvas element instead.
<canvas id='progress-bar' width="200" height="20" style="border:1px solid green;">canvas not supported</canvas>
Then in your JavaScript, create a handle to reference it by
var mediaPlayer;
var progressBar;
var canvas;
When the document loads, initialize everything including the progress bar items
mediaPlayer = document.getElementById('media-video');
progressBar = document.getElementById('progress-bar');
canvas = document.getElementById('progress-bar');
canvas.addEventListener("click", function(e) {
var canvas = document.getElementById('progress-bar');
if (!e) {
e = window.event;
} //get the latest windows event if it isn't set
try {
//calculate the current time based on position of mouse cursor in canvas box
mediaPlayer.currentTime = mediaPlayer.duration * (e.offsetX / canvas.clientWidth);
}
catch (err) {
// Fail silently but show in F12 developer tools console
if (window.console && console.error("Error:" + err));
}
}, true);
mediaPlayer.addEventListener('timeupdate', updateProgressBar, false);
Then create a function outside of your initialization function for the timeupdate listener to call and automatically update the progress bar for you
function updateProgressBar() {
mediaPlayer = document.getElementById('media-video');
//get current time in seconds
var elapsedTime = Math.round(mediaPlayer.currentTime);
//update the progress bar
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//clear canvas before painting
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
ctx.fillStyle = "rgb(255,0,0)";
var fWidth = (elapsedTime / mediaPlayer.duration) * (canvas.clientWidth);
if (fWidth > 0) {
ctx.fillRect(0, 0, fWidth, canvas.clientHeight);
}
}
}
I haven't completely cleaned it up yet. Hence the redundant handles to the same id. But I'm sure you get the picture.
Hope this can save someone some time. If you're trying to set this up in an Angular app, you'll notice this is your controller context. So you'll need to use e.srcElement.clientWidth for this to work.
vm.setPosition = function(e){
var percent = ((e.offsetX / e.srcElement.clientWidth));
vm.songObject.setProgress(percent);
}
I wrote about that in Working with HTML5 multimedia components – Part 3: Custom controls, scroll down to "Adding a progress bar".

Categories