提交 2bc43dfa 编写于 作者: fxy060608's avatar fxy060608

feat: onPageScroll

上级 d2181186
......@@ -128,9 +128,11 @@ declare namespace UniApp {
}
interface PagesJsonPageStyle extends PagesJsonPagePlatformStyle {
disableScroll?: boolean
enablePullDownRefresh?: boolean
navigationBar: PageNavigationBar
refreshOptions?: PageRefreshOptions
onReachBottomDistance?: number
}
interface PageRouteMeta extends PagesJsonPageStyle {
id: number
......
export * from './util'
export * from './icon'
export * from './scroll'
export * from './getRealRoute'
export * from './updateCssVar'
export * from './getWindowOffset'
export interface CreateScrollListenerOptions {
onPageScroll?: (scrollTop: number) => void
onReachBottom?: () => void
onReachBottomDistance?: number
}
export function disableScrollListener(evt: Event) {
evt.preventDefault()
}
let testReachBottomTimer: number
let lastScrollHeight = 0
export function createScrollListener({
onPageScroll,
onReachBottom,
onReachBottomDistance,
}: CreateScrollListenerOptions) {
let ticking = false
let hasReachBottom = false
let reachBottomLocking = true
const isReachBottom = () => {
const { scrollHeight } = document.documentElement
// 部分浏览器窗口高度变化后document.documentelement.clientheight不会变化,采用window.innerHeight
const windowHeight = window.innerHeight
const scrollY = window.scrollY
const isBottom =
scrollY > 0 &&
scrollHeight > windowHeight &&
scrollY + windowHeight + onReachBottomDistance! >= scrollHeight
// 兼容部分浏览器滚动时scroll事件不触发
const heightChanged =
Math.abs(scrollHeight - lastScrollHeight) > onReachBottomDistance!
if (isBottom && (!hasReachBottom || heightChanged)) {
lastScrollHeight = scrollHeight
hasReachBottom = true
return true
}
if (!isBottom && hasReachBottom) {
hasReachBottom = false
}
return false
}
const trigger = () => {
onPageScroll && onPageScroll(window.pageYOffset)
function testReachBottom() {
if (isReachBottom()) {
onReachBottom && onReachBottom()
reachBottomLocking = false
setTimeout(function () {
reachBottomLocking = true
}, 350)
return true
}
}
if (onReachBottom && reachBottomLocking) {
if (testReachBottom()) {
} else {
// 解决部分浏览器滚动中js获取窗口高度不准确导致的问题
testReachBottomTimer = setTimeout(testReachBottom, 300)
}
}
ticking = false
}
return function onScroll() {
clearTimeout(testReachBottomTimer)
if (!ticking) {
requestAnimationFrame(trigger)
}
ticking = true
}
}
import { App } from 'vue'
import { initAppConfig } from './appConfig'
import { initOn } from './on'
import { initSubscribe } from './subscribe'
export * from './page'
export function initService(app: App) {
initAppConfig(app._context.config)
initOn()
initSubscribe()
initAppConfig(app._context.config)
}
import { ComponentPublicInstance } from '@vue/runtime-core'
import { getCurrentPage, invokeHook } from './page'
export function initOn() {
UniServiceJSBridge.on('onAppEnterForeground', onAppEnterForeground)
UniServiceJSBridge.on('onAppEnterBackground', onAppEnterBackground)
}
function onAppEnterForeground() {
const page = getCurrentPage()
const showOptions = {
path: '',
query: {},
}
if (page) {
showOptions.path = page.$page.route
showOptions.query = page.$page.options
}
invokeHook(getApp() as ComponentPublicInstance, 'onShow', showOptions)
invokeHook(page as ComponentPublicInstance, 'onShow')
}
function onAppEnterBackground() {
invokeHook(getApp() as ComponentPublicInstance, 'onHide')
invokeHook(getCurrentPage() as ComponentPublicInstance, 'onHide')
}
......@@ -2,6 +2,17 @@ import { isString } from '@vue/shared'
import { ComponentPublicInstance } from 'vue'
import { invokeArrayFns } from '@dcloudio/uni-shared'
export function getPageById(id: number) {
return getCurrentPages().find((page) => page.$page.id === id)
}
export function getPageVmById(id: number) {
const page = getPageById(id)
if (page) {
return (page as any).$vm as ComponentPublicInstance
}
}
export function getCurrentPage() {
const pages = getCurrentPages()
const len = pages.length
......
import { ComponentPublicInstance } from '@vue/runtime-core'
import { getCurrentPage, invokeHook } from './page'
import { getPageVmById, invokeHook } from './page'
export function initSubscribe() {
UniServiceJSBridge.on('onAppEnterForeground', () => {
const page = getCurrentPage()
const showOptions = {
path: '',
query: {},
}
if (page) {
showOptions.path = page.$page.route
showOptions.query = page.$page.options
UniServiceJSBridge.subscribe('onPageScroll', createPageEvent('onPageScroll'))
}
function createPageEvent(name: string) {
return (args: unknown, pageId: number) => {
const vm = getPageVmById(pageId)
if (vm) {
invokeHook(vm, name, args)
}
invokeHook(getApp() as ComponentPublicInstance, 'onShow', showOptions)
invokeHook(page as ComponentPublicInstance, 'onShow')
})
UniServiceJSBridge.on('onAppEnterBackground', () => {
invokeHook(getApp() as ComponentPublicInstance, 'onHide')
invokeHook(getCurrentPage() as ComponentPublicInstance, 'onHide')
})
}
}
此差异已折叠。
......@@ -17,8 +17,8 @@ import {
import { RouterView, useRoute } from 'vue-router'
import { useTabBar } from '../../plugin/state'
import { useKeepAliveRoute } from '../../plugin/page'
import { useTabBar } from '../../setup/state'
import { useKeepAliveRoute } from '../../setup/page'
import TabBar from './tabBar'
......
......@@ -6,7 +6,7 @@ import {
OnTabBarMidButtonTap,
} from '@dcloudio/uni-api'
import { getRealPath } from '../../../platform'
import { useTabBar } from '../../plugin/state'
import { useTabBar } from '../../setup/state'
import { cssBackdropFilter } from '../../../service/api/base/canIUse'
import { normalizeWindowBottom } from '../../../helpers/cssVar'
......
......@@ -10,7 +10,7 @@ import {
import PageHead from './pageHead'
import PageBody from './pageBody'
import { providePageMeta } from '../../plugin/provide'
import { providePageMeta } from '../../setup/provide'
export default defineComponent({
name: 'Page',
......
......@@ -36,7 +36,7 @@
</uni-page-refresh>
</template>
<script lang="ts">
import { usePageMeta } from '../../../plugin/provide'
import { usePageMeta } from '../../../setup/provide'
export default {
name: 'PageRefresh',
setup() {
......
......@@ -5,7 +5,7 @@ import {
API_STOP_PULL_DOWN_REFRESH,
} from '@dcloudio/uni-api'
import { useSubscribe } from '@dcloudio/uni-components'
import { usePageMeta } from '../../../plugin/provide'
import { usePageMeta } from '../../../setup/provide'
function processDeltaY(
ev: TouchEvent,
......
import { ref, renderSlot, defineComponent, Ref } from 'vue'
import { usePageMeta } from '../../plugin/provide'
import { usePageMeta } from '../../setup/provide'
import PageRefresh from './page-refresh/component.vue'
......
......@@ -7,7 +7,7 @@ import {
createSvgIconVNode,
invokeHook,
} from '@dcloudio/uni-core'
import { usePageMeta } from '../../plugin/provide'
import { usePageMeta } from '../../setup/provide'
import {
usePageHeadTransparent,
usePageHeadTransparentBackgroundColor,
......
export { getApp } from './plugin/app'
export { getCurrentPages } from './plugin/page'
export { getApp } from './setup/app'
export { getCurrentPages } from './setup/page'
......@@ -4,7 +4,6 @@ import { initApp } from '@dcloudio/uni-vue'
import { initView, initService } from '@dcloudio/uni-core'
import { initRouter } from './router'
// import { initMixin } from './mixin'
export default {
install(app: App) {
......@@ -12,8 +11,6 @@ export default {
initView(app)
initService(app)
// initMixin(app)
if (__UNI_FEATURE_PAGES__) {
initRouter(app)
}
......
......@@ -5,7 +5,7 @@ import {
createWebHistory,
createWebHashHistory,
} from 'vue-router'
import { getCurrentPages, normalizeRouteKey, removePage } from './page'
import { getCurrentPages, normalizeRouteKey, removePage } from '../setup/page'
export function initRouter(app: App) {
app.use(createAppRouter(createRouter(createRouterOptions())))
......
......@@ -11,13 +11,12 @@ import {
onBeforeDeactivate,
onBeforeMount,
} from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { parseQuery, decodedQuery } from '@dcloudio/uni-shared'
import { useRouter } from 'vue-router'
import { decodedQuery } from '@dcloudio/uni-shared'
import { LayoutComponent } from '../..'
import { initApp } from './app'
import { initPage } from './page'
import { usePageMeta } from './provide'
import { updateCurPageCssVar } from '../../helpers/cssVar'
import { initPage, onPageShow } from './page'
import { usePageMeta, usePageRoute } from './provide'
interface SetupComponentOptions {
init: (vm: ComponentPublicInstance) => void
......@@ -25,27 +24,6 @@ interface SetupComponentOptions {
after?: (comp: DefineComponent) => void
}
export function usePageRoute() {
if (__UNI_FEATURE_PAGES__) {
return useRoute()
}
const url = location.href
const searchPos = url.indexOf('?')
const hashPos = url.indexOf('#', searchPos > -1 ? searchPos : 0)
let query = {}
if (searchPos > -1) {
query = parseQuery(
url.slice(searchPos + 1, hashPos > -1 ? hashPos : url.length)
)
}
const { meta } = __uniRoutes[0]
return {
meta,
query: query,
path: '/' + meta.route,
}
}
function wrapperComponentSetup(
comp: DefineComponent,
{ init, setup, after }: SetupComponentOptions
......@@ -71,10 +49,6 @@ function setupComponent(comp: any, options: SetupComponentOptions) {
return comp
}
function onPageShow(pageMeta: UniApp.PageRouteMeta) {
updateCurPageCssVar(pageMeta)
}
export function setupPage(comp: any) {
return setupComponent(comp, {
init: initPage,
......@@ -88,7 +62,7 @@ export function setupPage(comp: any) {
const pageMeta = usePageMeta()
onBeforeMount(() => {
const { onLoad, onShow } = instance
onPageShow(pageMeta)
onPageShow(instance, pageMeta)
onLoad && invokeArrayFns(onLoad, decodedQuery(route.query))
instance.__isVisible = true
onShow && invokeArrayFns(onShow)
......@@ -99,7 +73,7 @@ export function setupPage(comp: any) {
})
onBeforeActivate(() => {
if (!instance.__isVisible) {
onPageShow(pageMeta)
onPageShow(instance, pageMeta)
instance.__isVisible = true
const { onShow } = instance
onShow && invokeArrayFns(onShow)
......
......@@ -4,11 +4,19 @@ import {
computed,
ConcreteComponent,
ComponentPublicInstance,
ComponentInternalInstance,
} from 'vue'
import { useRoute, RouteLocationNormalizedLoaded } from 'vue-router'
import { invokeHook } from '@dcloudio/uni-core'
import {
invokeHook,
disableScrollListener,
createScrollListener,
CreateScrollListenerOptions,
} from '@dcloudio/uni-core'
import { ON_REACH_BOTTOM_DISTANCE } from '@dcloudio/uni-shared'
import { usePageMeta } from './provide'
import { NavigateType } from '../../service/api/route/utils'
import { updateCurPageCssVar } from '../../helpers/cssVar'
const SEP = '$$'
......@@ -176,3 +184,67 @@ function pruneRouteCache(key: string) {
}
})
}
export function onPageShow(
instance: ComponentInternalInstance,
pageMeta: UniApp.PageRouteMeta
) {
updateCurPageCssVar(pageMeta)
initPageScrollListener(instance, pageMeta)
}
let curScrollListener: (evt: Event) => any
function initPageScrollListener(
instance: ComponentInternalInstance,
pageMeta: UniApp.PageRouteMeta
) {
document.removeEventListener('touchmove', disableScrollListener)
if (curScrollListener) {
document.removeEventListener('scroll', curScrollListener)
}
if (pageMeta.disableScroll) {
return document.addEventListener('touchmove', disableScrollListener)
}
const { onPageScroll, onReachBottom } = instance
const navigationBarTransparent = pageMeta.navigationBar.type === 'transparent'
if (!onPageScroll && !onReachBottom && !navigationBarTransparent) {
return
}
const opts: CreateScrollListenerOptions = {}
const pageId = instance.proxy!.$page.id
if (onPageScroll || navigationBarTransparent) {
opts.onPageScroll = createOnPageScroll(
pageId,
onPageScroll,
navigationBarTransparent
)
}
if (onReachBottom) {
opts.onReachBottomDistance =
pageMeta.onReachBottomDistance || ON_REACH_BOTTOM_DISTANCE
opts.onReachBottom = () =>
UniViewJSBridge.publishHandler('onReachBottom', {}, pageId)
}
curScrollListener = createScrollListener(opts)
// 避免监听太早,直接触发了 scroll
requestAnimationFrame(() =>
document.addEventListener('scroll', curScrollListener)
)
}
function createOnPageScroll(
pageId: number,
onPageScroll: unknown,
navigationBarTransparent: boolean
) {
return (scrollTop: number) => {
if (onPageScroll) {
UniViewJSBridge.publishHandler('onPageScroll', { scrollTop }, pageId)
}
if (navigationBarTransparent) {
UniViewJSBridge.emit(pageId + '.onPageScroll', {
scrollTop,
})
}
}
}
import { reactive, provide, inject } from 'vue'
import { useRoute } from 'vue-router'
import { NAVBAR_HEIGHT } from '@dcloudio/uni-shared'
import { NAVBAR_HEIGHT, parseQuery } from '@dcloudio/uni-shared'
import { PolySymbol, rpx2px } from '@dcloudio/uni-core'
import safeAreaInsets from 'safe-area-insets'
......@@ -17,7 +17,26 @@ export function providePageMeta(id: number) {
provide(pageMetaKey, pageMeta)
return pageMeta
}
export function usePageRoute() {
if (__UNI_FEATURE_PAGES__) {
return useRoute()
}
const url = location.href
const searchPos = url.indexOf('?')
const hashPos = url.indexOf('#', searchPos > -1 ? searchPos : 0)
let query = {}
if (searchPos > -1) {
query = parseQuery(
url.slice(searchPos + 1, hashPos > -1 ? hashPos : url.length)
)
}
const { meta } = __uniRoutes[0]
return {
meta,
query: query,
path: '/' + meta.route,
}
}
function initPageMeta(id: number) {
if (__UNI_FEATURE_PAGES__) {
return reactive<UniApp.PageRouteMeta>(
......
import { NAVBAR_HEIGHT } from '@dcloudio/uni-shared'
import { updatePageCssVar } from '@dcloudio/uni-core'
import { useTabBar } from '../framework/plugin/state'
import { useTabBar } from '../framework/setup/state'
import { cssEnv, cssConstant } from '../service/api/base/canIUse'
const envMethod = /*#__PURE__*/ (() =>
......
export { default as plugin } from './framework/plugin'
export * from './framework/plugin/setup'
export * from './framework/setup'
export * from '@dcloudio/uni-components'
export * from './view/components'
......
......@@ -5,7 +5,7 @@ import {
ReLaunchOptions,
ReLaunchProtocol,
} from '@dcloudio/uni-api'
import { getCurrentPagesMap, removePage } from '../../../framework/plugin/page'
import { getCurrentPagesMap, removePage } from '../../../framework/setup/page'
import { navigate } from './utils'
function removeAllPages() {
......
......@@ -6,7 +6,7 @@ import {
RedirectToProtocol,
} from '@dcloudio/uni-api'
import { getCurrentPage } from '@dcloudio/uni-core'
import { removePage, normalizeRouteKey } from '../../../framework/plugin/page'
import { removePage, normalizeRouteKey } from '../../../framework/setup/page'
import { navigate } from './utils'
function removeLastPage() {
......
......@@ -7,7 +7,7 @@ import {
SwitchTabProtocol,
} from '@dcloudio/uni-api'
import { getCurrentPageVm, invokeHook } from '@dcloudio/uni-core'
import { getCurrentPagesMap, removePage } from '../../../framework/plugin/page'
import { getCurrentPagesMap, removePage } from '../../../framework/setup/page'
import { navigate } from './utils'
function removeNonTabBarPages() {
......
import { isNavigationFailure, Router } from 'vue-router'
import { createPageState } from '../../../framework/plugin/page'
import { createPageState } from '../../../framework/setup/page'
export type NavigateType =
| 'navigateTo'
......
......@@ -32,7 +32,7 @@ import {
ShowTabBarRedDotOptions,
ShowTabBarRedDotProtocol,
} from '@dcloudio/uni-api'
import { useTabBar } from '../../../framework/plugin/state'
import { useTabBar } from '../../../framework/setup/state'
const setTabBarItemProps = ['text', 'iconPath', 'selectedIconPath']
const setTabBarStyleProps = [
'color',
......
......@@ -292,6 +292,7 @@ function debounce(fn, delay) {
const NAVBAR_HEIGHT = 44;
const TABBAR_HEIGHT = 50;
const ON_REACH_BOTTOM_DISTANCE = 50;
const RESPONSIVE_MIN_WIDTH = 768;
const COMPONENT_NAME_PREFIX = 'VUni';
const PRIMARY_COLOR = '#007aff';
......@@ -301,6 +302,7 @@ exports.COMPONENT_NAME_PREFIX = COMPONENT_NAME_PREFIX;
exports.COMPONENT_PREFIX = COMPONENT_PREFIX;
exports.COMPONENT_SELECTOR_PREFIX = COMPONENT_SELECTOR_PREFIX;
exports.NAVBAR_HEIGHT = NAVBAR_HEIGHT;
exports.ON_REACH_BOTTOM_DISTANCE = ON_REACH_BOTTOM_DISTANCE;
exports.PLUS_RE = PLUS_RE;
exports.PRIMARY_COLOR = PRIMARY_COLOR;
exports.RESPONSIVE_MIN_WIDTH = RESPONSIVE_MIN_WIDTH;
......
......@@ -47,6 +47,8 @@ export declare function normalizeTarget(el: HTMLElement): {
offsetLeft: number;
};
export declare const ON_REACH_BOTTOM_DISTANCE = 50;
export declare function once(fn: (...args: any[]) => any, ctx?: unknown): (...args: any[]) => any;
/**
......
......@@ -288,8 +288,9 @@ function debounce(fn, delay) {
const NAVBAR_HEIGHT = 44;
const TABBAR_HEIGHT = 50;
const ON_REACH_BOTTOM_DISTANCE = 50;
const RESPONSIVE_MIN_WIDTH = 768;
const COMPONENT_NAME_PREFIX = 'VUni';
const PRIMARY_COLOR = '#007aff';
export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, addFont, debounce, decode, decodedQuery, getLen, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, scrollTo, stringifyQuery, updateElementStyle };
export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, addFont, debounce, decode, decodedQuery, getLen, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, scrollTo, stringifyQuery, updateElementStyle };
export const NAVBAR_HEIGHT = 44
export const TABBAR_HEIGHT = 50
export const ON_REACH_BOTTOM_DISTANCE = 50
export const RESPONSIVE_MIN_WIDTH = 768
export const COMPONENT_NAME_PREFIX = 'VUni'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册