npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

iwe7-ng-better-scroll

v1.0.2

Published

> better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll 的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又扩展了一些 feature 以及做了一些性能优化。 > better-scroll 是基于原生 JS 实现的,不依赖任何框架。它编译后的代码大小是 63kb,压缩后是 35kb,gzip 后仅有 9kb,是一款非常轻量的 JS lib。

Downloads

6

Readme

better-scroll 是什么

better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll 的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又扩展了一些 feature 以及做了一些性能优化。 better-scroll 是基于原生 JS 实现的,不依赖任何框架。它编译后的代码大小是 63kb,压缩后是 35kb,gzip 后仅有 9kb,是一款非常轻量的 JS lib。

github 源码

安装

yarn add iwe7-ng-better-scroll

api

使用

import {
  NgBetterScrollModule,
  BetterScrollConfigDefault
} from "iwe7-ng-better-scroll";

@NgModule({
  imports: [
    ...
    NgBetterScrollModule.forRoot(BetterScrollConfigDefault)
    ...
  ]
})
export class AppModule {}

在模板中使用

<better-scroll (onPullingDown)="init($event)"
  (onPullingUp)="loadMore($event)">
  <ul>
    <li *ngFor="let item of list">
      {{item}}
    </li>
  </ul>
</better-scroll>
import {
  Component,
  OnInit,
  ElementRef,
  InjectionToken,
  Inject,
  AfterViewInit,
  ViewChild,
  Input,
  OnChanges,
  SimpleChanges,
  EventEmitter,
  Output,
  OnDestroy
} from "@angular/core";
// import BScroll from "better-scroll";
// 为了兼容才这样写
import * as _better_scroll from "better-scroll";
const BScroll = (_better_scroll as any).default || _better_scroll;

export const BETTER_SCROLL_CONFIG = new InjectionToken("BETTER_SCROLL_CONFIG");
import { CdkObserveContent } from "@angular/cdk/observers";

export const BetterScrollConfigDefault = {
  startX: 0,
  startY: 0,
  scrollX: false,
  scrollY: true,
  freeScroll: false,
  directionLockThreshold: 5,
  eventPassthrough: "",
  click: false,
  tap: false,
  bounce: true,
  bounceTime: 800,
  momentum: true,
  momentumLimitTime: 300,
  momentumLimitDistance: 15,
  swipeTime: 2500,
  swipeBounceTime: 500,
  deceleration: 0.001,
  flickLimitTime: 200,
  flickLimitDistance: 100,
  resizePolling: 60,
  probeType: 0,
  preventDefault: true,
  preventDefaultException: {
    tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
  },
  HWCompositing: true,
  useTransition: true,
  useTransform: true,
  bindToWrapper: false,
  observeDOM: true,
  autoBlur: true,
  wheel: false,
  snap: false,
  scrollbar: false,
  pullDownRefresh: {
    threshold: 50,
    stop: 20
  },
  pullUpLoad: { threshold: 50 },
  mouseWheel: {
    speed: 20,
    invert: false
  },
  stopPropagation: false
};

export interface BetterScrollWhellInterface {
  selectedIndex: number;
  rotate: number;
  adjustTime: number;
  wheelWrapperClass: string;
  wheelItemClass: string;
}

export interface BetterScrollEasingInterface {
  style: string;
  fn: Function;
}

export interface BetterScrollSnapInterface {
  loop: boolean;
  threshold: number;
  stepX: number;
  stepY: number;
  easing: BetterScrollEasingInterface;
}

export interface BetterScrollScrollbarInterface {
  fade: boolean;
  interactive: boolean;
}

export interface BetterScrollPullDownRefreshInterface {
  threshold: number;
  stop: number;
}

export interface BetterScrollPullUpLoadInterface {
  threshold: number;
}

export interface BetterScrollMouseWheelInterface {
  speed: number;
  invert: boolean;
}

