问题
I made a simple slideshow using only CSS, where on the radio button click, the margin of the element changes so it displays the desired slide. You can see how it works in the code snippet below. I also made this slideshow auto play with JavaScript. The script checks the next radio button in the list every 3 seconds.
Now I need help to make the slideshow auto play in a loop and to also stop auto play when you check any radio button manually.
$(document).ready(function() {
function autoplay() {
$("input[name=radio-button]:checked").nextAll(':radio:first').prop('checked', true);
};
setInterval(autoplay, 3000);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked ~ .slider {
margin-left: 0%;
}
#radio-button2:checked ~ .slider {
margin-left: -100%;
}
#radio-button3:checked ~ .slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
回答1:
Create a curr
variable, increment it inside the interval and use Modulo %
to loop it back to 0:
jQuery(function($) { // DOM ready and $ alias in scope
var curr = 0;
function autoplay() {
curr = ++curr % 3; // Increment and Reset to 0 when 3
$("[name=radio-button]")[curr].checked = true;
}
setInterval(autoplay, 3000);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked~.slider {
margin-left: 0%;
}
#radio-button2:checked~.slider {
margin-left: -100%;
}
#radio-button3:checked~.slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
The better way:
- Overflow a super parent so you don't get scrollbars
- Use
display: flex;
instead of ugly floats. - Use
$('.slider').each(
so you can have multiple sliders in a single page! - Create a
anim()
play()
andstop()
function to control what happens. - Animate using
transform:
andtransition
, since transition is GPU accelerated. Whilst margins are not, and require reflow and repaint. - Animate the transform by using
curr * -100
% - Use
curr %= tot
(Modulo operator) to loopback thecurr
index to 0 when needed. - Why create buttons manually? Create your slides manually and let JS create the buttons for you.
- Use
setInterval
by storing it into a variableitv
- To stop your auto-animation use
clearInterval(itv)
- Use
.hover(stop, play)
to stop on mouseenter and autoplay on mouseleave
$('.slider').each(function(slider_idx) { // Use each, so you can have multiple sliders
let curr = 0; // Set current index
let itv = null; // The interval holder
const $slider = $(this);
const $nav = $('.slider-nav', $slider);
const $items = $('.slider-items', $slider);
const $item = $('.slider-item', $slider);
const tot = $item.length; // How many
const btns = [...new Array(tot)].map((_, i) => $('<input>', {
type: 'radio',
name: `slider-btn-${slider_idx}`,
checked: curr == i,
change() { // On button change event
curr = i; // Set current index to this button index
anim();
}
})[0]);
function anim() {
$items.css({transform: `translateX(-${curr*100}%)`}); // Animate
btns[curr].checked = true; // Change nav btn state
}
function play() {
itv = setInterval(() => {
curr = ++curr % tot; // increment curr and reset to 0 if exceeds tot
anim(); // and animate!
}, 3000); // Do every 3sec
}
function stop() {
clearInterval(itv);
}
$nav.empty().append(btns); // Insert buttons
$slider.hover(stop, play); // Handle hover state
play(); // Start autoplay!
});
/*QuickReset*/ * {margin: 0; box-sizing: border-box;}
.slider {
position: relative;
height: 100px;
}
.slider-overflow {
overflow: hidden; /* use an overflow parent! You don't want scrollbars */
height: inherit;
}
.slider-items {
display: flex; /* Use flex */
flex-flow: row nowrap;
height: inherit;
transition: transform 1s; /* Don't use margin, use transform */
}
.slider-item {
flex: 0 0 100%;
height: inherit;
}
.slider-nav {
position: absolute;
width: 100%;
bottom: 0;
text-align: center;
}
<div class="slider">
<div class="slider-overflow">
<div class="slider-items"> <!-- This one will transition -->
<div class="slider-item" style="background:#0bf;">curr = 0</div>
<div class="slider-item" style="background:#fb0;">curr = 1</div>
<div class="slider-item" style="background:#f0b;">curr = 2</div>
</div>
</div>
<div class="slider-nav">
<!-- JS will populate buttons here -->
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
So both DIV elements .slider-nav
and .slider-overflow
need to be inside of the common parent .slider
- so that we properly stop (pause) the auto-animation after any interaction with those elements.
回答2:
Looping the Slideshow
To loop the slideshow, you'll want to modify your autoplay
function to:
- Get the list of all radio buttons,
- Determine which one is checked,
- Uncheck that and check the next one in the list,
- Paying special attention to wrap back to the first one if already at the end.
I'll rename the function next
.
function next() {
// get all the radio buttons.
var buttons = $('input[name="radio-button"]');
// look at each one in the list.
for (let i = 0; i < buttons.length; i++) {
// if this one isn't the checked one, move on to the next.
if (!buttons[i].checked) {
continue;
}
// okay, so this one is checked. let's uncheck it.
buttons[i].checked = false;
// was this one at the end of the list?
if (i == buttons.length - 1) {
// if so, the new checked one should be the first one.
buttons[0].checked = true;
} else {
// otherwise, just the new checked one is the next in the list.
buttons[i + 1].checked = true;
}
// now that we've made that change, we can break out of the loop.
break;
}
}
As a bonus, you can easily make a similar function called prev
to go in the opposite direction.
Stopping the Slideshow
When you manually click a radio button, the slideshow should stop. To stop the slideshow, you need to clear the interval that you already set.
The .setInterval() function returns an "interval id". This id can be used to make changes to the interval later on -- like stopping it.
var timer = setInterval(next, 3000);
Then, later on, you'll want to pass that timer
value back into clearInterval to stop the timer.
clearInterval(timer);
It would be easier to factor that into two functions, start
and stop
and let the timer
value be global:
var timer;
function start() {
timer = setInterval(next, 3000);
}
function stop() {
clearInterval(timer);
}
So now you can call the stop
function whenever any of the radio buttons receive a click
event:
$('input[name="radio-button"]').on('click', stop);
Full Example
This is your code with the modifications described above.
I've added buttons for "start", "stop", "prev", and "next" -- these aren't necessary for things to function. They're just there for demonstration purposes.
$(document).ready(function() {
/* the list of buttons is not dynamic, so rather than fetching the list of
* buttons every time `next` or `prev` gets executed, we can just fetch it
* once and store it globally. */
var buttons = $('input[name="radio-button"]');
var timer;
function start() {
timer = setInterval(next, 3000);
}
function stop() {
clearInterval(timer);
}
function next() {
for (let i = 0; i < buttons.length; i++) {
if (!buttons[i].checked) {
continue;
}
buttons[i].checked = false;
if (i == buttons.length - 1) {
buttons[0].checked = true;
} else {
buttons[i + 1].checked = true;
}
break;
}
}
function prev() {
for (let i = 0; i < buttons.length; i++) {
if (!buttons[i].checked) {
continue;
}
buttons[i].checked = false;
if (i == 0) {
buttons[buttons.length - 1].checked = true;
} else {
buttons[i - 1].checked = true;
}
break;
}
}
start();
buttons.on('click', stop);
/* these next lines are unnecessary if you aren't including the buttons */
$('#start').on('click', start);
$('#stop').on('click', stop);
$('#next').on('click', next);
$('#prev').on('click', prev);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked~.slider {
margin-left: 0%;
}
#radio-button2:checked~.slider {
margin-left: -100%;
}
#radio-button3:checked~.slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
<!-- these buttons are unnecessary, they are only for demonstration purposes -->
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="prev">Prev</button>
<button id="next">Next</button>
来源:https://stackoverflow.com/questions/60660673/enable-css-slideshow-autoplay-with-javascript