/any-scroll

:rocket: 模拟scrollview, 支持pc/移动端, 让实现Tab/Slider等组件轻而易举.

Primary LanguageTypeScriptMIT LicenseMIT

any-scroll NPM Version NPM Downloads size-image

模拟滚动插件,支持滚轮和手势, 手势识别基于any-touch.

概念(wrap/content)

any-scroll的滚动实际是通过2个"父子div"的相对位置变化模拟的, 默认使用translate3d, 外层div叫"wrap", 里面用来装载内容的div叫"content".

<div> <!-- warp --> 
    <div> <!-- content -->
        content
    </div>
</div>

GIF

⚡⚡⚡ 多个content

如果wrap下有多个元素, 那么any-scroll会把他们都构造成content实例, 但是只有一个"激活"状态的content实例, 只有他可以响应滚动, 当然你也可以切换任意content实例为激活.

<!-- warp --> 
<div> 
    <!-- content -->
    <div>content-1<div> 

    <!-- content -->
    <div style="position: absolute;z-index:1;top:0;left:0;">
        content-2
    <div> 
    
    <!-- content -->
    <div no-scroll>content-3<div> 
</div>

注意:

  1. 多个content情况下, 请给第二个子元素做一个"初始定位", 比如style="position: absolute;z-index:1;top:0;left:0;", any-scroll内部没有默认定位, 这样你可以自定义"位置"和"层级".
  2. 如果不想某个子元素(content)"可滚动", 使用"no-scroll"进行标记.

🚀返回目录

目录

安装

npm i any-scroll -S

🚀返回目录

使用

首先在页面构造如下html结构, 同时给wrap一个固定尺寸.

<div id="scroll-view" style="height:600px;width:360px;"><!-- wrap -->
    <div><!-- content -->
        <!-- 你的内容写在这里 -->
    <div>
</div>

初始化:

import AnyScroll from 'any-scroll';
const el = document.getElementById('scroll-view');
const as = new AnyScroll(el);

// 滚动到x=-100,y=-100的位置.
as.scrollTo([-100,-100]);

// 只控制Y轴,1s内滑动到目标位置
as.scrollTo({y:-100},1000);

🚀返回目录

API

选项

名称 默认值 说明
allow [true,true] 是否允许X或Y轴滚动,数组第一位控制X轴
damping 0.1 dampScroll的消损系数,范围0~1
overflowDistance 100 允许超过边界的最大距离,单位"px"
render 查看 控制content元素位置变化的函数

render

默认通过translate3d控制content的位移, 如有需要也可改为控制margintop(left) 属性.

// 默认
function render(el, [x, y]) {
    el.style.setProperty('transform',`translate3d(${x}px, ${y}px,0)`);
}

// 或者改为
function render(el, [x, y]) {
    el.style.setProperty('marginLeft', x+'px');
    el.style.setProperty('marginTop', y+'px');
}

const as = new AnyScroll({render});

🚀返回目录

实例方法

on

监听事件, 默认继承了any-touch的所有手势事件.

事件名 说明
scroll 每次滚动
scroll-end 滚动停止
tap 单击
press 按压
pan 拖拽
swipe 快滑
rotate 旋转
pinch 缩放
as.on('scroll-end',()=>{
    console.log('滚动停止了');
})

🚀 返回目录

scrollTo

滚动到指定位置, 支持时间动画

参数
参数名 参考值 是否可选 说明
distXY [-100,-200]{x:-100,y:-200} 必选 目标位置
duration 1000 可选 滚动时间,单位毫秒
easing 参考 可选 缓动动画函数, 一般不需要修改
// 3秒钟移动到x=-100,y=-500的位置.
as.scrollTo([-100,-500],3000);
// 等价写法
as.scrollTo({x:100,y:-500},3000);

🚀 返回目录

moveTo

瞬间移动到目标位置

参数名 参考值 是否可选 说明
distXY [-100,-200]{x:-100,y:-200} 必选 目标位置
as.moveTo([-100,-200]);

🚀 返回目录

scrollToElement

移动元素到wrap左上角.

参数
参数名 参考值 是否可选 说明
el DOM元素 必选 目标元素
offset [0,0]{x:0,y:0} 可选 对目标位置修正, 多滚动的距离
duration 1000 可选 滚动时间,单位毫秒
easing 参考 可选 缓动动画函数, 一般不需要修改
// 滚动到content中的子元素(child-1), 让其左上角和wrap的左上角重合.
const childEl = doucument.getElementById('child-1');
as.scrollToElement(childEl);
// 滚动到child-1下方100px的位置
as.scrollToElement(childEl,{y:100});
// 等价写法
as.scrollToElement(childEl,[,100]);