@Component({
  selector: "better-scroll",
  templateUrl: "./better-scroll.component.html",
  styleUrls: ["./better-scroll.component.scss"]
})
export class BetterScrollComponent implements OnInit, OnChanges, OnDestroy {
  scroll: any;
  // 配置选项
  // 横轴方向初始化位置。
  @Input() startX: number = 0;
  // 纵轴方向初始化位置
  @Input() startY: number = 0;
  // 当设置为 true 的时候,可以开启横向滚动
  @Input() scrollX: boolean = false;
  // 当设置为 true 的时候,可以开启纵向滚动
  @Input() scrollY: boolean = true;
  // 有些场景我们需要支持横向和纵向同时滚动,而不仅限制在某个方向,这个时候我们只要设置 freeScroll 为 true 即可
  @Input() freeScroll: boolean = false;
  // 当我们需要锁定只滚动一个方向的时候,我们在初始滚动的时候根据横轴和纵轴滚动的绝对值做差,当差值大于 directionLockThreshold 的时候来决定滚动锁定的方向。
  @Input() directionLockThreshold: number = 5;
  // 有时候我们使用 better-scroll 在某个方向模拟滚动的时候,希望在另一个方向保留原生的滚动(比如轮播图,我们希望横向模拟横向滚动,而纵向的滚动还是保留原生滚动,我们可以设置 eventPassthrough 为 vertical;相应的,如果我们希望保留横向的原生滚动,可以设置eventPassthrough为 horizontal)
  @Input() eventPassthrough: string = "";
  // better-scroll 默认会阻止浏览器的原生 click 事件。当设置为 true,better-scroll 会派发一个 click 事件,我们会给派发的 event 参数加一个私有属性 _constructed,值为 true。
  @Input() click: boolean = false;
  // 因为 better-scroll 会阻止原生的 click 事件,我们可以设置 tap 为 true,它会在区域被点击的时候派发一个 tap 事件,你可以像监听原生事件那样去监听它,如 element.addEventListener('tap', doSomething, false);。如果 tap 设置为字符串, 那么这个字符串就作为自定义事件名称。如 tap: 'myCustomTapEvent'。
  @Input() tap: boolean = false;
  // 当滚动超过边缘的时候会有一小段回弹动画。设置为 true 则开启动画。
  @Input() bounce: boolean = true;
  // 设置回弹动画的动画时长。
  @Input() bounceTime: number = 800;
  // 当快速在屏幕上滑动一段距离的时候,会根据滑动的距离和时间计算出动量,并生成滚动动画。设置为 true 则开启动画。
  @Input() momentum: boolean = true;
  // 只有在屏幕上快速滑动的时间小于 momentumLimitTime,才能开启 momentum 动画
  @Input() momentumLimitTime: number = 300;
  // 只有在屏幕上快速滑动的距离大于 momentumLimitDistance,才能开启 momentum 动画。
  @Input() momentumLimitDistance: number = 15;
  // 设置 momentum 动画的动画时长。
  @Input() swipeTime: number = 2500;
  // 设置当运行 momentum 动画时,超过边缘后的回弹整个动画时间
  @Input() swipeBounceTime: number = 500;
  // 表示 momentum 动画的减速度。
  @Input() deceleration: number = 0.001;
  // 有的时候我们要捕获用户的轻拂动作(短时间滑动一个较短的距离)。只有用户在屏幕上滑动的时间小于 flickLimitTime ,才算一次轻拂。
  @Input() flickLimitTime: number = 200;
  // 只有用户在屏幕上滑动的距离小于 flickLimitDistance ,才算一次轻拂。
  @Input() flickLimitDistance: number = 100;
  // 当窗口的尺寸改变的时候,需要对 better-scroll 做重新计算,为了优化性能,我们对重新计算做了延时。60ms 是一个比较合理的值。
  @Input() resizePolling: number = 60;
  // 有时候我们需要知道滚动的位置。当 probeType 为 1 的时候,会非实时(屏幕滑动超过一定时间后)派发scroll 事件;当 probeType 为 2 的时候,会在屏幕滑动的过程中实时的派发 scroll 事件;当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。如果没有设置该值,其默认值为 0,即不派发 scroll 事件。
  @Input() probeType: number = 0;
  // 当事件派发后是否阻止浏览器默认行为。这个值应该设为 true,除非你真的知道你在做什么,通常你可能用到的是 preventDefaultException。
  @Input() preventDefault: boolean = true;
  // better-scroll 的实现会阻止原生的滚动,这样也同时阻止了一些原生组件的默认行为。这个时候我们不能对这些元素做 preventDefault,所以我们可以配置 preventDefaultException。默认值 {tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/}表示标签名为 input、textarea、button、select 这些元素的默认行为都不会被阻止。
  @Input()
  preventDefaultException: any = {
    tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
  };
  // 是否开启硬件加速,开启它会在 scroller 上添加 translateZ(0) 来开启硬件加速从而提升动画性能,有很好的滚动效果。
  @Input() HWCompositing: boolean = true;
  // 是否使用 CSS3 transition 动画。如果设置为 false,则使用 requestAnimationFrame 做动画。
  @Input() useTransition: boolean = true;
  // 是否使用 CSS3 transform 做位移。如果设置为 false, 则设置元素的 top/left (这种情况需要 scroller 是绝对定位的)。
  @Input() useTransform: boolean = true;
  // move 事件通常会绑定到 document 上而不是滚动的容器上,当移动的过程中光标或手指离开滚动的容器滚动仍然会继续,这通常是期望的。当然你也可以把 move 事件绑定到滚动的容器上,bindToWrapper 设置为 true 即可,这样一旦移动的过程中光标或手指离开滚动的容器,滚动会立刻停止。
  @Input() bindToWrapper: boolean = false;
  // 当在移动端环境(支持 touch 事件),disableMouse 会计算为 true,这样就不会监听鼠标相关的事件,而在 PC 环境,disableMouse 会计算为 false,就会监听鼠标相关事件,不建议修改该属性,除非你知道你在做什么。
  @Input() disableMouse: boolean;
  // 当在移动端环境(支持 touch 事件),disableTouch 会计算为 false,这样会监听 touch 相关的事件,而在 PC 环境,disableTouch 会计算为 true,就不会监听 touch 相关事件。不建议修改该属性,除非你知道你在做什么。
  @Input() disableTouch: boolean;
  // 会检测 scroller 内部 DOM 变化,自动调用 refresh 方法重新计算来保证滚动的正确性。它会额外增加一些性能开销,如果你能明确地知道 scroller 内部 DOM 的变化时机并手动调用 refresh 重新计算,你可以把该选项设置为 false。
  @Input() observeDOM: boolean = false;
  // 在滚动之前会让当前激活的元素(input、textarea)自动失去焦点
  @Input() autoBlur: boolean = true;
  // 是否阻止事件冒泡。
  @Input() stopPropagation: boolean = false;

