实现图片轮播的基本原理
- 有一个视窗容器显示当前图片内容,容器之外的内容都被隐藏
- 在视窗容器中,有一个包含一组图片的子容器,改变其水平或者垂直方向的值,形成轮播
上图的蓝色框表示视窗容器,蓝色框之外的区域是被隐藏的。当橙色区域的left或者tranlateX为-200时,可视范围由1变成2。此时轮播图为向右翻页。
<div class="container"> <!--视窗容器-->
<div class="slider-content" style="left: -200px;"><!--图片容器-->
<div class="slider-item">1</div>
<div class="slider-item">2</div>
<div class="slider-item">3</div>
</div>
</div>
接下来得思考轮播图应该具有的哪些功能点:
- 自动播放
- 无缝循环播放
- 点击按钮翻页
- 拖动翻页
轮播图的自动播放
自动播放可以理解为定时执行,就是间隔几秒去修改容器的位置。因此使用setInterval或者setTimeout即可。
以下代码简单是实现了自动轮播的效果。
var content = document.querySelector('.slider-content');
class Slider {
constructor(ele){
this.ele = ele;
this.width = this.ele.parentNode.offsetWidth; // 获取宽度
this.length = document.querySelectorAll('.slider-item').length;
this.animate();
}
move() {
let ele = this.ele;
if(parseInt(ele.style.left) > -(this.length*this.width)) {
// 设置容器改变的left值
ele.style.left = parseInt(ele.style.left) - this.width +'px';
}else{
ele.style.left = 0;
}
}
animate() {
setInterval(this.move.bind(this),2000)
}
}
var box = new Slider(content);
观察上面的效果可以发现,当播放到最后一个图片,再回到第一页时,出现了空白,显然这个效果不理想,以上代码的边界逻辑存在问题,没有实现无缝循环播放。
无缝循环播放
实现无缝循环播放,我们需要向slider-content中的末尾添加上第1页。如下图
无缝循环播放示意图html结构
<div class="container"> <!--视窗容器-->
<div class="slider-content" style="left: -200px;"><!--图片容器-->
<div class="slider-item">1</div>
<div class="slider-item">2</div>
<divclass="slider-item">3</div>
<div class="slider-item">1</div>
</div>
</div>
var content = document.querySelector('.slider-content');
class Slider {
constructor(ele){
this.ele = ele;
this.width = this.ele.parentNode.offsetWidth; // 获取宽度
this.length = document.querySelectorAll('.slider-item').length;
this.index = 0; // 当前页的索引值
this.animate();
}
move () {
// 当前页的索引值等于最后一页,重置索引值为第一页
if (this.index === this.length - 1) {
this.index = 0;
}
this.index ++;
this.ele.style.left = - this.index * this.width + 'px';
}
animate() {
setInterval(this.move.bind(this),2000)
}
}
var box = new Slider(content);
以上代码新增了索引值index,用于记录当前窗口显示的是第几张图,当索引值不断递增,直到等于length-1时,表示显示的是最后一张。length-1就是边界点,此时将索引值重置为0,轮播图就实现了循环。
相反的往左翻页index--,递减的边界值为0,当index为0时,将index的值设置为length-1。
点击按钮翻页
点击按钮翻页,实际就是操作index值,向左翻页则index--,向右翻页则index++。需要注意的是,在操作翻页时,最好将定时器清除,以免造成效果混乱。
结合拖拽在react中封装轮播图
class Slider extends React.Component {
constructor(props){
super(props);
// props为组件传递的配置项
this.speed = this.props.speed || 2000; // 播放速度
this.direction = this.props.direction || 'X'; // 翻页方向
this.auto = this.props.auto || true; // 是否自动播放
this.effect = this.props.effect ||'easeIn'; // 动效方式
this.data = this.props.data || []; // 页面数据内容
this.length = this.data.length + 1; // 页面数量
this.width = this.props.width || 320; // 容器宽度
this.height = this.props.height || 200; // 容器高度
this.index = 0; // 当前页索引值
this.timer = 0; // 定时器ID
/**
* 是否正在自动滚动
*/
this.autoRun = false;
// 根据配置的方向,设置翻页的值
if(this.direction === 'X') {
this.dir = parseInt(this.width);
}else {
this.dir = parseInt(this.height);
}
if(this.auto) {
this.autoRun = true;
this.timer = setInterval(this.autoPlay, this.speed);
}
}
/**
* 移动到第几页
* @param {number} index 第几页
* @param {string} direction 方向,从 'left, right, up, down' 中取一个作为值
*/
setIndex = (direction) => {
let to = 0;
const ele = this.refs.sliderContent.refs.dragElement;
/**
* 点击左侧按钮,index --,边界点为0
*/
if (direction === 'left') {
// 初始位置就是极限位置的时候
if (this.index === 0) {
this.index = this.length - 1;
ele.style.transform = `translate${this.direction}(${- this.index * this.dir}px)`;
}
to = - (this.index - 1) * this.dir;
this.index --;
}
/**
* 点击右侧按钮,到下一页,index ++,边界点为length-1
*/
if (direction === 'right') {
if (this.index === this.length - 1) {
this.index = 0;
ele.style.transform = `translate${this.direction}(${0}px)`;
}
to = - (this.index + 1) * this.dir;
this.index ++;
}
// 此处结合了封装的运动框架
animate(ele, this.effect, {
to,
direction: this.direction
}, () => {
if (direction === 'right' && this.index === this.length - 1) {
ele.style.transform = `translate${this.direction}(${0}px)`;
this.index = 0;
}
if (direction === 'left' && this.index === 0) {
ele.style.transform = `translate${this.direction}(${- this.index * this.dir}px)`;
this.index = this.length - 1;
}
if (!this.autoRun) {
this.timer = setInterval(() => this.setIndex(direction), this.speed);
this.autoRun = true;
}
})
}
clickPrev = () => {
this.autoRun = false;
clearInterval(this.timer);
this.setIndex('left');
}
clickNext = () => {
this.autoRun = false;
clearInterval(this.timer);
this.setIndex('right');
}
autoPlay = () => {
this.setIndex('right');
}
// 此处结合了封装的拖拽组件,获取滑动的差值,判断滑动方向
touchEnd = (dis) => {
let that = this;
this.autoRun = false;
clearInterval(this.timer);
if(Math.abs(dis[this.direction]) > 60) {
dis[this.direction] > 0 ? this.clickNext() : this.clickPrev();
}
}
render() {
const sliderItem = [...this.data, this.data[0]];
return (
<div className="container" ref="container" >
<div className="prev" onClick={this.clickPrev}></div>
<div className="next" onClick={this.clickNext}></div>
<div ref="sliderContent" >
<Drag className="sliderContent" onDragEnd={this.touchEnd}>
{sliderItem.map((item,i) =>
<li className="sliderItem" key={i}>{item}</li>)
}
</Drag>
</div>
<Drag className="sliderContent" ref="sliderContent" onDragEnd={this.touchEnd}>
{sliderItem.map((item,i) =>
<li className="sliderItem" key={i}>{item}</li>)
}
</Drag>
</div>
)
}
}
export default Slider;
网友评论