🚀 返回目录

dampScroll

作用同scrollTo,只是滚动效果不同, 其不能指定时间. 仅供插件开发者使用, 模拟快速划动scrollView产生的滚动.

参数
参数名 参考值 是否可选 说明
distXY [-100,-200]{x:-100,y:-200} 必选 目标位置
damping 默认0.1, 范围0~1 可选 不断靠近目标位置,每次靠近剩余距离的0.1倍.
as.dampScroll([-100,-200]);

🚀 返回目录

update

如果滚动范围出现异常, 可手动更新.

as.update();

🚀 返回目录

getContentRef

获取content实例, 其上有尺寸等数据.

参数
参数名 参考值 是否可选 说明
elOrIndex number或HTML元素 可选 查看 ↓
elOrIndex不同的值
  1. 如是number, 那么会按照wrap下的子元素索引取对应的content实例.
  2. 如是元素, 那么会递归向上找父元素, 直到找到对应的实例.
  3. 如果不传默认找当前激活状态的content实例.
as.getContentRef(1);
as.getContentRef(childEl);
as.getContentRef();
返回值

content实例或null.

🚀 返回目录

active

如果有多个content实例, 激活指定content实例, 只有激活的content才会响应滚动.

参数
参数名 是否可选
contentRef 必选
// 激活第二个content实例.
const contentRef = as.getContentRef(1);
as.active(contentRef);

🚀 返回目录

实例属性

el

wrap元素

as.el; // <div class="any-scroll"></div>

🚀返回目录

at

any-scroll内部使用了any-touch手势库, 通过as.at可以访问any-touch的实例,从而修改手势行为等, 详情参考any-touch

// 仅对表单元素阻止触发"默认事件"
// 这是any-touch参数的默认值, 在此仅做展示
as.at.set({
    preventDefault(e){
        if (event.target && 'tagName' in event.target) {
            const { tagName } = event.target;
            return !/^(?:INPUT|TEXTAREA|BUTTON|SELECT)$/.test(tagName);
        }
        return false;
    }
})

注意: 普通开发者谨慎使用, 修改不当会影响滚动效果, 如开发中遇到缺少功能, 大家尽量先提Issue

🚀返回目录

size

wrap元素尺寸.

console.log(as.size);

🚀返回目录

事件

as.on('scroll', context=>{
    // 当前位置信息
    // context === as
    console.log(context.xy);
});
事件名称 说明
scroll 滚动
scrollEnd 滚动结束
tap 单击
press 按压
pressup 按压释放
pan 拖拽
swipe 滑动
pinch 缩放
rotate 旋转

除了"scroll"和"scroll-end", 其他事件都是any-touch实现的, 其事件对象上包含当前触点/距离/速度等信息, 更多请参考any-touch

as.on('tap', e=>{
    // 当前触点x坐标
    console.log(e.x)
})

🚀返回目录

进阶使用

支持更多手势

any-scroll基于any-touch开发, 所以也支持他的所有手势, 但是为了体积默认只加载了swipe/pan, 开启更多方法:

import AScroll from 'any-scroll';
import tap from '@any-touch/tap';
import swipe from '@any-touch/swipe';
import pinch from '@any-touch/pinch';
import rotate from '@any-touch/rotate';

const as = new AScroll();
// 加载单击手势
as.at.use(tap);
// 加载快划
as.at.use(swipe);
// 加载缩放
as.at.use(pinch);
// 加载旋转
as.at.use(rotate);

as.on('tap',e=>{

});

🚀返回目录

常见问题

no-scroll

如果wrap下的子元素, 有些你并不想让他"滚动", 可以给其加"no-scroll"标记.

<div>
    <div></div> <!-- 能滚动 -->
    <div no-scroll></div> <!-- 不能滚动 -->
</div>

🚀返回目录

监视内容变化

当content中的子元素发生变化(增/减去/尺寸), any-scroll需要重新计算"可滑动范围".

默认any-scroll内部使用ResizeObserver(caniuse)监视wrap/content尺寸变化, 实现自动更新可滑动范围.

image

但其兼容性较差, 所以在不支持ResizeObserver的浏览器会使用MutationObserver(caniuse)降级兼容, 其只能监视content的子元素的增/减, 从而更新滑动范围. image

resize-observer-polyfill

如果不在意体积, 可以使用"resize-observer-polyfill", 其可让ResizeObserver兼容到最低IE9.

import ResizeObserver from 'resize-observer-polyfill';
// ⭐注入到全局
window.ResizeObserver = ResizeObserver;

import AnyScroll from 'any-scroll';
const as = new AnyScroll(el);
手动更新

使用实例上的"update"方法更新"可滑动范围".

🚀返回目录