  // ----高级 ----- //
  @Input() wheel: BetterScrollWhellInterface | boolean = false;
  @Input() snap: BetterScrollSnapInterface | boolean = false;
  @Input() scrollbar: BetterScrollScrollbarInterface | boolean = false;
  @Input()
  pullDownRefresh: BetterScrollPullDownRefreshInterface | boolean = {
    threshold: 50,
    stop: 20
  };
  @Input()
  pullUpLoad: BetterScrollPullUpLoadInterface | boolean = { threshold: 50 };
  @Input()
  mouseWheel: BetterScrollMouseWheelInterface | boolean = {
    speed: 20,
    invert: false
  };

  // outputs
  @Output() onBeforeScrollStart: EventEmitter<this> = new EventEmitter();
  @Output() onScroll: EventEmitter<BScroll.Position> = new EventEmitter();

  // 组件内部
  // 是否已经创建了
  isCreated: boolean = false;

  @Output() onScrollCancel: EventEmitter<any> = new EventEmitter();
  @Output() onScrollEnd: EventEmitter<BScroll.Position> = new EventEmitter();
  @Output() onTouchEnd: EventEmitter<BScroll.Position> = new EventEmitter();
  @Output() onFlick: EventEmitter<any> = new EventEmitter();
  @Output() onRefresh: EventEmitter<any> = new EventEmitter();
  @Output() onDestroy: EventEmitter<any> = new EventEmitter();

  @Output() onPullingDown: EventEmitter<this> = new EventEmitter();
  @Output() onPullingUp: EventEmitter<this> = new EventEmitter();

  constructor(
    // 通过注入的ElementRef访问dom
    public ele: ElementRef,
    // 通过注入 配置我们的默认参数
    @Inject(BETTER_SCROLL_CONFIG) public _default: any
  ) {}
  /**
   * 如果配置改变并且已经创建了 那么销毁重新创建
   */
  ngOnChanges(changes: SimpleChanges) {
    let hasChanged: boolean = false;
    for (let key in changes) {
      this._default[key] = changes[key].currentValue;
      hasChanged = true;
    }
    if (hasChanged && this.isCreated) {
      this.destroy();
      this.createScroll();
    }
  }
  /**
   * 注销移除
   */
  ngOnDestroy() {
    this.destroy();
  }

