H5焦点图

焦点图算是最基础的控件了,很多地方都在用,在网上找了一圈,没发现原生JS的,都是各种框架和库,正好自己写一个,发现里面小门道还挺多的,想要体验好,各种细节都要去抠才行:
1、大家都知道,一般翻页都是滑动距离超过 50% 才翻页,但如果快速滑动,即使滑动距离很短,也是可以翻页的,
这里就需要记录滑动开始的时间和滑动结束的时间,还要记录滑动的距离,设定一个范围值就ok,我设的是滑动耗时在150毫秒以下,距离在60以上,切换焦点图。
2、手机滑动焦点图,方向肯定不是很标准的横或竖,左或右。
所以呢,这里就需要计算滑动的方向,也就是角度,根据角度来判断往前翻,还是往后翻,为了体验好,例如大于-45度并且小于45度的,都算往右滑动,查看详细说明文章
3、体验好的焦点图,都是可以无限循环翻页的。
实现的方式也比较取巧,首先要克隆第一张图,并插入到父节点的最后,当翻到最后一页,再往后翻的话,就会翻到克隆的这一页,然后如果再往后翻,当 touchstart 刚触发的时候,会把图重置到第一幅图(第一和最后的图都一样,所以不会察觉到),这样,便实现了循环。如果要从第一幅图,往前滑动(从 dom 节点上看,第一幅图前面是克隆的图),就把 transform 的值,直接设成真实的最后一幅图的值,就行了。
4、仔细体验一下,当你在滑动焦点图的时候,页面是无法上下滚动的。在页面上下滚动的时候,你又无法滑动焦点图。
这需要在滑动的时候设置一个开关变量,来控制 touchmove 的时候,是否执行代码,还要 touchmove 的时候阻止浏览器默认事件 event.stopPropagation(),来实现滑动焦点图的时候,页面无法上下滚动;
前面说过了,要通过计算滑动的角度,来分出4个方向,上下左右。只有左右两个方向,会执行 touchmove 的代码,页面会滚动,上下两个方向不执行代码,页面不滚动,也就实现了页面上下滚动,无法滑动焦点图。

