From 43df0e56fce2c5a8cc6efaabc1b69e2d0b23ee8a Mon Sep 17 00:00:00 2001 From: theniceangel Date: Mon, 19 Oct 2020 17:44:11 +0800 Subject: [PATCH 1/2] fix(slide): slideWillChange should work well when probeType is 3 --- .../examples/vue/components/slide/banner.vue | 4 +- packages/slide/src/SlidePages.ts | 39 ++++----- packages/slide/src/__mocks__/SlidePages.ts | 10 ++- .../slide/src/__tests__/SlidePages.spec.ts | 4 +- packages/slide/src/__tests__/index.spec.ts | 60 +++----------- packages/slide/src/index.ts | 80 +++++++++---------- 6 files changed, 82 insertions(+), 115 deletions(-) diff --git a/packages/examples/vue/components/slide/banner.vue b/packages/examples/vue/components/slide/banner.vue index 74fe3e9e..7f4dad57 100644 --- a/packages/examples/vue/components/slide/banner.vue +++ b/packages/examples/vue/components/slide/banner.vue @@ -50,7 +50,7 @@ momentum: false, bounce: false, stopPropagation: true, - probeType: 2 + probeType: 3 }) this.slide.on('scrollEnd', this._onScrollEnd) @@ -59,7 +59,7 @@ }) }, _onScrollEnd () { - console.log(this.slide.getCurrentPage()) + console.log('CurrentPage => ', this.slide.getCurrentPage()) }, nextPage() { this.slide.next() diff --git a/packages/slide/src/SlidePages.ts b/packages/slide/src/SlidePages.ts index 97cd25ed..fd27b739 100644 --- a/packages/slide/src/SlidePages.ts +++ b/packages/slide/src/SlidePages.ts @@ -35,7 +35,7 @@ export default class SlidePages { refresh() { this.pagesMatrix = new PagesMatrix(this.scroll) const { pageX, pageY } = this.currentPage - // when refresh or resize, currenpage.(x|y) need recaculate + // when refresh or resize, currentPage.(x|y) need recaculate const { x, y } = this.pagesMatrix.getPageStats(pageX, pageY) this.currentPage = { pageX, pageY, x, y } this.checkSlideLoop() @@ -176,33 +176,36 @@ export default class SlidePages { return this.getPageIndexByDirection(Direction.Negative) } - nearestPage( - x: number, - y: number, - directionX: number, - directionY: number - ): Page { + getNearestPage(x: number, y: number): Page { const pageIndex = this.pagesMatrix.getNearestPageIndex(x, y) - let { pageX, pageY } = pageIndex - let newX - let newY + let newX = this.pagesMatrix.getPageStats(pageX, 0).x + let newY = this.pagesMatrix.getPageStats(0, pageY).y + + return { + x: newX, + y: newY, + pageX, + pageY, + } + } + + getPageByDirection(page: Page, directionX: number, directionY: number): Page { + let { pageX, pageY } = page if (pageX === this.currentPage.pageX) { - pageX += directionX - pageX = between(pageX, 0, this.pagesMatrix.pageLengthOfX - 1) + pageX = between(pageX + directionX, 0, this.pagesMatrix.pageLengthOfX - 1) } if (pageY === this.currentPage.pageY) { - pageY += directionY - pageY = between(pageY, 0, this.pagesMatrix.pageLengthOfY - 1) + pageY = between(pageY + directionY, 0, this.pagesMatrix.pageLengthOfY - 1) } - newX = this.pagesMatrix.getPageStats(pageX, 0).x - newY = this.pagesMatrix.getPageStats(0, pageY).y + const x = this.pagesMatrix.getPageStats(pageX, 0).x + const y = this.pagesMatrix.getPageStats(0, pageY).y return { - x: newX, - y: newY, + x, + y, pageX, pageY, } diff --git a/packages/slide/src/__mocks__/SlidePages.ts b/packages/slide/src/__mocks__/SlidePages.ts index ab5bd281..1400f547 100644 --- a/packages/slide/src/__mocks__/SlidePages.ts +++ b/packages/slide/src/__mocks__/SlidePages.ts @@ -56,7 +56,7 @@ const SlidePage = jest.fn().mockImplementation(() => { pageY: 0, } }), - nearestPage: jest.fn().mockImplementation(() => { + getNearestPage: jest.fn().mockImplementation(() => { return { pageX: 0, pageY: 0, @@ -66,6 +66,14 @@ const SlidePage = jest.fn().mockImplementation(() => { }), resetLoopPage: jest.fn(), getPageIndexByDirection: jest.fn(), + getPageByDirection: jest.fn().mockImplementation(() => { + return { + pageX: 0, + pageY: 0, + x: 0, + y: 0, + } + }), } }) diff --git a/packages/slide/src/__tests__/SlidePages.spec.ts b/packages/slide/src/__tests__/SlidePages.spec.ts index 1032d7c4..2b76ac4d 100644 --- a/packages/slide/src/__tests__/SlidePages.spec.ts +++ b/packages/slide/src/__tests__/SlidePages.spec.ts @@ -180,8 +180,8 @@ describe('slide test for SlidePages class', () => { }) }) - it('should work well with nearestPage()', () => { - const pageIndex = slidePages.nearestPage(0, 0, 1, 1) + it('should work well with getNearestPage()', () => { + const pageIndex = slidePages.getNearestPage(30, 0) expect(pageIndex).toMatchObject({ pageX: 1, diff --git a/packages/slide/src/__tests__/index.spec.ts b/packages/slide/src/__tests__/index.spec.ts index a9fe0796..e17bac7f 100644 --- a/packages/slide/src/__tests__/index.spec.ts +++ b/packages/slide/src/__tests__/index.spec.ts @@ -133,7 +133,7 @@ describe('slide test for SlidePage class', () => { it('should call modifyCurrentPage() when BScroll trigger scrollEnd hook', () => { const spyFn = jest.spyOn(Slide.prototype, 'startPlay') slide = new Slide(scroll) - scroll.trigger(scroll.eventTypes.scrollEnd) + scroll.trigger(scroll.eventTypes.scrollEnd, { x: 0, y: 0 }) expect(spyFn).toBeCalled() }) @@ -179,12 +179,6 @@ describe('slide test for SlidePage class', () => { expect(slide.pages.refresh).toBeCalled() expect(slide.pages.getInitialPage).toBeCalled() - expect(slide.pages.setCurrentPage).toBeCalledWith({ - pageX: 0, - pageY: 0, - x: 10, - y: 10, - }) expect(position).toMatchObject({ x: 10, y: 10, @@ -208,51 +202,17 @@ describe('slide test for SlidePage class', () => { expect(scrollMeta.newY).toBe(0) expect(scrollMeta.time).toBe(400) - expect(slide.pages.setCurrentPage).toBeCalledWith({ - x: 0, - y: 0, - pageX: 0, - pageY: 0, - }) - - expect(slide.pages.getWillChangedPage).toBeCalledWith({ - x: 0, - y: 0, - pageX: 0, - pageY: 0, - }) - }) - - it('should call setTouchFlag when scroller.hooks.scroll triggered', () => { - const position = { - x: 0, - y: 0, - } - scroll.scroller.hooks.trigger( - scroll.scroller.hooks.eventTypes.scroll, - position + expect(slide.pages.getPageByDirection).toBeCalledWith( + { + x: 0, + y: 0, + pageX: 0, + pageY: 0, + }, + 0, + 0 ) - - expect(slide.pages.getWillChangedPage).not.toBeCalled() - - const scrollMeta = { - newX: -1, - newY: -1, - time: 0, - } - // set isTouching = true - scroll.scroller.hooks.trigger( - scroll.scroller.hooks.eventTypes.momentum, - scrollMeta - ) - scroll.scroller.hooks.trigger( - scroll.scroller.hooks.eventTypes.scroll, - position - ) - - expect(slide.pages.getWillChangedPage).toBeCalled() }) - it('should start a new autoPlay timer when scroller.hooks.checkClick triggered', () => { const spyFn = jest.spyOn(Slide.prototype, 'startPlay') slide = new Slide(scroll) diff --git a/packages/slide/src/index.ts b/packages/slide/src/index.ts index 6c9389fc..00dc6a35 100644 --- a/packages/slide/src/index.ts +++ b/packages/slide/src/index.ts @@ -64,7 +64,6 @@ export default class Slide implements PluginAPI { private thresholdY: number private hooksFn: Array<[EventEmitter, string, Function]> private resetLooping = false - private isTouching = false private willChangeToPage: Page private autoplayTimer: number = 0 private prevContent: HTMLElement @@ -208,15 +207,10 @@ export default class Slide implements PluginAPI { scrollerHooks.eventTypes.momentum, this.modifyScrollMetaHandler ) - this.registerHooks( - scrollerHooks, - scrollerHooks.eventTypes.beforeStart, - this.setTouchFlag - ) this.registerHooks( scrollerHooks, scrollerHooks.eventTypes.scroll, - this.scrollMoving + this.scrollHandler ) // a click operation will clearTimer, so restart a new one this.registerHooks( @@ -307,36 +301,34 @@ export default class Slide implements PluginAPI { nearestPage(x: number, y: number): Page { const { scrollBehaviorX, scrollBehaviorY } = this.scroll.scroller const { - absStartPos: absStartPosX, maxScrollPos: maxScrollPosX, minScrollPos: minScrollPosX, - direction: directionX, } = scrollBehaviorX const { - absStartPos: absStartPosY, maxScrollPos: maxScrollPosY, minScrollPos: minScrollPosY, - direction: directionY, } = scrollBehaviorY - let triggerThreshold = true + return this.pages.getNearestPage( + between(x, maxScrollPosX, minScrollPosX), + between(y, maxScrollPosY, minScrollPosY) + ) + } + + private satisfyThreshold(x: number, y: number): boolean { + const { scrollBehaviorX, scrollBehaviorY } = this.scroll.scroller + + let satisfied = true if ( - Math.abs(x - absStartPosX) <= this.thresholdX && - Math.abs(y - absStartPosY) <= this.thresholdY + Math.abs(x - scrollBehaviorX.absStartPos) <= this.thresholdX && + Math.abs(y - scrollBehaviorY.absStartPos) <= this.thresholdY ) { - triggerThreshold = false - } - if (!triggerThreshold) { - return this.pages.currentPage + satisfied = false } - return this.pages.nearestPage( - between(x, maxScrollPosX, minScrollPosX), - between(y, maxScrollPosY, minScrollPosY), - directionX, - directionY - ) + return satisfied } + private refreshHandler(content: HTMLElement) { if (!this.satisfyInitialization()) { return @@ -362,7 +354,6 @@ export default class Slide implements PluginAPI { this.initialised = true position.x = initPage.x position.y = initPage.y - this.pages.setCurrentPage(initPage) } ) } @@ -409,11 +400,14 @@ export default class Slide implements PluginAPI { } } - private modifyCurrentPage() { - this.isTouching = false - if (!this.options.loop) { + private modifyCurrentPage(point: Position) { + // if in animation, and force stopping + if (this.scroll.scroller.animater.forceStopped) { return } + const newPage = this.nearestPage(point.x, point.y) + this.pages.setCurrentPage(newPage) + this.pageWillChangeTo(newPage) // triggered by resetLoop if (this.resetLooping) { this.resetLooping = false @@ -428,7 +422,6 @@ export default class Slide implements PluginAPI { // since it is a seamless scroll return true } - this.pageWillChangeTo(this.pages.currentPage) } private goTo(pageX: number, pageY: number, time?: number, easing?: EaseItem) { @@ -445,8 +438,6 @@ export default class Slide implements PluginAPI { return } time = time === undefined ? this.getEaseTime(deltaX, deltaY) : time - this.pages.setCurrentPage(newPage) - this.pageWillChangeTo(this.pages.currentPage) this.scroll.scroller.scrollTo(x, y, time, scrollEasing) } @@ -490,7 +481,18 @@ export default class Slide implements PluginAPI { time: number [key: string]: any }) { - const newPage = this.nearestPage(scrollMeta.newX, scrollMeta.newY) + const { scrollBehaviorX, scrollBehaviorY, animater } = this.scroll.scroller + const newX = scrollMeta.newX + const newY = scrollMeta.newY + + const newPage = + this.satisfyThreshold(newX, newY) || animater.forceStopped + ? this.pages.getPageByDirection( + this.nearestPage(newX, newY), + scrollBehaviorX.direction, + scrollBehaviorY.direction + ) + : this.pages.currentPage scrollMeta.time = this.getEaseTime( scrollMeta.newX - newPage.x, @@ -499,14 +501,12 @@ export default class Slide implements PluginAPI { scrollMeta.newX = newPage.x scrollMeta.newY = newPage.y scrollMeta.easing = this.options.easing || ease.bounce - this.pages.setCurrentPage(newPage) - this.pageWillChangeTo(this.pages.currentPage) } - private scrollMoving(point: Position) { - if (this.isTouching) { - const newPos = this.nearestPage(point.x, point.y) - this.pageWillChangeTo(newPos) + private scrollHandler({ x, y }: Position) { + if (this.satisfyThreshold(x, y)) { + const newPage = this.nearestPage(x, y) + this.pageWillChangeTo(newPage) } } @@ -521,10 +521,6 @@ export default class Slide implements PluginAPI { } } - private setTouchFlag() { - this.isTouching = true - } - private registerHooks(hooks: EventEmitter, name: string, handler: Function) { hooks.on(name, handler, this) this.hooksFn.push([hooks, name, handler]) From 661aa83a89976e6b7073c40354fd7476219451fe Mon Sep 17 00:00:00 2001 From: theniceangel Date: Tue, 20 Oct 2020 11:25:25 +0800 Subject: [PATCH 2/2] docs(observe-dom): update doc --- packages/vuepress-docs/docs/en-US/plugins/observe-dom.md | 2 +- packages/vuepress-docs/docs/zh-CN/plugins/observe-dom.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vuepress-docs/docs/en-US/plugins/observe-dom.md b/packages/vuepress-docs/docs/en-US/plugins/observe-dom.md index 4811540b..b02fc67f 100644 --- a/packages/vuepress-docs/docs/en-US/plugins/observe-dom.md +++ b/packages/vuepress-docs/docs/en-US/plugins/observe-dom.md @@ -43,5 +43,5 @@ yarn add @better-scroll/observe-dom :::warning -For version <= `2.0.4`, because the internal implementation of the plugin uses [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), it cannot detect whether the load of the img tag is complete, so for images with uncertain heights inside the content, you need to wait for the image to load before calling `bs.refresh()` to recalculate the scrollable size. If the browser does not support MutationObserver, the fallback inside the plugin is to recalculate the scrollable size every second. +For version <= `2.0.5`, because the internal implementation of the plugin uses [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), it cannot detect whether the load of the img tag is complete, so for images with uncertain heights inside the content, you need to wait for the image to load before calling `bs.refresh()` to recalculate the scrollable size. If the browser does not support MutationObserver, the fallback inside the plugin is to recalculate the scrollable size every second. ::: \ No newline at end of file diff --git a/packages/vuepress-docs/docs/zh-CN/plugins/observe-dom.md b/packages/vuepress-docs/docs/zh-CN/plugins/observe-dom.md index 4122fb59..02f210e4 100644 --- a/packages/vuepress-docs/docs/zh-CN/plugins/observe-dom.md +++ b/packages/vuepress-docs/docs/zh-CN/plugins/observe-dom.md @@ -45,5 +45,5 @@ yarn add @better-scroll/observe-dom :::warning 注意 -对于 `2.0.4` 版本及之前版本,由于插件的内部实现使用的是 [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver),它无法探测到 img 标签的是否加载完成,因此对于 content 内部含有不确定高度的图片,需要等图片加载完成再调用 bs.refresh() 来重新计算可滚动尺寸。如果浏览器不支持 MutationObserver,插件内部的降级方案是每秒重新计算可滚动的尺寸。 +对于 `2.0.5` 版本及之前版本,由于插件的内部实现使用的是 [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver),它无法探测到 img 标签的是否加载完成,因此对于 content 内部含有不确定高度的图片,需要等图片加载完成再调用 bs.refresh() 来重新计算可滚动尺寸。如果浏览器不支持 MutationObserver,插件内部的降级方案是每秒重新计算可滚动的尺寸。 ::: \ No newline at end of file