  ngOnInit() {}

  ngAfterViewInit() {
    this.createScroll();
  }

  /**
   * 监控dom内容变更
   * 刷新better scroll
   */
  changeContent(e: any) {
    this.refresh();
  }
  /**
   * 初始化better scroll
   */
  createScroll() {
    this.scroll = new BScroll(this.ele.nativeElement, this._default);
    // 已经创建了
    this.isCreated = true;
    this.scroll.on("beforeScrollStart", () => {
      this.onBeforeScrollStart.emit(this);
    });
    this.scroll.on("scroll", (pos: BScroll.Position) => {
      this.onScroll.emit(pos);
    });
    this.scroll.on("scrollCancel", () => {
      this.onScrollCancel.emit();
    });
    this.scroll.on("scrollEnd", (pos: BScroll.Position) => {
      this.onScrollEnd.emit(pos);
    });
    this.scroll.on("touchEnd", (pos: BScroll.Position) => {
      this.onTouchEnd.emit(pos);
    });
    this.scroll.on("flick", () => {
      this.onFlick.emit();
    });
    this.scroll.on("refresh", () => {
      this.onRefresh.emit();
    });
    this.scroll.on("destroy", () => {
      this.onDestroy.emit();
    });
    this.scroll.on("pullingDown", () => {
      this.onPullingDown.emit(this);
    });
    this.scroll.on("pullingUp", () => {
      this.onPullingUp.emit(this);
    });
  }

  // 刷新状态
  refresh(): this {
    this.scroll.refresh();
    return this;
  }
  // 启用 better-scroll, 默认 开启
  enable(): this {
    this.scroll.enable();
    return this;
  }
  // 禁用 better-scroll,DOM 事件(如 touchstart、touchmove、touchend)的回调函数不再响应
  disable(): this {
    this.scroll.disable();
    return this;
  }
  // 相对于当前位置偏移滚动 x,y 的距离
  scrollBy(x: number, y: number, time?: number, easing?: object): this {
    this.scroll.scrollBy(x, y, time, easing);
    return this;
  }
  // 滚动到指定的位置
  scrollTo(x: number, y: number, time?: number, easing?: object): this {
    this.scroll.scrollTo(x, y, time, easing);
    return this;
  }
  // 滚动到指定的目标元素
  scrollToElement(
    el: HTMLElement | string,
    time?: number,
    offsetX?: number | boolean,
    offsetY?: number | boolean,
    easing?: object
  ): this {
    this.scroll.scrollToElement(el, time, offsetX, offsetY, easing);
    return this;
  }
  // 立即停止当前运行的滚动动画
  stop(): this {
    this.scroll.stop();
    return this;
  }
  // 销毁 better-scroll,解绑事件
  destroy(): this {
    this.scroll.destroy();
    return this;
  }

  // 当我们做 slide 组件的时候,slide 通常会分成多个页面。调用此方法可以滚动到指定的页面。
  goToPage(x: number, y: number, time?: number, easing?: object): this {
    this.scroll.goToPage(x, y, time, easing);
    return this;
  }
  // 滚动到下一个页面
  next(time?: number, easing?: object): this {
    this.next(time, easing);
    return this;
  }
  // 滚动到上一个页面
  prev(time?: number, easing?: object): this {
    this.scroll.prev(time, easing);
    return this;
  }
  // 获取当前页面的信息
  getCurrentPage(): {
    x: number;
    y: number;
    pageX: number;
    pageY: number;
  } {
    return this.scroll.getCurrentPage();
  }
  // 当我们做 picker 组件的时候,调用该方法可以滚动到索引对应的位置
  wheelTo(index: number): this {
    this.scroll.wheelTo(index);
    return this;
  }
  // 获取当前选中的索引值
  getSelectedIndex(): number {
    return this.scroll.getSelectedIndex();
  }
  // 当下拉刷新数据加载完毕后,需要调用此方法告诉 better-scroll 数据已加载
  finishPullDown(): this {
    this.scroll.finishPullDown();
    return this;
  }
  // 当上拉加载数据加载完毕后,需要调用此方法告诉 better-scroll 数据已加载
  finishPullUp(): this {
    this.scroll.finishPullUp();
    return this;
  }
  trigger(type: string): this {
    this.scroll.trigger();
    return this;
  }
}