附上代码:
[code lang=”js”]
//===================================================【焦点图组件】
//构造函数
function SLIDESHOW(container,params){
this.dom = container; //焦点图元素
this.pagination = params.pagination; //是否显示指示器
this.loop = params.loop; //是否循环显示
this.silderShow = this.dom.getElementsByClassName("slides")[0]; //焦点图容器
this.btnWarp = this.dom.getElementsByClassName("btn")[0]; //指示器容器
this.imgWidth = parseInt(this.dom.offsetWidth); //焦点图容器的宽度(移动的单位距离)
this.imgNum = this.silderShow.getElementsByTagName("li").length; //焦点图的数量
this.activeNum = 1; //指示器
this.dataTransform = 0; //保存偏移值
var $slides = this.silderShow; //焦点图容器
var touchstart_x = 0; //滑动开始时的x坐标
var touchstart_y = 0; //滑动开始时的y坐标
var touchmoveX = 0; //滑动的x轴距离
var touchmove_x = 0; //滑动时的x轴距离
var touchmove_y = 0; //滑动时的y轴距离
var touchend_x = 0; //滑动结束时的x坐标
var moveSW = false; //是否滑动的开关
var touchstart_time = 0; //滑动开始时的时间
var touchend_time = 0; //滑动结束时的时间
var touch_time = 0; //滑动耗时
var sw = false; //如果滑动耗时在150毫秒以下,距离在60以上,切换焦点图
var touch_fx = false; //根据起点和终点返回方向
var t = this;
//创建指示器
if(!this.pagination){
this.btnWarp.style.display = "none";
}
var html = "";
for(var i=0;i<this.imgNum;i++){
html += "<li></li>";
}
this.btnWarp.innerHTML = "<ul>" + html + "</ul>";
this.btnWarp.getElementsByTagName("li")[0].className = "active";
//创建克隆DOM
if(this.loop){
var sourceNode = this.silderShow.getElementsByTagName("li")[0]; // 获得被克隆的节点对象
var clonedNode = sourceNode.cloneNode(true); // 克隆节点
sourceNode.parentNode.appendChild(clonedNode); // 在父节点插入克隆的节点
}
//滑动开始
this.silderShow.addEventListener(‘touchstart’, function(event) {
//重置
touch_time = 0;
touchmoveX = 0;
//阻止事件冒泡
event.stopPropagation();
//打开滑动的开关
moveSW = true;
//关闭过渡效果
$slides.style.transitionDuration = "0s";
//记录坐标
touchstart_x = event.changedTouches[0].pageX;
touchstart_y = event.changedTouches[0].pageY;
//记录时间
touchstart_time = new Date();
}, false);
//滑动中
this.silderShow.addEventListener(‘touchmove’, function(event) {
//阻止事件冒泡
event.stopPropagation();
if(moveSW){
//记录坐标
touchmove_x = event.changedTouches[0].pageX;
touchmove_y = event.changedTouches[0].pageY;
//每次滑动只获取一次
if(!touch_fx){
//根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
touch_fx = getSlideDirection(touchstart_x,touchstart_y,touchmove_x,touchmove_y);
}
//左右滑动焦点图
if(touch_fx === 3 || touch_fx === 4){
//阻止浏览器默认动作
event.preventDefault();
//滑动距离
touchmoveX = parseInt(event.changedTouches[0].pageX) – parseInt(touchstart_x);
var x = t.dataTransform + touchmoveX;
$slides.style.transform = "translate3d(" + x + "px,0,0)";
if(t.loop){
//图片循环显示
//向前翻循环
if(x > 0 && x < 50){
//重置到最后一张
t.dataTransform = t.imgWidth * t.imgNum * -1;
}
//向后翻循环
if(x < (t.imgWidth * t.imgNum * -1)){
//重置到第一张
t.dataTransform = 0;
}
}
}
//上下滚动页面
else{
moveSW = false;
}
}
}, false);
//滑动结束
this.silderShow.addEventListener(‘touchend’, function(event) {
//阻止事件冒泡
event.stopPropagation();
//关闭滑动的开关
moveSW = false;
//删除获取的滑动方向
touch_fx = false;
//打开过度效果
$slides.style.transitionDuration = "0.5s";
//记录坐标
touchend_x = event.changedTouches[0].pageX;
//记录时间
touchend_time = new Date();
//记录滑动耗时
touch_time = touchend_time.getTime() – touchstart_time.getTime();
//如果滑动耗时在150毫秒以下,距离在60以上,切换焦点图
if(touch_time <= 150 && Math.abs(touchmoveX) > 60){
sw = true;
}else{
sw = false;
}
//发送移动的数值
t.domMove(touchmoveX,sw);
}, false);
}
//焦点图容器的移动
SLIDESHOW.prototype.domMove = function(num,sw){
var moveNum;
var index;
var needWidth = this.imgWidth / 2;
//滑动超过半个宽度,或有特殊指令,切换焦点图
if(Math.abs(num) > needWidth || sw){
//上一个
if(num > 0 && (this.loop ? true : this.activeNum > 1)){
//if(num > 0){
moveNum = this.dataTransform + this.imgWidth * 1;
this.moveAnimate(moveNum);
}
//下一个
else if(num < 0 && (this.loop ? true : this.activeNum < this.imgNum)){
//else if(num < 0){
moveNum = this.dataTransform + this.imgWidth * -1;
this.moveAnimate(moveNum);
}
//不动
else{
this.moveAnimate(this.dataTransform);
}
}else{
//不动
this.moveAnimate(this.dataTransform);
}
};
//焦点图的移动动画
SLIDESHOW.prototype.moveAnimate = function(moveNum){
//移动动画
this.silderShow.style.transform = "translate3d(" + moveNum + "px,0,0)";
this.dataTransform = moveNum;
//当前下标值
this.activeNum = Math.abs(this.dataTransform) / this.imgWidth + 1;
//指示器动画
var $btnLi = this.btnWarp.getElementsByTagName("li");
for(var i=0;i<$btnLi.length;i++){
$btnLi[i].className = "";
}
var showIndex = this.activeNum > this.imgNum ? 0 : this.activeNum – 1;
$btnLi[showIndex].className = "active";
};
//根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
function getSlideDirection(startX,startY,endX,endY) {
var dy = startY – endY;
var dx = endX – startX;
var result = 0;
//如果滑动距离太短
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
//未滑动
return result;
}
//返回滑动的角度
var angle = Math.atan2(dy,dx) * 180 / Math.PI;;
if (angle >= -45 && angle < 45) {
//向右
result = 4;
}
else if (angle >= 45 && angle < 135) {
//向上
result = 1;
}
else if (angle >= -135 && angle < -45) {
//向下
result = 2;
}
else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
//向左
result = 3;
}
return result;
}
[/code]
使用方法也很简单:
[code lang=”js”]
var mySlidShow = new SLIDESHOW($dom,{
//是否显示指示器
pagination: true,
//是否循环显示
loop: true,
});
[/code]
猛击demo ☻

发表评论

电子邮件地址不会被公开。 必填项已用*标注