I wanted to make an effect similar to "click and hold" of this page, but with some changes, with svg forms, the point is that I did two functions that do what I wanted to do very well, but at the moment I introduced another svg form, the Data of the effect is transferred to the other, affecting the execution of the functions, the question is, how do I prevent this from happening?
Note: The best way to see what is happening is to let one of the two complete.
Here is an example of what I have programmed
of course I leave you all the code that I have been working
//Efect Drivers
class EffectValues {
constructor(count, time, initOffset, label) {
this.count = count;
this.time = time;
this.initOffset = initOffset;
this.label = label;
}
}
//Controlers
let counter; //it will be interval controller
let globalCount = 0;
//Call objects DOM
const loader = document.querySelector('.loader');
const circle = document.querySelector('.circle');
const svgText = document.querySelector('.svgText');
const textSvg = document.querySelector('.textSvg');
//Preloader svg
const startCircle = new EffectValues(0, 3, 1300, circle);
const showEffect = new EffectValues(0, 3, 500, svgText);
//Mouse events
// Circle
loader.addEventListener('mousedown', e => {
increase(e, startCircle);
});
loader.addEventListener('mouseup', e => {
decrease(e, startCircle);
});
// Text SVG
textSvg.addEventListener('mousedown', e => {
increase(e, showEffect);
});
textSvg.addEventListener('mouseup', e => {
decrease(e, showEffect);
});
//main functions
const increase = (e, { count, initOffset, time, label }) => {
let flag = true;
// console.log(flag);
clearInterval(counter);
while (e.type == 'mousedown') {
counter = setInterval(() => {
if (globalCount < initOffset) {
count = initOffset - globalCount;
label.style.strokeDashoffset = count;
globalCount++;
}
}, time);
break;
}
return flag;
};
const decrease = (e, { count, initOffset, time, label }) => {
let flag = true;
// console.log(flag);
clearInterval(counter);
while (e.type == 'mouseup') {
counter = setInterval(() => {
if (globalCount >= 0 && globalCount < initOffset) {
count = -globalCount + initOffset;
label.style.strokeDashoffset = count;
globalCount--;
} else {
flag = false;
}
}, time);
break;
}
return flag;
};
:root {
--dark: #2f3640;
--dark-light: #353b48;
--blue: #192a56;
--blue-dark: #273c75;
--cian: #0097e6;
--cian-light: #00a8ff;
--orange: #c23616;
--orange-light: #e84118;
}
* {
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
background-color: var(--dark);
display: flex;
justify-content: center;
align-content: center;
}
.loader {
position: relative;
width: 50%;
height: 100vh;
}
.loader svg {
position: absolute;
width: 550px;
height: 550px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader svg circle {
width: 100%;
height: 100%;
fill: none;
stroke-width: 10;
stroke: var(--cian);
stroke-linecap: round;
transform: translate(5px, 5px);
stroke-dasharray: 1300;
stroke-dashoffset: 1300;
}
.textSvg {
position: relative;
width: 40%;
}
.textSvg svg text {
stroke: var(--orange-light);
fill: none;
stroke-width: 3;
stroke-dasharray: 500;
stroke-dashoffset: 500;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="styles.css" />
<title>Loader</title>
</head>
<body>
<div class="loader">
<svg>
<circle class="circle" cx="200" cy="200" r="200"></circle>
</svg>
</div>
<div class="textSvg">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1413"
height="274"
viewBox="0 0 1413 274"
>
<text
class="svgText"
transform="translate(0 198)"
fill="#c6e0ee"
font-size="100"
font-family="MonotypeCorsiva, Monotype Corsiva"
>
<tspan x="0" y="0">David Figueroa</tspan>
</text>
</svg>
</div>
</body>
<script src="main.js" defer></script>
</html>
I have been looking for information but nothing has helped me.
Beforehand thank you very much
You can implement your requirements by moving global variables and functions inside the class.
Codepen here
//Efect Drivers
class EffectValues {
constructor(time, initOffset, label) {
this.time = time;
this.initOffset = initOffset;
this.label = label;
this.counter;
this.globalCount = 0;
}
increase(e) {
let flag = true;
// console.log(flag);
clearInterval(this.counter);
while (e.type == 'mousedown') {
this.counter = setInterval(() => {
if (this.globalCount < this.initOffset) {
const count = this.initOffset - this.globalCount;
this.label.style.strokeDashoffset = count;
this.globalCount++;
}
}, this.time);
break;
}
return flag;
};
decrease(e) {
let flag = true;
// console.log(flag);
clearInterval(this.counter);
while (e.type == 'mouseup') {
this.counter = setInterval(() => {
if (this.globalCount >= 0 && this.globalCount < this.initOffset) {
const count = -this.globalCount + this.initOffset;
this.label.style.strokeDashoffset = count;
this.globalCount--;
} else {
flag = false;
}
}, this.time);
break;
}
return flag;
};
}
//Call objects DOM
const loader = document.querySelector('.loader');
const circle = document.querySelector('.circle');
const svgText = document.querySelector('.svgText');
const textSvg = document.querySelector('.textSvg');
//Preloader svg
const startCircle = new EffectValues(3, 1300, circle);
const letterEffect = new EffectValues(3, 500, svgText);
//Mouse events
// Circle
loader.addEventListener('mousedown', e => {
startCircle.increase(e);
});
loader.addEventListener('mouseup', e => {
startCircle.decrease(e);
});
// Text SVG
textSvg.addEventListener('mousedown', e => {
letterEffect.increase(e);
});
textSvg.addEventListener('mouseup', e => {
letterEffect.decrease(e);
});
Related
I am playing around with intersection observer to create an infinite scroll dog website. As you scroll and 6 dogs appear, an api fires off 6 more times to grab more dogs to add to the DOM. I would like for the dogs to load in as a user scrolls but as an already viewed dog leaves the viewport and goes up on the page, that element is then deleted off the page. SO the dogs always load in scrolling down, but scrolling up you are always at the top of the page. My current implementation in the function called lastFunc is causing it to act really weird. How can I achieve the desired effect.
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
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>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
When deleting elements, the entire contents of the container are shifted and observers start to fire. In order for observers not to be triggered when deleting an element, it is necessary to shift the scroll just before deleting the element to the height of this element.
Example below:
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.add('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
const scrollTop = this.$cardContainer.scrollTop;
const height = item.offsetHeight;
this.$cardContainer.scrollTo(0, Math.max(0, scrollTop - height));
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
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>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
I hope this will help you somehow.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<style>
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
width: 48vw;
margin: 1%;
}
.card.show {
// opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
.card-container{
border: solid 1px #00f;
padding: 20px;
overflow-y:scroll;
}
</style>
<style>
#sentinel{
height:0px;
}
</style>
<h1>Dog Random Images</h1>
<div class="card-container">
<div id="sentinel"></div>
</div>
<script>
/* Question on https://stackoverflow.com/questions/70482606/delete-elements-that-have-intersected-the-viewport */
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.mysentinel = document.querySelector('#sentinel');
this.observer = new IntersectionObserver(
(entries) => {
let [entry] = entries; //destructure array, get first entry - should only be 1 - sentinel
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
this.loadNewCards();
}
}, {
threshold: 1,
rootMargin: '150px' /*expanded root/viewport(due to null) by 150px*/,
}
);
this.loadNewCards();
} // end constructor;
cacheDOMElements() {
//The Document method querySelectorAll() returns a static (not live) NodeList
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
/* https://stackoverflow.com/questions/31710768/how-can-i-fetch-an-array-of-urls-with-promise-all , from peirix*/
this.mypromises = [];
this.mymessages = [];
this.urls = new Array(6).fill("https://dog.ceo/api/breeds/image/random", 0, 6);
//create array of promises
var promises = this.urls.map(url => fetch(url).then(y => y.json()));
//Promise.all() method takes an iterable of promises
//promise.all returns a single Promise that resolves to an array of the results of the input promises
Promise.all(promises)
.then(results => {
//accumulate all the urls from message property
results.forEach(v => this.mymessages.push(v.message));
})
.finally(() => {
let idx = 0;
for (let message of this.mymessages) {
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.setAttribute('src', message);
imageElement.setAttribute('title', `${idx++}:${message}`);
card.appendChild(imageElement);
this.$cardContainer.appendChild(card);
}// end for
this.cacheDOMElements();
//stop this sentinel possibly hitting the observer to loadnewcards as we (re)move cards
this.observer.unobserve(this.mysentinel);
//if number of cards is>12 then takeoff the first 6
if (this.$allCards.length > 12) {
for (let i = 0; i < 6; i++) {
this.$allCards[i].remove();
}
}
//already exists so move it to bottom of container div
this.$cardContainer.appendChild(this.mysentinel);
/*this should be outside the root so when it invokes observer it will not fire loadnewcards*/
this.observer.observe(this.mysentinel);
}); //end of finally end of Promise.all
} //end loadnewcards
} //class CardGenerator
const cardGenerator = new CardGenerator();
</script>
</body>
</html>
I am trying to create an "Etch-A-Sketch"-program, which should let the user draw only by clicking or holding the mouse-button. How can I realize that in JavaScript?
Somehow after the user chooses a color via clicking on the color-button, the color is already drawn as soon as the mouse cursor enters the drawing area (div class="container").
I've tried several functions, but it's still not working as expected...
Could someone please provide a hint?
"use strict";
const divContainer = document.querySelector(".container");
const btnsContainer = document.querySelector(".buttons");
const btnBlack = document.createElement("button");
const btnGreyScale = document.createElement("button");
const btnRgb = document.createElement("button");
const btnErase = document.createElement("button");
const btnShake = document.createElement("button");
function createGrid(col, rows) {
for(let i = 0; i < (col * rows); i++) {
const div = document.createElement("div");
divContainer.style.gridTemplateColumns = `repeat(${col}, 1fr)`;
divContainer.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
divContainer.appendChild(div).classList.add("box");
}
}
createGrid(16,16)
let isDrawing = false;
window.addEventListener("mousedown", () => {
isDrawing = true;
});
window.addEventListener("mouseup", () => {
isDrawing = false;
});
function paintBlack() {
const boxes = divContainer.querySelectorAll(".box");
btnBlack.textContent = "Black";
btnBlack.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
this.style.background = "#000";
}))
})
btnsContainer.appendChild(btnBlack).classList.add("btn");
}
paintBlack();
function paintGreyScale() {
const boxes = divContainer.querySelectorAll(".box");
btnGreyScale.textContent = "Grey";
btnGreyScale.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
let randNum = Math.floor(Math.random() * 256);
let grayScale = `rgb(${randNum},${randNum},${randNum})`;
box.style.background = grayScale;
}))
})
btnsContainer.appendChild(btnGreyScale).classList.add("btn");
}
paintGreyScale();
function paintRgb() {
const boxes = divContainer.querySelectorAll(".box");
btnRgb.textContent = "Rainbow";
btnRgb.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
const rgb = `rgb(${r},${g},${b})`;
box.style.background = rgb;
}))
})
btnsContainer.appendChild(btnRgb).classList.add("btn");
}
paintRgb();
function erase() {
const boxes = divContainer.querySelectorAll(".box");
btnErase.textContent = "Erase";
btnErase.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
this.style.background = "#FFF";
}))
})
btnsContainer.appendChild(btnErase).classList.add("btn");
}
erase();
function clearCanvas() {
const boxes = divContainer.querySelectorAll(".box");
btnShake.textContent = "Shake it!";
btnShake.addEventListener("click", function () {
boxes.forEach(box => box.style.backgroundColor = "#FFF");
})
btnsContainer.appendChild(btnShake).classList.add("shake");
}
clearCanvas();
btnShake.addEventListener("click", clearCanvas);
*,
*::before,
*::after {
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
html {
font-size: 16px;
}
body {
background: linear-gradient(to bottom, #1488CC, #2B32B2);
color: #FFF;
line-height: 1.5;
height: 100vh;
}
#wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
text-align: center;
}
.container {
width: 500px;
height: 500px;
display: grid;
background-color: #FFF;
box-shadow: 0 0 10px;
}
.box {
border: .5px solid #808080;
}
.shake {
animation: shake .5s linear 1;
}
#keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}
<!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, shrink-to-fit=no">
<title>Etch-A-Sketch</title>
</head>
<body>
<div id="wrapper">
<main>
<div class="container"></div>
<div class="buttons"></div>
</main>
</div>
<script src="etchAsketch.js"></script>
</body>
</html>
The mousedown event
window.addEventListener("mousedown", () => {
isDrawing = true;
});
sets isDrawing to true, and mouseup event to false, but you never use this variable to check whether the color should be drawn.
Solution: for each statement you have that's setting the background color of a square (except for clearCanvas), wrap it in an if statement checking if the user isDrawing:
if (isDrawing){this.style.background = "#000";} //black
if (isDrawing){this.style.background = grayScale;} //gray
if (isDrawing){this.style.background = rgb;} // rainbow
if (isDrawing){this.style.background = "#FFF";} // erase
boxes.forEach(box => box.style.backgroundColor = "#FFF");
}) //leave clearCanvas as it is
I have written my first piece of code that I feel could actually be reusable and possibly useful for others. The code is a bit of css, a few javascript-classes and some js-functions.
To use the package, you set up some HTML, and then pass a html-element and an optional parameter, for example like this:
dom = new DOM(document.getElementById('fullScreenWrapper'), 175),
I would like to pack it in some way so that you basically include a js-file, a css-file and then you can use it in any project. jQuery has a design-pattern for extending itself, but I don't use jQuery. Is there any best practice for stuff like this?
Here is the mockup, not packaged:
<html>
<head>
<style>
/* specific styles */
&:root {--scrollPosition:0}
.flicWrapper{
height: calc(var(--wrapperHeight));
}
.flicWrapper .flicChild{
top: calc(var(--previousHeight));
position: fixed;
}
.flicWrapper.transition{
height: initial;
}
.flicWrapper.transition .flicChild{
transition: transform .3s cubic-bezier(0, 0, 0.25, 1);
}
.flicWrapper .before{
transform: translate3d(0,calc((var(--previousHeight)*-1)),0);
}
.flicWrapper .active{
transform: translate3d(0, calc((var(--previousHeight)*-1) + var(--stickyScrollPosition)), 0);
}
.flicWrapper .next{
transform: translate3d(0, calc(var(--scrollPosition)), 0);
}
/*custom style*/
body{
margin: 0;
}
section{
width: 100%;
height: 100%;
}
.s1{
height: 100%;
background: repeating-linear-gradient(
45deg,
red,
red 10px,
blue 10px,
blue 20px
);
}
.s2{
height: 150%;
background: repeating-linear-gradient(
45deg,
#606dbc,
#606dbc 10px,
#465298 10px,
#465298 20px
);
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div class="fullScreenWrapper" id="fullScreenWrapper">
<section class="s1" ></section>
<section class="s2"></section>
<section class="s3" style="background: red";></section>
<section class="s4" style="background: green";></section>
<section class="s5" style="background: black";></section>
</div>
<script type="text/javascript">
var State = class {
constructor(heights, offset) {
this.breakingPoints = heights;
this.offset = offset;
this.state = this.currentState;
}
get currentState(){
return this.breakingPoints.findIndex((elm, i, arr)=> {
if(window.scrollY >= arr[i] && window.scrollY < arr[i+1] ){
return[i]
}
})
}
get update(){
var isUpdated = Math.sign( this.currentState - this.state)
this.state += isUpdated;
return isUpdated !== 0
}
get snapPos() {
var offset = this.state ? this.offset : 0; // first state should not have offset.
return this.breakingPoints[this.state] + offset;
}
},
DOM = class {
constructor(wrapper, offset) {
this.wrapper = wrapper;
this.offset = offset
this.children = Array.from(wrapper.children);
this.childHeights = [];
this.scrollHeights = this.getScrollHeights;
this.wrapper.classList.add('flicWrapper')
setGlobalProperty('wrapperHeight', this.documentHeight)
//apply custom css-values
this.children.forEach((child, i) => {
child.classList.add('flicChild')
var height = 0
if(i > 0 ){
var height = this.children[i-1].offsetHeight;
}
child.style.setProperty('--previousHeight', height+'px');
this.childHeights.push(child.offsetHeight - window.innerHeight);
})
}
get getScrollHeights() {
var heights = [0];
//calculate al the heights
this.children.forEach((el, i) => {
var mod = 2;
if(i === 0 || i === this.children.length -1){mod = 1}; //first and last should only have one offset
var elheight = el.offsetHeight - window.innerHeight + this.offset * mod;
heights.push( elheight + heights[i] );
})
return heights;
}
get documentHeight() {return this.scrollHeights.slice(-1)[0]+window.innerHeight-1}
},
Scroll = class{
constructor(){
this.on = true;
}
throttle(fn, wait){
var time = Date.now();
return () => {
if ((time + wait - Date.now()) < 0 && this.on) {
fn();
time = Date.now();
}
}
}
},
scrollEvent = () => {
if(state.update){ //check state
scroll.on = false;
dom.wrapper.classList.add('transition')
setClasses();
setScrollPos(0, dom.childHeights[state.state]);
setTimeout(changeDone, 1200);
}
},
setClasses = () => {
var s = state.state;
dom.children.forEach((child, i) => {
let classes = child.classList;
classes.toggle('before', i < s);
classes.toggle('after', i > s);
classes.toggle('active', i === s);
classes.toggle('previous', i === s-1);
classes.toggle('next', i === s+1);
})
},
changeDone = () => {
dom.wrapper.classList.remove('transition');
window.scrollTo(0, state.snapPos);
scroll.on = true;
},
//helper
setGlobalProperty = (name, value) => {
document.documentElement.style.setProperty('--'+name, (value+'px'))
},
setScrollPos = (pos, max) => {
setGlobalProperty('scrollPosition', -pos)
setGlobalProperty('stickyScrollPosition', (Math.min(pos, max)*-1))
},
dom = new DOM(document.getElementById('fullScreenWrapper'), 175),
state = new State(dom.scrollHeights, dom.offset),
scroll = new Scroll();
setClasses();
window.addEventListener('scroll', scroll.throttle(scrollEvent, 50));
window.addEventListener('scroll', scroll.throttle(() => {setScrollPos(window.scrollY - state.snapPos, dom.childHeights[state.state])}, 0))
</script>
</body>
I'm trying to create a SimonSays-game but I'm having problems with the inputCheck()-function.
When a user presses a button, the variable userChoice equals whatever color the user pressed down. Then the inputCheck-function is called. The problem is that it always gives you an alert that you lost and ends the game, even if the user pressed the correct color. I added console.logs that log both user input and the current sequence step in the console.
Any help is appreciated. Thanks!
var gameRunning = false;
var userClick = "";
var correctSequence = true;
var sequence = [];
var sequenceCountDisplay = document.getElementById("sequenceCountDisplay");
var currentIndex = 0;
var startButton = document.getElementById("startButton");
startButton.addEventListener("click", startGame);
var buttonGreen = document.getElementById("buttonGreen");
buttonGreen.addEventListener("click", buttonPressGreen);
var buttonRed = document.getElementById("buttonRed");
buttonRed.addEventListener("click", buttonPressRed);
var buttonBlue = document.getElementById("buttonBlue");
buttonBlue.addEventListener("click", buttonPressBlue);
var buttonYellow = document.getElementById("buttonYellow");
buttonYellow.addEventListener("click", buttonPressYellow);
var greenSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound1.mp3");
var redSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound2.mp3");
var yellowSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound3.mp3");
var blueSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound4.mp3");
function buttonPressGreen(){
greenSound.play();
userClick = "green";
if (gameRunning) {
inputCheck();
}
console.log(userClick);
console.log(sequence[currentIndex]);
}
function buttonPressRed(){
redSound.play();
userClick = "red";
if (gameRunning) {
inputCheck();
}
console.log(userClick);
console.log(sequence[currentIndex]);
}
function buttonPressYellow(){
yellowSound.play();
userClick = "yellow";
if (gameRunning) {
inputCheck();
}
console.log(userClick);
console.log(sequence[currentIndex]);
}
function buttonPressBlue(){
blueSound.play();
userClick = "blue";
if (gameRunning) {
inputCheck();
}
console.log(userClick);
console.log(sequence[currentIndex]);
}
function generateSequenceStep() {
var randomNumber = Math.floor(Math.random() * 4);
switch(randomNumber){
case 0: sequence.push("green");
break;
case 1: sequence.push("red");
break;
case 2: sequence.push("yellow");
break;
case 3: sequence.push("blue");
break;
}
}
function playBackSequence() {
currentIndex = 0;
setInterval(function(){
if (sequence[currentIndex] == "green") {
buttonGreen.style.opacity = "0.4";
greenSound.play();
}else if(sequence[currentIndex] == "red"){
buttonRed.style.opacity = "0.4";
redSound.play();
}else if(sequence[currentIndex] == "yellow"){
buttonYellow.style.opacity = "0.4";
yellowSound.play();
}else if(sequence[currentIndex] == "blue"){
buttonBlue.style.opacity = "0.4";
blueSound.play();
}
currentIndex++;
setTimeout(function(){
buttonGreen.style.opacity = "1";
buttonRed.style.opacity = "1";
buttonYellow.style.opacity = "1";
buttonBlue.style.opacity = "1";
}, 500);
}, 1000);
currentIndex = 0;
}
function inputCheck(){
if (userClick == sequence[currentIndex]) {
currentIndex++;
}else{
correctSequence = false;
}
if (correctSequence === false) {
gameOver();
}else if (currentIndex >= sequence.length) {
nextRound();
}
}
function gameOver(){
if (!correctSequence) {
alert("Game Over! You were able to remember " + sequence.length + " steps!");
currentIndex = 0;
gameRunning = false;
}
}
function startGame(){
gameRunning = true;
currentIndex = 0;
sequence = [];
correctSequence = true;
generateSequenceStep();
generateSequenceStep();
sequenceCountDisplay.innerHTML = sequence.length;
playBackSequence();
}
function nextRound(){
correctSequence = true;
currentIndex = 0;
generateSequenceStep();
sequenceCountDisplay.innerHTML = sequence.length;
playBackSequence();
}
/*General ----------------------------------------------*/
body {}
#pageContainer {
display: block;
position: relative;
text-align: center;
margin: auto;
height: 100%;
width: 100%;
}
/*------------------------------------------------------*/
/*Button Styles -----------------------------------------*/
#buttons {
max-width: 720px;
width: 100%;
height: 100%;
margin: auto;
z-index: -1;
stroke: #000;
stroke-miterlimit: 10;
stroke-width: 3px;
}
#buttonYellow {
fill: #fcee21;
}
#buttonYellow:hover {
fill: #fcf14f;
stroke-width: 4px;
}
#buttonYellow:active {
fill: #c9bc03;
stroke-width: 3px;
}
#buttonGreen {
fill: #00dc00;
}
#buttonGreen:hover {
fill: #1aff1a;
stroke-width: 4px;
}
#buttonGreen:active {
fill: #008000;
stroke-width: 3px;
}
#buttonRed {
fill: red;
}
#buttonRed:hover {
fill: #ff4d4d;
stroke-width: 4px;
}
#buttonRed:active {
fill: #990000;
stroke-width: 3px;
}
#buttonBlue {
fill: blue;
}
#buttonBlue:hover {
fill: #3333ff;
stroke-width: 4px;
}
#buttonBlue:active {
fill: #000099;
stroke-width: 3px;
}
/*------------------------------------------------------*/
/*Center Control --------------------------*/
#center {
position: relative;
top: -500px;
left: -35px;
margin: auto;
width: 100%;
text-align: center;
font-size: 35px;
max-width: 200px;
}
#sequenceCountDisplay {
margin: 20px;
font-size: 45px;
}
#startButton {
margin: 20px;
font-size: 20px;
}
<body>
<div id="pageContainer">
<svg id="buttons" viewBox="0 0 500 500"><title>Buttons</title>
<path id="buttonYellow" d="M56.08,238.5h46.85c5.93,0,7.28,1.15,8.19,6.92,7.53,47.9,43.75,84,91.68,91.46,5.25.81,6.66,2.32,6.66,7.51q0,47.54-.06,95.09c0,5.33-1.16,6.41-6.63,6A215.32,215.32,0,0,1,96.47,408.82,213.45,213.45,0,0,1,48,364.3c-23.49-29.56-38.7-62.89-44.3-100.39-.91-6.09-1.61-12.21-2.13-18.35-.42-5,1.67-7,6.75-7q23.89,0,47.77,0Z"/>
<path id="buttonGreen" d="M56.08,208.57h46.85c5.93,0,7.28-1.15,8.19-6.92,7.53-47.9,43.75-84,91.68-91.46,5.25-.81,6.66-2.32,6.66-7.51q0-47.54-.06-95.09c0-5.33-1.16-6.41-6.63-6A215.32,215.32,0,0,0,96.47,38.25,213.45,213.45,0,0,0,48,82.77c-23.49,29.56-38.7,62.89-44.3,100.39-.91,6.09-1.61,12.21-2.13,18.35-.42,5,1.67,7,6.75,7q23.89,0,47.77,0Z"/>
<path id="buttonRed" d="M394.08,208.57H347.23c-5.93,0-7.28-1.15-8.19-6.92-7.53-47.9-43.75-84-91.68-91.46-5.25-.81-6.66-2.32-6.66-7.51q0-47.54.06-95.09c0-5.33,1.16-6.41,6.63-6A215.32,215.32,0,0,1,353.69,38.25a213.45,213.45,0,0,1,48.5,44.52c23.49,29.56,38.7,62.89,44.3,100.39.91,6.09,1.61,12.21,2.13,18.35.42,5-1.67,7-6.75,7q-23.89,0-47.77,0Z"/>
<path id="buttonBlue" d="M394.08,238.5H347.23c-5.93,0-7.28,1.15-8.19,6.92-7.53,47.9-43.75,84-91.68,91.46-5.25.81-6.66,2.32-6.66,7.51q0,47.54.06,95.09c0,5.33,1.16,6.41,6.63,6a215.32,215.32,0,0,0,106.29-36.68,213.45,213.45,0,0,0,48.5-44.52c23.49-29.56,38.7-62.89,44.3-100.39.91-6.09,1.61-12.21,2.13-18.35.42-5-1.67-7-6.75-7q-23.89,0-47.77,0Z"/>
</svg>
<div id="center">
<h2 id="sequenceCountDisplay">0</h2>
<button id="startButton" type="button">Start New Game</button>
</div>
</div>
<script src="scripts.js"></script>
</body>
currentIndex isn't being set to 0 properly after playBackSequence()
Change the function to use a temporary variable:
function playBackSequence() {
tmpCurrentIndex = currentIndex;
setInterval(function(){
if (sequence[tmpCurrentIndex] == "green") {
buttonGreen.style.opacity = "0.4";
greenSound.play();
}else if(sequence[tmpCurrentIndex] == "red"){
buttonRed.style.opacity = "0.4";
redSound.play();
}else if(sequence[tmpCurrentIndex] == "yellow"){
buttonYellow.style.opacity = "0.4";
yellowSound.play();
}else if(sequence[tmpCurrentIndex] == "blue"){
buttonBlue.style.opacity = "0.4";
blueSound.play();
}
tmpCurrentIndex++;
setTimeout(function(){
buttonGreen.style.opacity = "1";
buttonRed.style.opacity = "1";
buttonYellow.style.opacity = "1";
buttonBlue.style.opacity = "1";
}, 500);
}, 1000);
currentIndex = 0;
}
help please help, I do not understand the reason. Throws an error ReferenceError: Velocity is not defined. If I use $ (function () {}; then the error disappears, but then simply do not work. Use hammer 2.0.4 and velocity 1.2.3
// Constants
const THRESH = 0.75;
const TIMING = 250;
// When the dom is loaded, set up hammer events.
document.addEventListener('DOMContentLoaded', f => {
var lis = document.querySelectorAll('.swipe');
for (let i = 0; i < lis.length; i++) {
let hammertime = new Hammer(lis[i]);
hammertime.on('panright', e => handlePan(e));
hammertime.on('panend', e => reset(e));
}
});
// pane{right} handler
function handlePan(e, model) {
var {target: el, deltaX: dx} = e;
if (doAction(dx, el)) {
el.classList.add('action');
} else {
el.classList.remove('action');
}
Velocity(el, {
translateX: dx
}, 0);
}
// panend handler
function reset(e) {
var {target: el, deltaX: dx} = e;
// Should we remove the element?
if (doAction(dx, el)) {
Velocity(el, {
translateX: dx * 2
}, TIMING).then(() => {
return Velocity(el, {
height: 0
}, TIMING);
}).then(() => {
el.parentNode.removeChild(el);
});
} else {
Velocity(el, {
translateX: 0
}, TIMING);
}
}
// Determines if an element will be dismissed
function doAction(dx, el) {
return Math.abs(dx) >= THRESH * el.clientWidth;
}
.swipe {
position: relative;
list-style: none;
text-align: center;
background: #9e9e9e;
height: 150px;
line-height: 150px;
width: 40vw;
}
<div class="swipe">Swipe me!</div>
<!--Scripts-->
<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.4/hammer.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
The velocity library reference should have been BEFORE the
jQuery reference. The way you know this is that when you run the code, with the developers tools open (F12) and you get the error, the error comes up from within the jQuery code - - so that means that jQuery can't find velocity. Then looking at the script references, you can see why...the jQuery library is running before the velocity library has been loaded.
This works:
// Constants
const THRESH = 0.75;
const TIMING = 250;
// When the dom is loaded, set up hammer events.
document.addEventListener('DOMContentLoaded', f => {
var lis = document.querySelectorAll('.swipe');
for (let i = 0; i < lis.length; i++) {
let hammertime = new Hammer(lis[i]);
hammertime.on('panright', e => handlePan(e));
hammertime.on('panend', e => reset(e));
}
});
// pane{right} handler
function handlePan(e, model) {
var {target: el, deltaX: dx} = e;
if (doAction(dx, el)) {
el.classList.add('action');
} else {
el.classList.remove('action');
}
Velocity(el, {
translateX: dx
}, 0);
}
// panend handler
function reset(e) {
var {target: el, deltaX: dx} = e;
// Should we remove the element?
if (doAction(dx, el)) {
Velocity(el, {
translateX: dx * 2
}, TIMING).then(() => {
return Velocity(el, {
height: 0
}, TIMING);
}).then(() => {
el.parentNode.removeChild(el);
});
} else {
Velocity(el, {
translateX: 0
}, TIMING);
}
}
// Determines if an element will be dismissed
function doAction(dx, el) {
return Math.abs(dx) >= THRESH * el.clientWidth;
}
.swipe {
position: relative;
list-style: none;
text-align: center;
background: #9e9e9e;
height: 150px;
line-height: 150px;
width: 40vw;
}
<!--Scripts-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.4/hammer.min.js"></script>
<div class="swipe">Swipe me!</div>