diff --git a/packages/examples/vue/components/pulldown/sina-weibo.vue b/packages/examples/vue/components/pulldown/sina-weibo.vue new file mode 100644 index 00000000..82af6912 --- /dev/null +++ b/packages/examples/vue/components/pulldown/sina-weibo.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/packages/examples/vue/pages/pulldown-entry.vue b/packages/examples/vue/pages/pulldown-entry.vue index 0c522015..91a85487 100644 --- a/packages/examples/vue/pages/pulldown-entry.vue +++ b/packages/examples/vue/pages/pulldown-entry.vue @@ -1,16 +1,24 @@ - - - \ No newline at end of file diff --git a/packages/examples/vue/router/index.js b/packages/examples/vue/router/index.js index cd1a25af..56115d0b 100644 --- a/packages/examples/vue/router/index.js +++ b/packages/examples/vue/router/index.js @@ -68,6 +68,9 @@ import ComposeSlideNested from 'vue-example/components/compose/slide-nested' import IndicatorsMinimap from 'vue-example/components/indicators/minimap' import IndicatorsParallaxScroll from 'vue-example/components/indicators/parallax-scroll' + +import PulldownDefault from 'vue-example/components/pulldown/default' +import PulldownSinaWeibo from 'vue-example/components/pulldown/sina-weibo' Vue.use(Router) export default new Router({ @@ -199,6 +202,16 @@ export default new Router({ { path: '/pulldown', component: PullDownEntry, + children: [ + { + path: 'default', + component: PulldownDefault + }, + { + path: 'sina', + component: PulldownSinaWeibo + } + ] }, { path: '/scrollbar', diff --git a/packages/pull-down/src/__tests__/index.spec.ts b/packages/pull-down/src/__tests__/index.spec.ts index eb478fb9..dfebec1c 100644 --- a/packages/pull-down/src/__tests__/index.spec.ts +++ b/packages/pull-down/src/__tests__/index.spec.ts @@ -100,6 +100,7 @@ describe('pull down tests', () => { const mockFn = jest.fn() scroll.on(scroll.eventTypes.pullingDown, mockFn) + scroll.trigger(scroll.eventTypes.scrollStart) // simulate pullUp action scroll.y = -100 scroll.scroller.hooks.trigger(scroll.scroller.hooks.eventTypes.end) @@ -112,11 +113,31 @@ describe('pull down tests', () => { expect(mockFn).toHaveBeenCalledTimes(1) }) + it('should checkLocationOfThresholdBoundary', () => { + const enterThresholdFn = jest.fn() + const leaveThresholdFn = jest.fn() + scroll.on(scroll.eventTypes.enterThreshold, enterThresholdFn) + scroll.on(scroll.eventTypes.leaveThreshold, leaveThresholdFn) + scroll.trigger(scroll.eventTypes.scrollStart) + + // enter threshold boundary + scroll.y = 20 + scroll.trigger(scroll.eventTypes.scroll) + + // leave threshold boundary + scroll.y = 100 + scroll.trigger(scroll.eventTypes.scroll) + + expect(enterThresholdFn).toHaveBeenCalledTimes(1) + expect(leaveThresholdFn).toHaveBeenCalledTimes(1) + }) + it('should trigger pullingDown once', () => { const mockFn = jest.fn() scroll.on(scroll.eventTypes.pullingDown, mockFn) // when scroll.y = 100 + scroll.trigger(scroll.eventTypes.scrollStart) scroll.scroller.hooks.trigger('end') scroll.scroller.hooks.trigger('end') // then @@ -136,10 +157,10 @@ describe('pull down tests', () => { }) it('should work well when call finishPullDown()', () => { - pullDown.pulling = true + pullDown.pulling = 2 pullDown.finishPullDown() - expect(pullDown.pulling).toBe(false) + expect(pullDown.pulling).toBe(0) expect(scroll.scroller.scrollBehaviorY.computeBoundary).toBeCalled() expect(scroll.resetPosition).toBeCalled() }) @@ -196,7 +217,7 @@ describe('pull down tests', () => { it('should call finishPullDown when content DOM changed', () => { // simulate pullDown action - pullDown.pulling = true + pullDown.pulling = 2 scroll.hooks.trigger(scroll.hooks.eventTypes.contentChanged) expect(scroll.scroller.scrollBehaviorY.computeBoundary).toBeCalled() diff --git a/packages/pull-down/src/index.ts b/packages/pull-down/src/index.ts index 318dd1d1..dc8f1351 100644 --- a/packages/pull-down/src/index.ts +++ b/packages/pull-down/src/index.ts @@ -5,6 +5,22 @@ import propertiesConfig from './propertiesConfig' export type PullDownRefreshOptions = Partial | true +// pulldownRefresh phase will go through: +// DEFAULT -> MOVING -> FETCHING +// or +// DEFAULT -> MOVING +const enum PullDownPhase { + DEFAULT, + MOVING, + FETCHING, +} + +const enum ThresholdBoundary { + DEFAULT, + INSIDE, + OUTSIDE, +} + export interface PullDownRefreshConfig { threshold: number stop: number @@ -26,12 +42,15 @@ interface PluginAPI { autoPullDownRefresh(): void } -const PULL_DOWN_HOOKS_NAME = 'pullingDown' +const PULLING_DOWN_EVENT = 'pullingDown' +const ENTER_THRESHOLD_EVENT = 'enterThreshold' +const LEAVE_THRESHOLD_EVENT = 'leaveThreshold' export default class PullDown implements PluginAPI { static pluginName = 'pullDownRefresh' private hooksFn: Array<[EventEmitter, string, Function]> - pulling: boolean = false + pulling: PullDownPhase = PullDownPhase.DEFAULT + thresholdBoundary: ThresholdBoundary = ThresholdBoundary.DEFAULT watching: boolean options: PullDownRefreshConfig cachedOriginanMinScrollY: number @@ -41,6 +60,14 @@ export default class PullDown implements PluginAPI { this.init() } + private setPulling(status: PullDownPhase) { + this.pulling = status + } + + private setThresholdBoundary(boundary: ThresholdBoundary) { + this.thresholdBoundary = boundary + } + private init() { this.handleBScroll() @@ -52,7 +79,11 @@ export default class PullDown implements PluginAPI { } private handleBScroll() { - this.scroll.registerType([PULL_DOWN_HOOKS_NAME]) + this.scroll.registerType([ + PULLING_DOWN_EVENT, + ENTER_THRESHOLD_EVENT, + LEAVE_THRESHOLD_EVENT, + ]) this.scroll.proxy(propertiesConfig) } @@ -66,8 +97,7 @@ export default class PullDown implements PluginAPI { stop: 40, } this.options = extend(defaultOptions, userOptions) - // plugin relies on scrollTo api - // set it to Realtime make bs dispatch scroll、scrollEnd hooks + this.scroll.options.probeType = Probe.Realtime } @@ -137,12 +167,62 @@ export default class PullDown implements PluginAPI { scroller.hooks.eventTypes.end, this.checkPullDown ) + + this.registerHooks( + this.scroll, + this.scroll.eventTypes.scrollStart, + this.resetStateBeforeScrollStart + ) + + this.registerHooks( + this.scroll, + this.scroll.eventTypes.scroll, + this.checkLocationOfThresholdBoundary + ) + } + + private resetStateBeforeScrollStart() { + // current fetching pulldownRefresh has ended + if (!this.isFetchingStatus()) { + this.setPulling(PullDownPhase.MOVING) + this.setThresholdBoundary(ThresholdBoundary.DEFAULT) + } + } + + private checkLocationOfThresholdBoundary() { + // pulldownRefresh is in the phase of Moving + if (this.pulling === PullDownPhase.MOVING) { + const scroll = this.scroll + // enter threshold boundary + const enteredThresholdBoundary = + this.thresholdBoundary !== ThresholdBoundary.INSIDE && + this.locateInsideThresholdBoundary() + // leave threshold boundary + const leftThresholdBoundary = + this.thresholdBoundary !== ThresholdBoundary.OUTSIDE && + !this.locateInsideThresholdBoundary() + if (enteredThresholdBoundary) { + this.setThresholdBoundary(ThresholdBoundary.INSIDE) + scroll.trigger(ENTER_THRESHOLD_EVENT) + } + if (leftThresholdBoundary) { + this.setThresholdBoundary(ThresholdBoundary.OUTSIDE) + scroll.trigger(LEAVE_THRESHOLD_EVENT) + } + } + } + + private locateInsideThresholdBoundary() { + return this.scroll.y <= this.options.threshold } private unwatch() { - const scroller = this.scroll.scroller + const scroll = this.scroll + const scroller = scroll.scroller this.watching = false scroller.hooks.off(scroller.hooks.eventTypes.end, this.checkPullDown) + scroll.off(scroll.eventTypes.scrollStart, this.resetStateBeforeScrollStart) + scroll.off(scroll.eventTypes.scroll, this.checkLocationOfThresholdBoundary) } private checkPullDown() { @@ -152,12 +232,12 @@ export default class PullDown implements PluginAPI { return false } - if (!this.pulling) { + if (this.pulling === PullDownPhase.MOVING) { this.modifyBehaviorYBoundary(stop) - this.pulling = true + this.setPulling(PullDownPhase.FETCHING) - this.scroll.trigger(PULL_DOWN_HOOKS_NAME) + this.scroll.trigger(PULLING_DOWN_EVENT) } this.scroll.scrollTo( @@ -167,7 +247,11 @@ export default class PullDown implements PluginAPI { ease.bounce ) - return this.pulling + return this.isFetchingStatus() + } + + private isFetchingStatus() { + return this.pulling === PullDownPhase.FETCHING } private modifyBehaviorYBoundary(stopDistance: number) { @@ -180,12 +264,12 @@ export default class PullDown implements PluginAPI { } finishPullDown() { - if (this.pulling === true) { + if (this.isFetchingStatus()) { const scrollBehaviorY = this.scroll.scroller.scrollBehaviorY // restore minScrollY since the hang animation has ended this.currentMinScrollY = this.cachedOriginanMinScrollY scrollBehaviorY.computeBoundary() - this.pulling = false + this.setPulling(PullDownPhase.DEFAULT) this.scroll.resetPosition(this.scroll.options.bounceTime, ease.bounce) } } @@ -205,15 +289,17 @@ export default class PullDown implements PluginAPI { autoPullDownRefresh() { const { threshold, stop } = this.options - if (this.pulling || !this.watching) { + if (this.isFetchingStatus() || !this.watching) { return } - this.pulling = true this.modifyBehaviorYBoundary(stop) + this.scroll.trigger(this.scroll.eventTypes.scrollStart) this.scroll.scrollTo(this.scroll.x, threshold) - this.scroll.trigger(PULL_DOWN_HOOKS_NAME) + + this.setPulling(PullDownPhase.FETCHING) + this.scroll.trigger(PULLING_DOWN_EVENT) this.scroll.scrollTo( this.scroll.x, stop, diff --git a/packages/scroll-bar/src/__tests__/event-handler.spec.ts b/packages/scroll-bar/src/__tests__/event-handler.spec.ts index b9ff30bd..cd73b6c0 100644 --- a/packages/scroll-bar/src/__tests__/event-handler.spec.ts +++ b/packages/scroll-bar/src/__tests__/event-handler.spec.ts @@ -27,6 +27,8 @@ describe('scroll-bar indicator tests', () => { wrapper, direction: IndicatorDirection.Vertical, fade: true, + fadeInTime: 250, + fadeOutTime: 500, interactive: false, minSize: 8, isCustom: false, diff --git a/packages/scroll-bar/src/__tests__/indicator.spec.ts b/packages/scroll-bar/src/__tests__/indicator.spec.ts index c860b334..dad6ba50 100644 --- a/packages/scroll-bar/src/__tests__/indicator.spec.ts +++ b/packages/scroll-bar/src/__tests__/indicator.spec.ts @@ -26,6 +26,8 @@ describe('scroll-bar indicator tests', () => { wrapper, direction: IndicatorDirection.Vertical, fade: true, + fadeInTime: 250, + fadeOutTime: 500, interactive: false, minSize: 8, isCustom: false, diff --git a/packages/scroll-bar/src/index.ts b/packages/scroll-bar/src/index.ts index 4db7eb6f..aa97aaac 100644 --- a/packages/scroll-bar/src/index.ts +++ b/packages/scroll-bar/src/index.ts @@ -10,6 +10,8 @@ export type ScrollbarOptions = Partial | true export interface ScrollbarConfig { fade: boolean + fadeInTime: number + fadeOutTime: number interactive: boolean customElements: HTMLElement[] minSize: number @@ -50,6 +52,8 @@ export default class ScrollBar { const defaultOptions: ScrollbarConfig = { fade: true, + fadeInTime: 250, + fadeOutTime: 500, interactive: false, customElements: [], minSize: 8, diff --git a/packages/scroll-bar/src/indicator.ts b/packages/scroll-bar/src/indicator.ts index ad82f7f3..e8995f31 100644 --- a/packages/scroll-bar/src/indicator.ts +++ b/packages/scroll-bar/src/indicator.ts @@ -28,6 +28,8 @@ export interface IndicatorOptions { wrapper: HTMLElement direction: IndicatorDirection fade: boolean + fadeInTime: number + fadeOutTime: number interactive: boolean minSize: number isCustom: boolean @@ -247,7 +249,8 @@ export default class Indicator { } fade(visible?: boolean) { - const time = visible ? 250 : 500 + const { fadeInTime, fadeOutTime } = this.options + const time = visible ? fadeInTime : fadeOutTime const wrapper = this.wrapper wrapper.style[style.transitionDuration as any] = time + 'ms' wrapper.style.opacity = visible ? '1' : '0' diff --git a/packages/vuepress-docs/docs/en-US/plugins/pulldown.md b/packages/vuepress-docs/docs/en-US/plugins/pulldown.md index 41b6f599..7863cf91 100644 --- a/packages/vuepress-docs/docs/en-US/plugins/pulldown.md +++ b/packages/vuepress-docs/docs/en-US/plugins/pulldown.md @@ -35,7 +35,9 @@ new BScroll('.bs-wrapper', { ## Demo - +- **Basic Usage** + + @@ -48,6 +50,45 @@ new BScroll('.bs-wrapper', { +- **Sina-Weibo(v2.4.0)** + + + + + + + + +In order to match the effects of App, in version v2.4.0, pulldown has made some changes and is compatible with previous versions. During a pulldown procedure, there are three internal circulation states, and the states are irreversible. They are as follows: + +1. **default** + + The initial state. + +2. **moving** + + Moving state, this state represents that the user's finger is manipulating BetterScroll, and the finger is keeping in touch. In this state, BetterScroll will dispatch two events. + + - **enterThreshold** + + Dispatched when BetterScroll scrolls **into** the pulldown threshold area. Inside this event, you can do the logic of texts initialization, such as prompting the user to "pull down to refresh" + + - **leaveThreshold** + + Dispatched when BetterScroll scrolls **out of** the pulldown threshold area. You can prompt the user to "Release finger" + +3. **fetching** + + Once the finger went away, the pullingDown event is triggered to execute the logic of fetching data + +The state change can only be `default -> moving -> fetching` or `default -> moving`. The latter means that at the moment the user's finger is released, the conditions for triggering the pullingDown event are not met. + ## pullDownRefresh Options ### threshold @@ -141,8 +182,18 @@ bs.autoPullDownRefresh() ### `pullingDown` - **Arguments**: None - - **Trigger**:A `pullingDown` event is fired when the top pull-down distance is greater than the `threshold` value after touchend. + - **Trigger**: A `pullingDown` event is fired when the top pull-down distance is greater than the `threshold` value after touchend. ::: danger Note After the pull-down refresh action is detected, the consumption opportunity of the `pullingDown` hook is only once, so you need to call `finishPullDown()` to tell BetterScroll to provide the next consumption opportunity of the `pullingDown` event. -::: \ No newline at end of file +::: + +### `enterThreshold` + + - **Arguments**: None + - **Trigger**: when pulldown is in the **moving** state and **enters** threshold area. + +### `leaveThreshold` + + - **Arguments**: None + - **Trigger**: when pulldown is in the **moving** state and **leaves** threshold area. \ No newline at end of file diff --git a/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md b/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md index 07a43e3a..6b514246 100644 --- a/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md +++ b/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md @@ -192,7 +192,21 @@ new BScroll('.bs-wrapper', { - **Type**: `number` - **Default**: `300` - the scroll time after the scrollbar track is clicked,. + the scroll time after the scrollbar track is clicked. + +### fadeInTime + + - **Type**: `number` + - **Default**: `250` + + The duration of the animation when the scrollbar fades in. + +### fadeOutTime + + - **Type**: `number` + - **Default**: `500` + + The duration of the animation when the scrollbar fades out. :::tip When `scrollbar` is configured as `true`, the plugin uses the default plugin option. diff --git a/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md b/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md index 74661889..1296d464 100644 --- a/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md +++ b/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md @@ -35,7 +35,9 @@ new BScroll('.bs-wrapper', { ## 示例 - +- **基础使用** + + @@ -48,6 +50,47 @@ new BScroll('.bs-wrapper', { +- **仿新浪微博(v2.4.0)** + + + + + + + + +为了拉齐客户端的交互效果,在 v2.4.0 版本,pulldown 内部进行了功能的改造并且兼容以前的版本,在一次 pulldown 的操作过程中,内部存在三个流转的状态,并且状态是不可逆的。分别如下: + +1. **default** + + 初始状态。 + +2. **moving** + + 移动状态,这个状态代表用户的手指正在操控 BetterScroll,手指未移开,在这种状态下,BetterScroll 会派发两个事件。 + + - **enterThreshold** + + 当 BetterScroll 滚动到 pulldown 的 threshold 阈值区域**之内**的时候派发,在这个事件内部,你可以做文案初始化的逻辑,比如提示用户“下拉刷新” + + - **leaveThreshold** + + 当 BetterScroll 滚动到 pulldown 的 threshold 阈值区域**之外**的时候派发。你可以提示用户“手指释放刷新” + +3. **fetching** + + 手指移开的瞬间,触发 pullingDown 事件,执行获取数据的逻辑 + +状态的变换只可能是 `default -> moving -> fetching` 或者是 `default -> moving`,后者代表用户的手指在释放的瞬间,没有满足触发 pullingDown 事件的条件。 + + + ## pullDownRefresh 选项对象 ### threshold @@ -150,3 +193,13 @@ openPullDown 方法应该配合 closePullDown 一起使用,因为在 pulldown ::: danger 危险 监测到下拉刷新的动作之后,`pullingDown` 钩子的消费机会只有一次,因此你需要调用 `finishPullDown()` 来告诉 BetterScroll 来提供下一次 `pullingDown` 钩子的消费机会。 ::: + +### `enterThreshold` + +- **参数**:无 +- **触发时机**:当 pulldown 正处于 moving 状态,并且**进入** threshold 区域的瞬间。 + +### `leaveThreshold` + +- **参数**:无 +- **触发时机**:当 pulldown 正处于 moving 状态,并且**离开** threshold 区域的瞬间。 \ No newline at end of file diff --git a/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md b/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md index 862458bb..09f3d45a 100644 --- a/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md +++ b/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md @@ -185,12 +185,20 @@ yarn add @better-scroll/scroll-bar 滚动条轨道被点击之后,滚动距离的计算方式,默认与浏览器的表现形式一样,可以配置为 `'clickedPoint'`,代表滚动条滚动至点击的位置。 -### scrollbarTrackOffsetTime +### fadeInTime - **类型**:`number` - - **默认值**:`300` + - **默认值**:`250` + + 滚动条渐显的动画时长。 + +### fadeOutTime + + - **类型**:`number` + - **默认值**:`500` + + 滚动条渐隐的动画时长。 - 滚动条轨道被点击之后,滚动位移的时间 :::tip 提示 当 scrollbar 配置为 true 的时候,插件内部使用的是默认的插件选项对象。 diff --git a/tests/e2e/pulldown/default.e2e.ts b/tests/e2e/pulldown/default.e2e.ts index d3ec83e1..b817fb73 100644 --- a/tests/e2e/pulldown/default.e2e.ts +++ b/tests/e2e/pulldown/default.e2e.ts @@ -9,7 +9,7 @@ describe('Pulldown', () => { beforeEach(async () => { // disable cache await page.setCacheEnabled(false) - await page.goto('http://0.0.0.0:8932/#/pulldown/') + await page.goto('http://0.0.0.0:8932/#/pulldown/default') }) it('should render DOM correctly', async () => { @@ -36,7 +36,7 @@ describe('Pulldown', () => { }) // wait for requesting data - await page.waitFor(5000) + await page.waitFor(3000) const itemsCounts = await page.$$eval( '.pulldown-list-item', (element) => element.length diff --git a/tests/e2e/pulldown/sina.e2e.ts b/tests/e2e/pulldown/sina.e2e.ts new file mode 100644 index 00000000..40a221d9 --- /dev/null +++ b/tests/e2e/pulldown/sina.e2e.ts @@ -0,0 +1,112 @@ +import { Page } from 'puppeteer' +import extendTouch from '../../util/extendTouch' + +jest.setTimeout(10000000) + +const chunk = (array, size) => { + let index = 0 + let resIndex = 0 + const length = array.length + const result = new Array(Math.ceil(length / size)) + + while (index < length) { + result[resIndex++] = array.slice(index, (index += size)) + } + return result +} + +describe('Pulldown-sina-weibo', () => { + let page = (global as any).page as Page + extendTouch(page) + beforeEach(async () => { + // disable cache + await page.setCacheEnabled(false) + await page.goto('http://0.0.0.0:8932/#/pulldown/sina') + }) + + it('should render DOM correctly', async () => { + await page.waitFor(300) + + const itemsCounts = await page.$$eval( + '.pulldown-list-item', + (element) => element.length + ) + + await expect(itemsCounts).toBeGreaterThanOrEqual(20) + }) + + it('should go through correct phase', async () => { + await page.waitFor(300) + + await page.dispatchTouch({ + type: 'touchStart', + touchPoints: [ + { + x: 200, + y: 40, + }, + ], + }) + const touchMovePoints = (() => { + const start = 70 + const step = 5 + const end = 550 + const x = 200 + let ret = [] + for (let i = start; i <= end; i += step) { + ret.push({ + x, + y: i, + }) + } + // chrome only allow 16 items in touchPoints array + return chunk(ret, 16) + })() + // touchmove + for (const touchPoint of touchMovePoints) { + await page.dispatchTouch({ + type: 'touchMove', + touchPoints: touchPoint, + }) + } + + const textContent = await page.$$eval( + '.pulldown-wrapper', + (element) => element[0].textContent + ) + expect(textContent).toContain('Release') + + await page.dispatchTouch({ + type: 'touchEnd', + touchPoints: [ + { + x: 200, + y: 560, + }, + ], + }) + + await page.waitFor(300) + // loading + const textContent2 = await page.$$eval( + '.pulldown-wrapper', + (element) => element[0].textContent + ) + expect(textContent2).toContain('Loading...') + + await page.waitFor(3000) + + // refresh succeed + const textContent3 = await page.$$eval( + '.pulldown-wrapper', + (element) => element[0].textContent + ) + expect(textContent3).toContain('Refresh succeed') + const itemsCounts = await page.$$eval( + '.pulldown-list-item', + (element) => element.length + ) + // has loaded + expect(itemsCounts).toBeGreaterThanOrEqual(40) + }) +}) diff --git a/tests/util/extendTouch.ts b/tests/util/extendTouch.ts index ee3cc1cf..7bc730a1 100644 --- a/tests/util/extendTouch.ts +++ b/tests/util/extendTouch.ts @@ -23,18 +23,44 @@ interface ScrollParams { repeatDelayMs?: number } +interface TouchPoint { + x: number + y: number + radiusX?: number + radiusY?: number + rotationAngle?: number + force?: number + tangentialPressure?: number + tiltX?: number + tiltY?: number + twist?: number + id?: number +} + +interface TouchesParams { + type: 'touchStart' | 'touchEnd' | 'touchMove' | 'touchCancel' + touchPoints: TouchPoint[] + modifiers?: number // Alt=1, Ctrl=2, Meta/Command=4, Shift=8 + timestamp?: number +} + const PINCH_NAME = 'Input.synthesizePinchGesture' const SCROLL_NAME = 'Input.synthesizeScrollGesture' +const TOUCHES_NAME = 'Input.dispatchTouchEvent' declare module 'puppeteer' { interface Touchscreen { _client: { - send: (name: string, params: PinchParams | ScrollParams) => Promise + send: ( + name: T, + params: PinchParams | ScrollParams | TouchesParams + ) => Promise } } interface Page { dispatchPinch: (pinchParams: PinchParams) => Promise dispatchScroll: (scrollParams: ScrollParams) => Promise + dispatchTouch: (touchesParams: TouchesParams) => Promise touchsceen: Touchscreen } } @@ -43,10 +69,13 @@ declare module 'puppeteer' { // since puppeteer is connected to chromium with chromeDevTools // https://chromedevtools.github.io/devtools-protocol/tot/Input#method-dispatchTouchEvent export default (page: Page) => { - page.dispatchPinch = async pinchParams => { + page.dispatchPinch = async (pinchParams) => { await page.touchscreen._client.send(PINCH_NAME, pinchParams) } - page.dispatchScroll = async scrollParams => { + page.dispatchScroll = async (scrollParams) => { await page.touchscreen._client.send(SCROLL_NAME, scrollParams) } + page.dispatchTouch = async (touchesParams) => { + await page.touchscreen._client.send(TOUCHES_NAME, touchesParams) + } }