I am having trouble with some programming that I want to perform..
On my page there is a button called "resetButton" and it is invisible button in the middle of the page. It is using: z-index: 100 to be place in front of the images that appear behind it to appear to have the effect that you are actually clicking the image to perform the action.
This buttons functionality is to reset the entire race, including: the stoplight switching back to red, the winner image going away, and the two participants in the race begin again at the starting position.
I feel as I am just overthinking this problem and cannot figure it out and would appreciate being led in the right direction.
// script to show and hide winner
function showFish() {
document.getElementById('bluefishwin').style.visibility = "visible";
}
function showTurtle() {
document.getElementById('turtlewins').style.visibility = "visible";
}
function showFishText() {
document.getElementById('fishwins').style.visibility = "visible";
}
function showTurtleText() {
document.getElementById('turtlewinss').style.visibility = "visible";
}
// script to call both functions to start race
function letsRace() {
startTimer();
myMove();
}
// script for stoplight
function displayNextImage() {
document.getElementById("stoplight").src = images[1];
}
function startTimer() {
setInterval(displayNextImage);
}
var images = [],
x = -1;
images[0] = "http://www.drivingtesttips.biz/images/traffic-light-red.jpg";
images[1] = "http://www.drivingtesttips.biz/images/traffic-lights-green.jpg";
// script for race
function myMove() {
var elemBluefish = document.getElementById("bluefish");
var elemTurtle = document.getElementById("turtle");
var posBluefish = 0;
var posTurtle = 0;
var id = setInterval(frame, 5);
function frame() {
if (posBluefish >= 1150 && posTurtle >= 1150) {
clearInterval(id);
return;
}
if (posBluefish < 1140) {
posBluefish += Math.round(Math.random() * 5);
if (posBluefish > 1140) {
posBluefish = 1140;
}
elemBluefish.style.left = posBluefish + 'px';
}
if (posTurtle < 1140) {
posTurtle += Math.round(Math.random() * 5);
if (posTurtle > 1140) {
posTurtle = 1140;
}
elemTurtle.style.left = posTurtle + 'px';
}
if (posBluefish >= 1140 || posTurtle >= 1140) {
clearInterval(id);
if (posBluefish >= 1140 && posTurtle < 1140) {
showFish();
showFishText();
} else if (posBluefish < 1140 && posTurtle >= 1140) {
showTurtle();
showTurtleText();
} else {
window.alert("Tie");
}
return;
}
}
}
#racePrompt {
position: absolute;
left: 10pc;
font-size: 20px;
}
.raceButton {
position: absolute;
width: 5pc;
right: 82pc;
height: 10pc;
z-index: 100;
background: transparent;
border: none !important;
font-size: 0;
}
body {
overflow: hidden;
}
#myStoplight {
position: absolute;
width: 10pc;
}
#bluefish {
position: absolute;
top: 31pc;
width: 17pc;
left: -.5pc;
}
#turtle {
position: absolute;
width: 15pc;
top: 20pc;
left: .5pc;
}
body {
background-image: url("http://www.hpud.org/wp-content/uploads/2015/08/WaterBackground2.jpg")
}
.finishline {
position: absolute;
right: -12pc;
top: 18pc;
}
#stoplight {
position: absolute;
width: 10pc;
}
#bluefishwin {
position: absolute;
right: 31pc;
top: 12pc;
visibility: hidden;
}
#turtlewins {
position: absolute;
width: 20pc;
right: 35pc;
top: 15pc;
visibility: hidden;
}
#fishwins {
font-size: 3pc;
position: absolute;
right: 35pc;
top: 25pc;
visibility: hidden;
}
#turtlewinss {
font-size: 3pc;
position: absolute;
right: 34pc;
top: 26pc;
visibility: hidden;
}
<input type="button" onclick="letsRace()" class="raceButton">
<img id="stoplight" src="http://www.drivingtesttips.biz/images/traffic-light-red.jpg" />
<p id="fishwins">The Fish Wins!</p>
<p id="turtlewinss">The Turtle Wins!</p>
<p id="racePrompt">Click anywhere on the light to start the race!</p>
<img id="bluefish" src="http://clipartist.net/openclipart.org/2013/July/Blue_Fish_Goldfish.png">
<img id="turtle" src="http://www.clipartkid.com/images/386/turtle-free-stock-photo-illustration-of-a-green-sea-turtle-uPgZrm-clipart.png">
<img src="https://t1.rbxcdn.com/877010da8ce131dfcb3fa6a9b07fea89" class="finishline">
<img id="bluefishwin" src="http://clipartist.net/openclipart.org/2013/July/Blue_Fish_Goldfish.png">
<img id="turtlewins" src="http://www.clipartkid.com/images/386/turtle-free-stock-photo-illustration-of-a-green-sea-turtle-uPgZrm-clipart.png">
<div id="container">
<div id="animate"></div>
Any ideas on how to create a function so when I call it with an onClick I can reset everything back to as if I just rendered the page, similar to hitting the refresh button on the web browser.
Well, this practically impossible.
In order to make this work you would have to move state out of the DOM.
To do that, you must specified the Model of you application and just let the UI to render around it. You can imagine it like this:
var ui = createDOM(state)
body.innerHTML = ''
body.appendChild(ui)
//and you state might look like
var state = {
players: [{id: 'turle', winner: true}]
}
//in createDOM fn
function createDOM(state) {
var turtleWinnerDiv = document.createElement('div')
if (state.players[0].winner) {
turtleWinnerDiv.style.visibility = 'visible'
}
return turtleWinnerDiv
}
This is like "nic" way of doing this.
Uglier one might be something like this:
// save original app DOM
var defaultHTML = document.body.innerHTML
// at the end
document.body.innerHTML = defaultHTML
// voila App is back to default state
Related
This is my very first ever question asked here, so I do apologise in advance if I am not following correct guidelines.
Basically what I'm trying to do is to manually code side-section navigating in Javascript. The navigating itself is working just fine, one can click either left or right and the sections will scroll left and right endlessly in a carousel. But when I try to set the previous pages scrollTop back to 0 once the user has moved off the section I run into problems.
If I scroll through each section and scroll a bit of the way down on each, when I return to the first page and cycle through them again, they seem to alternate as to whether their scrollTop was actually set to 0.
HTML body:
<div class="Arrow" id="arrowLeft"></div>
<div class="Arrow" id="arrowRight"></div>
<section class="Page" id="page1"><div class="PageContent"></div></section>
<section class="Page" id="page2"><div class="PageContent"></div></section>
<section class="Page" id="page3"><div class="PageContent"></div></section>
<section class="Page" id="page4"><div class="PageContent"></div></section>
<section class="Page" id="page5"><div class="PageContent"></div></section>
<section class="Page" id="page6"><div class="PageContent"></div></section>
<section class="Page" id="page7"><div class="PageContent"></div></section>
<section class="Page" id="page8"><div class="PageContent"></div></section>
<script src="JavaScript/scroller.js"></script>
Javascript:
//initialising variables and populating arrays
var currentPage = 0;
var previousPage = 0;
var pages = document.getElementsByClassName("Page");
var sections = [];
for (i=0;i<pages.length;i++) {
sections.push(pages[i]);
}
var pageOffSets = [0, 100, 200, 300, 400, -300, -200, -100];
var zOffSets = [0,-1,-2,-3,-4,-3,-2,-1];
for (i = 0;i<sections.length;i++) {
sections[i].style.left = pageOffSets[i] + "vw";
sections[i].style.zIndex = zOffSets[i];
}
//Navigate function
function slidePage(direction) {
previousPage = currentPage;
if (direction=="right") {
currentPage+=1;
if (currentPage>7) {
currentPage = 0;
}
var hold = sections.shift();
sections.push(hold);
for (i=0;i<sections.length;i++) {
sections[i].style.left = pageOffSets[i] + "vw";
sections[i].style.zIndex = zOffSets[i];
}
}
if (direction=="left") {
currentPage-=1;
if (currentPage<0) {
currentPage = 7;
}
var hold = sections.pop();
sections.unshift(hold);
for (i=0;i<sections.length;i++) {
sections[i].style.left = pageOffSets[i] + "vw";
sections[i].style.zIndex = zOffSets[i];
}
}
//!!!
//This is the part that is supposed to set the scrollTop back to 0
//!!!
setTimeout(function(){
sections[previousPage].scrollTop = 0;
}, 1000);
}
//Event listeners for when the user clicks
document.getElementById("arrowLeft").addEventListener("click", function(){
slidePage("left");
});
document.getElementById("arrowRight").addEventListener("click", function(){
slidePage("right");
});
I would really appreciate if someone could point out what I'm doing wrong.
Edit:
Here is the CSS if you need it:
body {
overflow-x: hidden;
overflow-y: scroll;
}
section {
width: 100vw;
height: 100vh;
position: fixed;
-webkit-transition-duration: 0.4s;
transition-duration: 0.4s;
overflow: scroll;
}
.Arrow {
position: absolute;
z-index: 1;
width: 50px;
height: 50px;
background-color: black;
top: 48%;
}
.Arrow:hover {
cursor: pointer;
}
#arrowRight {
right: 0;
}
#page1 {
background-color: #c81996;
}
#page2 {
background-color: #64324b;
}
#page3 {
background-color: #1996c8;
}
#page4 {
background-color: #324b64;
}
#page5 {
background-color: #96c819;
}
#page6 {
background-color: #4b6432;
}
#page7 {
background-color: #f0fa1e;
}
#page8 {
background-color: #fa1ef0;
}
.PageContent {
height: 8000px;
width: 90vw;
margin: 5vw;
background-image: -webkit-gradient(linear, left top, left bottom, from(red), to(yellow));
background-image: linear-gradient(red, yellow);
position: absolute;
}
There are two problems:
You are rotating the sections array inside your function, so the previousPage index is no longer correct once your anonymous function is fired.
The previousPage variable referenced when setting scrollTop after the timeout can change before the timeout elapses.
The simplest fix would be to rotate pageOffSets and zOffSets arrays instead:
if (direction == "right") {
currentPage++;
if (currentPage >= sections.length) {
currentPage = 0;
}
// rotate pageOffsets
{
var x = pageOffSets.pop();
pageOffSets.unshift(x);
}
// rotate zOffsets
{
var x = zOffSets.pop();
zOffSets.unshift(x);
}
for (i=0;i<sections.length;i++) {
sections[i].style.left = pageOffSets[i] + "vw";
sections[i].style.zIndex = zOffSets[i];
}
} else {
currentPage--;
if (currentPage < 0) {
currentPage = sections.length - 1;
}
// rotate pageOffsets
{
var x = pageOffSets.shift();
pageOffSets.push(x);
}
// rotate zOffSets
{
var x = zOffSets.shift();
zOffSets.push(x);
}
for (i=0;i<sections.length;i++) {
sections[i].style.left = pageOffSets[i] + "vw";
sections[i].style.zIndex = zOffSets[i];
}
}
And you need to capture a copy of the previousPage variable:
// capture a copy of the index so that it doesn't change
var prev = previousPage;
setTimeout(function() {
sections[prev].scrollTop = 0;
}, 500);
I want to draw a horizontal line when the game launches and was able to come up with something that drew the line using requestAnimationFrame() and ctx.stroke().
The problem is that when I attempt to edit the length of the line by changing the values of the end point on the line, the line remains the same length.
If anyone could take a look at it and explain what is going on I would really appreciate it.
var canvasTop = document.getElementById('top');
var ctxTop = canvasTop.getContext('2d');
var frameCountTop = 0;
var fpsTop, fpsIntervalTop, startTimeTop, nowTop, thenTop, elapsedTop;
function startAnimatingTop(fpsTop) {
fpsIntervalTop = 1000 / fpsTop;
thenTop = Date.now();
startTimeTop = thenTop;
drawTop();
}
var topLinePointA = [32, 80];
var topLinePointB = [280, 80];
var topLineStart = topLinePointA[0];
var topLineIncrement = topLineStart + 1;
var topLineEnd = topLinePointB[0];
function drawTop() {
var i = 32;
while (i < topLineEnd) {
requestAnimationFrame(drawTop);
i++;
nowTop = Date.now();
elapsedTop = nowTop - thenTop;
if (elapsedTop > fpsIntervalTop && i < topLineEnd) {
thenTop = nowTop - (elapsedTop % fpsIntervalTop);
ctxTop.lineWidth = 20;
ctxTop.strokeStyle = "black";
ctxTop.moveTo(topLineStart, 80);
ctxTop.lineTo(topLineIncrement, 80);
ctxTop.stroke();
topLineStart += 1;
} else {
cancelAnimationFrame(drawTop);
return;
}
}
}
startAnimatingTop(100);
/*HANGMAN STYLES*/
/*CLASS SELECTORS*/
.lineContainer {
/*border-style: solid;
border-color: blue;*/
}
#top {
position: absolute;
height: 5%;
width: 45%;
left: 30%;
}
#side {
position: absolute;
bottom: 20%;
left: 70%;
height: 78%;
width: 5%;
}
#bottom {
position: absolute;
bottom: 35%;
height: 5%;
width: 56%;
left: 20%;
}
#hangman {
position: absolute;
left: 30%;
height: 40%;
width: 10%;
}
<canvas id='top' class='lineContainer'></canvas>
<canvas id='side' class='lineContainer'></canvas>
<canvas id='bottom' class='lineContainer'></canvas>
<canvas id='hangman' class='lineContainer'></canvas>
<canvas id='puzzle'></canvas>
<div id='scorecard'>
</div>
There are many problems with your code.
You need to read up on using the canvas. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D will help.
Only call requestAnimationFrame once per frame. Putting it inside a while loop will just start 1000's of frames fighting for display refresh.
Start paths with ctx.beginPath();
Clear the canvas using ctx.clearRect(0,0,width,height)
Set canvas size via attributes not via style properties.
The first argument to the function called by requestAnimationFrame is the time. eg drawTop(time)
See comments for more info.
ctx.canvas.width = 100; // << set the canvas size via canvas attributes not style
ctx.canvas.height = 100;
var thenTop = performance.now(); // to get time.
requestAnimationFrame(drawTop); // <<< start the render loop with request, dont call direct
function drawTop(nowTop) { // << time passed to render call
requestAnimationFrame(drawTop); // <<< put call here not in loop
ctxTop.clearRect(0, 0, ctxTop.canvas.width, ctxTop.canvas.height); // <<< clear the canvas
var i = 32;
while (i < topLineEnd) {
// requestAnimationFrame(drawTop); // <<<<<<< Not here
i++;
elapsedTop = nowTop - thenTop;
if (elapsedTop > fpsIntervalTop && i < topLineEnd) {
thenTop = nowTop - (elapsedTop % fpsIntervalTop);
ctxTop.lineWidth = 20;
ctxTop.strokeStyle = "black";
ctxTop.beginPath(); // <<<<<< To start a new path
ctxTop.moveTo(topLineStart, 80);
ctxTop.lineTo(topLineIncrement, 80);
ctxTop.stroke();
topLineStart += 1;
} else {
return;
}
}
}
To simplify this question I have created two divs: when you click on the orange box the blue box below it will move back and forth in a continuous loop. What I want to be able to do is:
Click the orange box to start and STOP the blue box loop
After starting and stopping, the blue box will stop and continue each time where it left off.
I've tried just about everything and can't get it to work. Any advice would be greatly appreciated.
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
hoverSlideBox.onclick = function() {
var pos = 0;
var moveLeft = false;
var stopAnimate = false;
init();
function init() {
setInterval(boxRight, 5);
}
function boxRight() {
if (!moveLeft) {
pos++;
slidingBox.style.left = pos + 'px';
}
if (pos == 500 || moveLeft) {
moveLeft = true;
boxLeft();
}
}
function boxLeft() {
if (moveLeft) {
pos--;
slidingBox.style.left = pos + 'px';
}
if (pos == 0 || !moveLeft) {
moveLeft = false;
}
}
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
You need to move some variables out of the onclick function so that they are not reset each time you click on the orange box. That, together with clearInterval, will give you a start/stop button.
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
var running = false;
var intervalId;
var pos = 0;
var moveLeft = false;
hoverSlideBox.onclick = function() {
init();
function init() {
if (!running) {
intervalId = setInterval(boxRight, 5);
running = true;
} else {
clearInterval(intervalId);
running = false;
}
}
function boxRight() {
if (!moveLeft) {
pos++;
slidingBox.style.left = pos + 'px';
}
if (pos == 500 || moveLeft) {
moveLeft = true;
boxLeft();
}
}
function boxLeft() {
if (moveLeft) {
pos--;
slidingBox.style.left = pos + 'px';
}
if (pos == 0 || !moveLeft) {
moveLeft = false;
}
}
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
To do this, you can use clearInterval to stop the movement. To make it resume when you click again, you just need to have your position variable in a permanent scope (I move it to the global scope for simplicity).
Changed code:
var pos = 0;
var moveLeft = false;
var stopAnimate = false;
var slideInterval;
hoverSlideBox.onclick = function() {
init();
function init() {
if (slideInterval) {
clearInterval(slideInterval);
slideInterval = null;
} else {
slideInterval = setInterval(boxRight, 5);
}
}
Snippet:
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
var pos = 0;
var moveLeft = false;
var stopAnimate = false;
var slideInterval;
hoverSlideBox.onclick = function() {
init();
function init() {
if (slideInterval) {
clearInterval(slideInterval);
slideInterval = null;
} else {
slideInterval = setInterval(boxRight, 5);
}
}
function boxRight() {
if (!moveLeft) {
pos++;
slidingBox.style.left = pos + 'px';
}
if (pos == 500 || moveLeft) {
moveLeft = true;
boxLeft();
}
}
function boxLeft() {
if (moveLeft) {
pos--;
slidingBox.style.left = pos + 'px';
}
if (pos == 0 || !moveLeft) {
moveLeft = false;
}
}
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
setInterval returns interval id which you can use to cancel the interval using clearInterval
You could cancel the timer as the other answer says, or use requestAnimationFrame which is specifically designed for animations like these. See documentation here
This way we can simply check if stopAnimating is set before queueing up our next frame. You could do the same thing with setInterval if you wanted to, but requestAnimationFrame is probably better.
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
hoverSlideBox.onclick = function() {
var pos = 0;
var moveLeft = false;
var stopAnimate = false;
init();
function init() {
animate();
}
function animate(){
// stop animating without queueing up thenext frame
if (stopAnimate) return;
//queue up theh next frame.
requestAnimationFrame(boxRight);
}
function boxRight() {
if (!moveLeft) {
pos++;
slidingBox.style.left = pos + 'px';
}
if (pos == 500 || moveLeft) {
moveLeft = true;
boxLeft();
}
animate();
}
function boxLeft() {
if (moveLeft) {
pos--;
slidingBox.style.left = pos + 'px';
}
if (pos == 0 || !moveLeft) {
moveLeft = false;
}
}
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
Your code doesn't clear the interval when box is clicked again. setInterval executes a function every number of given seconds. To stop it, you have use the function clearInterval, passing the timer id that the function setInterval returned.
Also, your code can be better organized. I've refactorized it base in the following advices:
Don't use different functions for each direction. Your code have two different functions (moveLeft and moveRight) that are almost the same. Instead, use a simpler function that increments position given the current direction. Use a simple variable with the position increment, using -1 for left movement and +1 for right movement. If you have to modify the way the box moves you only have to change the moveBox function.
Use a function to check if the box have reached the boundaries. In general is good to have small functions that do one simple thing. In your code, boundaries check is splited in the two movement functions. In the proposed code the check code is a separate function where you can modify boundaries easily in the future if needed, instead of changing them in different places of your code.
Don't put all your logic in the click listener. Again, use simple functions that perform one simple task. The click listener should do a simple task, that's switching animation on and off. Initialization code should be in its onw function.
Again, for simplicity, use a simple function as the function executed by setInterval. If not, you would have to change the function that is executed by the setInterval function: moveRight when box is moving to the right and moveLeft when the box is moving to the left. This makes the code more complex and hard to debug and mantain.
Those advices are not very useful with a small code like this, but its benefits are more visible when your code gets more complex.
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
var pos = 0;
var direction = 1;
var animate = false;
var intervalHandler;
hoverSlideBox.onclick = function() {
if (animate) {
stop();
}
else {
init();
}
}
function init() {
animate = true;
intervalHandler = setInterval(moveBox, 5);
}
function stop() {
animate = false;
clearInterval(intervalHandler);
}
function tic() {
if (animate) {
moveBox();
}
}
function checkBoundaries() {
if (pos == 500) {
direction = -1;
}
else if (pos == 0) {
direction = 1;
}
}
function moveBox() {
pos += direction;
slidingBox.style.left = pos + 'px';
checkBoundaries();
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;"></div>
Try this snippet, it should work aswell
var hoverSlideBox = document.getElementById("hover_slide_box");
var slidingBox = document.getElementById("sliding_box");
var moveLeft = false;
var pos = 0;
var intervalID = null;
hoverSlideBox.onclick = function(){
if (intervalID) {
clearInterval(intervalID);
intervalID = null;
} else {
intervalID = setInterval(function () {
pos += moveLeft ? -1 : 1;
slidingBox.style.left = pos + 'px';
moveLeft = pos >= 500 ? true : pos <= 0 ? false : moveLeft;
}, 5);
}
}
<div id="hover_slide_box" style="background-color: #ff931e; cursor: pointer; position: absolute; height: 100px; width: 100px;">
<div id="sliding_box" style="top: 120px; background-color: #0071bc; cursor: pointer; position: absolute; height: 100px; width: 100px;">
I wrote a script that creates a set of smaller circles arranged in a circle, which are added to the DOM one by one with a loop. After first loop is done (so I would expected this to be when i == 54) I would like to start another loop, starting from the first circle in a list and one by one changing the color of the circles from grey to red.
This is my code:
var i = 1;
var appendCircle = function loop() {
setTimeout(function() {
var $circle = "<div class='circle circle" + i + "' style='transform:rotate(" + 7.2 * i + "deg) translate(3em)'></div>";
var $container = $(".circles-wrapper .circles");
$container.append($circle);
i++;
if (i < 55) {
loop();
}
}, 20);
// this is the problem because this change color of all small circles at once.
if (i == 54) {
setTimeout(function() {
$(".circle").each(function() {
$(this).css({
"background": "blue"
});
})
}, 20);
}
};
setTimeout(appendCircle, 100);
.circles-wrapper {
position: absolute;
top: 39%;
left: 51%;
}
.circles {
position: relative;
transform: rotateY(48deg);
}
.circle {
width: .2em;
height: .2em;
margin: -.2em;
border-radius: 50%;
background: #ceced0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="circles-wrapper">
<div class="circles"></div>
</div>
You're giving each circle the class "circle"+index so all you have to do is loop through each index and change the background color of each element. What I did was used the same loop function and after i reached 55 I took the mod 55 of it and used that to select the circle. Code and snippet below.
I also noticed that some circles overlap. if you generate 50 circles then there won't be any overlap. I wrote the code below to reflect this.
var i = 1;
var appendCircle = function loop() {
setTimeout(function() {
if (i < 51) {
var $circle = "<div class='circle circle" + i + "' style='transform:rotate(" + 7.2 * i + "deg) translate(3em)'></div>";
var $container = $(".circles-wrapper .circles");
$container.append($circle);
}
else{
var circleIndex = (i % 51)+1;
$(".circle"+circleIndex).css("background-color", "blue");
}
if(i<109){
loop();
}
i++;
}, 20);
};
setTimeout(appendCircle, 100);
.circles-wrapper {
position: absolute;
top: 39%;
left: 51%;
}
.circles {
position: relative;
transform: rotateY(48deg);
}
.circle {
width: .2em;
height: .2em;
margin: -.2em;
border-radius: 50%;
background: #ceced0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="circles-wrapper">
<div class="circles"></div>
</div>
After your first pass you probably want to find the circle created and modify those. You are giving them a class of circle + i so you can easily find them. Check the code snip. I added a third pass just because.
var i = 1,
CIRCLE_COUNT = 52;
var appendCircle = function loop() {
setTimeout(function() {
i++;
// first pass
if (i < CIRCLE_COUNT * 1) {
var $circle = "<div class='circle circle" + i + "' style='transform:rotate(" + 7.2 * i + "deg) translate(3em)'></div>";
var $container = $(".circles-wrapper .circles");
$container.append($circle);
}
// second pass
else if (i < CIRCLE_COUNT * 2) {
$(".circle" + (i % CIRCLE_COUNT+1)).css('background', 'blue');
}
// third pass
else if (i < CIRCLE_COUNT * 3) {
$(".circle" + (i % CIRCLE_COUNT+1)).remove();
}
// keep looping?
if (i <= CIRCLE_COUNT * 3)
loop();
}, 20);
};
setTimeout(appendCircle, 100);
.circles-wrapper {
position: absolute;
top: 39%;
left: 51%;
}
.circles {
position: relative;
transform: rotateY(48deg);
}
.circle {
width: .2em;
height: .2em;
margin: -.2em;
border-radius: 50%;
background: #ceced0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="circles-wrapper">
<div class="circles"></div>
</div>
something like this? i put a timeout function in your .each() in order to make a delay between each iteration of the loop when you change the color of the circles to blue
var i = 1;
var appendCircle = function loop() {
setTimeout(function() {
var $circle = "<div class='circle circle" + i + "' style='transform:rotate(" + 7.2 * i + "deg) translate(3em)'></div>";
var $container = $(".circles-wrapper .circles");
$container.append($circle);
i++;
if (i < 55) {
loop();
}
}, 20);
// this is the problem because this change color of all small circles at once.
if (i == 54) {
var time = 50;
$(".circle").each(function() {
var $this = $(this)
setTimeout(function(){
$this.css({
"background": "blue"
});
}, time)
time += 50;
});
}
};
setTimeout(appendCircle, 100);
.circles-wrapper {
position: absolute;
top: 39%;
left: 51%;
}
.circles {
position: relative;
transform: rotateY(48deg);
}
.circle {
width: .2em;
height: .2em;
margin: -.2em;
border-radius: 50%;
background: #ceced0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="circles-wrapper">
<div class="circles"></div>
</div>
You need to change the CSS of just one element, then start a timeout to change the next one.
var i = 1;
var appendCircle = function loop() {
setTimeout(function() {
var $circle = "<div class='circle circle" + i + "' style='transform:rotate(" + 7.2 * i + "deg) translate(3em)'></div>";
var $container = $(".circles-wrapper .circles");
$container.append($circle);
i++;
if (i < 55) {
loop();
}
}, 20);
var j = 0;
function changeColor() {
$(".circle").eq(j).css("background", "blue");
j++;
if (j >= $(".circle").length) {
clearInterval(changeInterval);
}
}
if (i == 53) {
setInterval(changeColor, 20);
}
}
setTimeout(appendCircle, 100);
.circles-wrapper {
position: absolute;
top: 39%;
left: 51%;
}
.circles {
position: relative;
transform: rotateY(48deg);
}
.circle {
width: .2em;
height: .2em;
margin: -.2em;
border-radius: 50%;
background: #ceced0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="circles-wrapper">
<div class="circles"></div>
</div>
jQuery has features that make animations like this reasonably trivial, though you need to understand several methods.
jQuery's .delay(), .promise(), .then() and javascript's Array.prototype.reduce() can be exploited as follows :
var appendCircles = function($container) {
//create and append hidden circles
for(var i=0; i<50; i++) {
$("<div class='circle'></div>").css('transform', 'rotate(' + 7.2 * i + 'deg) translate(3em)').hide().appendTo($container);
}
//find the freshly appended hidden circles
var $circles = $container.find(".circle");
//initial delay
$circles.eq(0).delay(100).promise()
.then(function() {
//show the circles, one by one
return $circles.get().reduce(function(promise, el) {
return promise.then(function() {
return $(el).show().delay(20).promise();
});
}, $.when());//$when() is a resolved promise that gets the built promise chain started
})
.then(function() {
//make circles blue, one by one
return $circles.get().reduce(function(promise, el) {
return promise.then(function() {
return $(el).css('backgroundColor', 'blue').delay(20).promise();
});
}, $.when());//$when() is a resolved promise that gets the built promise chain started
});
};
appendCircles($(".circles"));
codepen
.reduce() probably needs some explanation. $circles.get() returns an array and .reduce(...) is used to build a promise chain equivalent to initialPromise.then(...).then(...).then(...). This trick is performed twice, in sequence, to give the initial "show" effect followed by the color-change effect.
This suite of methods is worth learning if you want to make custom animation sequences with jQuery.
I have used this script to create an infinite carousel on my website here. It's been customized with CSS so the first and last items are displayed half way.
If you keep clicking the right arrow, you will end up hitting an empty space at the end. So far I haven't been able to fix this. Can anybody offer any solutions?
Here is the relevant script:
/**
* #author Stéphane Roucheray
* #extends jquery
*/
jQuery.fn.simplecarousel = function(previous, next, options){
var sliderList = jQuery(this).children()[0];
if (sliderList) {
var increment = jQuery(sliderList).children().outerWidth(true),
elmnts = jQuery(sliderList).children(),
numElmts = elmnts.length,
sizeFirstElmnt = increment,
shownInViewport = Math.round(jQuery(this).width() / sizeFirstElmnt),
firstElementOnViewPort = 1,
isAnimating = false;
for (i = 0; i < shownInViewport; i++) {
jQuery(sliderList).css('width',(numElmts+shownInViewport)*increment + increment + "px");
jQuery(sliderList).append(jQuery(elmnts[i]).clone());
}
jQuery(previous).click(function(event){
if (!isAnimating) {
if (firstElementOnViewPort == 1) {
jQuery(sliderList).css('left', "-" + numElmts * sizeFirstElmnt + "px");
firstElementOnViewPort = numElmts;
}
else {
firstElementOnViewPort--;
}
jQuery(sliderList).animate({
left: "+=" + increment,
y: 0,
queue: true
}, "swing", function(){isAnimating = false;});
isAnimating = true;
}
});
jQuery(next).click(function(event){
if (!isAnimating) {
if (firstElementOnViewPort > numElmts) {
firstElementOnViewPort = 2;
jQuery(sliderList).css('left', "0px");
}
else {
firstElementOnViewPort++;
}
jQuery(sliderList).animate({
left: "-=" + increment,
y: 0,
queue: true
}, "swing", function(){isAnimating = false;});
isAnimating = true;
}
});
}
};
#home-carousel-container {
position: relative;
}
#home-carousel {
overflow: hidden;
}
#home-carousel ul {
margin-left: -143px;
padding: 0;
position: relative;
}
#home-carousel li {
float: left;
height: 645px;
list-style: none outside none;
margin: 0 3px;
width: 256px;
}
As per my comment.
You have set a negative left-margin on your carousel causing it to hide half of an image. As a result when you click next/previous, it shows where an image is moved to create the continuous affect.
Witihin your css, I changed
#home-carousel ul{
position: relative;
padding: 0;
margin-left: -143px;
}
to
#home-carousel ul{
position: relative;
padding: 0;
margin-left: -3px;
}
And had no problems what so ever.