JS+CSS3模拟溢出滚动效果

作者:像素漫游

时间:2026-10-30

浏览:399

本文介绍如何使用jQuery封装一个模拟溢出滚动的插件。通过监听touch事件,利用CSS3的translate属性动态改变子元素位置,并限制滑动边界。该方案解决了移动端overflow:auto响应touch事件时的冲突问题,适用于fullpage等需要局部滚动的场景。

文章目录

背景与需求

在移动开发中,滑动交互十分常见,手机端主要依赖touch事件实现。近期遇到两个页面需要类似overflow:auto的滚动效果。虽然CSS直接设置即可实现(尽管视觉效果一般),但当overflow:auto元素需要响应touch事件时,会遇到诸多限制。例如在fullpage插件中,若某元素内部自滑动,可通过normalScrollElements配置使其不触发整屏切换;但当该元素滚动至底部时,便无法继续响应下一屏的上滑事件,反之亦然。为此,需要借助touch事件来处理交互。为了便于后续封装复用,这里简单封装一个jQuery插件,同时复习jQuery插件开发及touch事件的使用。

插件的核心思路如图所示:方框区域为固定宽高的父容器(设置overflow:hidden),子元素高度超过父容器。通过响应touch事件来改变子元素的translate值(使用top也可,但translate性能更优)。在此过程中,需判断子元素translate值的边界:最大值为0,最小值为子元素高度减去父元素高度。

注:若需实现子元素随手指移动,松开手指后自动回弹至边界的功能,需另行编写逻辑。

背景与需求

插件实现步骤

接下来逐步编写该插件。

第一步,建立安全的作用域。

作用域与扩展


;(function($){
//插入代码
})(jQuery) 
如果我们需要通过$(“#id”).xx()方法调用,需要像下面这样写,这里的fn及jquery的prototype;
$.extend({
exec:function(){}
}) //这种扩展通过$.exec()调用
$.fn.simuScroll = function(option){
var scrollObj=new simuScroll(this,option);
return scrollObj.init();
}

查看上述代码中的simuScroll方法,这是一个构造函数,接收两个参数:ele为当前监听touch事件的元素(通常设为父元素),option为用户传入的配置参数,包括需要改变translate的目标元素、父元素与子元素的高度、extra为额外增加的高度、滑动至顶部或底部时执行的回调函数等。

构造函数与配置


var simuScroll = function(ele,option){
this.settings = {
target : ele, //事件元素
changeTarget:'',
outerHeight : '800', //外层高度
innerHeight : '500', //内层高度
extra : '0', //额外增加的距离
swipeUp : null,
swipeDown : null,
vertical:true
};
//通过jquery.extend对默认的settings进行扩展,得出一个全新的对象
this.opt = jQuery.extend({},this.settings,option); 
this.initNumber = this.lastNumber = this.result = 0;
this.flag = false;
//计算出子元素与父元素距离差,以此来判定元素的最大滑动距离
this.diff = parseFloat(this.opt.innerHeight) - parseFloat(this.opt.outerHeight) + parseFloat(this.opt.extra);
this.diff = this.diff < 0 ? 0 :this.diff;
this.direction = '';
}; 

在扩展jQuery prototype时,第一步是实例化上述构造函数,第二步直接调用构造函数的init方法,具体可见下方注释。

初始化与事件绑定


init:function(){
//jquery对象直接绑定touch事件获取event需originalEvent获取原生对象的event属性
//jquery对象 获取pagex event.originalEvent.touches[0].pageX 
//dom对象 event.touches[0].pageX 
var target = this.opt.target.get(0); 
//如果子元素高度大于父元素,则执行滑动事件,否则执行回调函数
this.flag = this.diff > 0 ? 'translate' : 'exec';
/*如果元素已设置transform中 translate,scale,skew,rotate中的任何一项,
则我们再直接设置transform:translateY(10px)时会覆盖掉初始设置的属性
所以我们通过获取元素的matrix值既保证原有属性,又能设置任意值*/
var cssText = this.opt.changeTarget.css('-webkit-transform');
if(cssText=='none'){ //元素未设置transform属性
this.str = this.opt.vertical ? 'translateY' : 'translateX' ;
}else{
this.str = cssText ;
var reg = /-?\d+\.?\d*/g; //正则表达式
/*获取matrix中各参数
直接获取到的matrix值为类似matrix(1,0,0,1,0,0)
获取到的为[1,0,0,1,0,0] 这样我们就可以随意的修改某一个值
数组的第5项为元素的translateX值,第6项为translateY值 */
this.params = this.str.match(reg); 
this.matrix = this.opt.vertical ? 6 : 5 ; //根据传入的参数确定要修改的值为translateY或tranlateX
} 
var _this = this;
//父元素绑定touch事件,传递事件对象及context
target.addEventListener('touchstart',function(e){
_this.start(e,_this)
});
target.addEventListener('touchmove',function(e){
_this.move(e,_this)
});
target.addEventListener('touchend',function(e){
_this.end(e,_this)
});
}

touchstart方法相对简单,根据预期的滑动方向,记录手指接触屏幕时的初始坐标值。

触摸开始处理


start:function(e,context){
e.preventDefault();
var touches = e.touches[0];
context.initNumber = context.opt.vertical ? touches.pageY : touches.pageX;
},

touchmove方法主要根据手指滑动的距离,动态改变元素的CSS属性,使元素能够跟随手指移动。

setNumber方法仅包含一行代码:this.params.splice(n-1,1,number),用于根据传入的参数修改matrix数组。

触摸移动与边界限制


move:function(e,context){
e.preventDefault();
var touches = e.touches[0];
number = context.opt.vertical ? touches.pageY : touches.pageX;
//获取相对手指触碰屏幕的变化值
var delta = number - context.initNumber;
if(context.flag == 'translate'){ //如果需要滑动
context.result = context.lastNumber + delta;
//设置滑动的最大值和最小值
context.result = context.result > 0 ? 0 : context.result ;
context.result = -context.result > context.diff ? -context.diff : context.result;
//动态设置元素css属性
if(context.matrix){
switch (context.matrix) {
case 6: 
context.setNumber(6,context.result)
break;
case 5:
context.setNumber(5,context.result)
break;
}
context.opt.changeTarget.css('-webkit-transform','matrix('+context.params.join(',')+')')
}else{
context.opt.changeTarget.css('-webkit-transform',context.str+'('+context.result+'px)')
}
}
},

touchend则判断释放手指时是否需要执行回调函数。

触摸结束与回弹逻辑


end:function(e,context){
e.preventDefault();
var touches = e.changedTouches[0];
var number = context.opt.vertical ? touches.pageY : touches.pageX,
n = number - context.initNumber;
//当元素滑动距离大于10 并且最初元素translate值为0同时回调函数存在,则执行回调函数
if(n>10 && context.lastNumber == 0 && context.opt.swipeDown){ 
context.opt.swipeDown()
}else if(n<-10 && context.lastNumber == -context.diff && context.opt.swipeUp){
context.opt.swipeUp();
}
//将常量设为结果值,能保证多次连贯滑动
context.lastNumber = context.result;
}

至此,一个简单的jQuery插件基本完成,框架如下。


;(function($){
var a = function(m,n){
//函数内容
};
a.prototype = {
init:function(){
},
start:function(){
},
move:function(){
},
end:function(){
},
setNumber:function(){
}
}
$.fn.scrol = function(o){
var obj = new a(this,o);
return obj.init()
}
})(jQuery) 

调用方式如下。


$(".outer").simuScroll({
'outerHeight':$(".outer").height(),
'innerHeight':$('.inner').height(),
'changeTarget' : $(".inner"),
'swipeUp' :function(){
console.log('up')
},
'swipeDown' :function(){
console.log('down')
},
vertical:true
})

以上介绍了JS+CSS3模拟溢出滚动效果的实现方法,希望对大家有所帮助。如有任何疑问,欢迎留言,小编会及时回复。在此感谢大家对本站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章