From 6f01c20f7de3b58cd2029df55bde6b5f7a6ab615 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Tue, 20 Apr 2021 20:40:48 +0800 Subject: [PATCH] feat: add PageScrollTo --- .../uni-api/src/protocols/ui/pageScrollTo.ts | 10 ++--- packages/uni-h5/dist/uni-h5.esm.js | 23 ++++++++++- packages/uni-h5/src/service/api/index.ts | 1 + .../uni-h5/src/service/api/ui/pageScrollTo.ts | 18 +++++++++ packages/uni-shared/dist/uni-shared.cjs.js | 35 +++++++++++++++++ packages/uni-shared/dist/uni-shared.d.ts | 3 ++ packages/uni-shared/dist/uni-shared.esm.js | 38 ++++++++++++++++++- packages/uni-shared/src/dom.ts | 36 ++++++++++++++++++ 8 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 packages/uni-h5/src/service/api/ui/pageScrollTo.ts diff --git a/packages/uni-api/src/protocols/ui/pageScrollTo.ts b/packages/uni-api/src/protocols/ui/pageScrollTo.ts index 8513792bb..6486bae09 100644 --- a/packages/uni-api/src/protocols/ui/pageScrollTo.ts +++ b/packages/uni-api/src/protocols/ui/pageScrollTo.ts @@ -1,13 +1,9 @@ export const API_PAGE_SCROLL_TO = 'pageScrollTo' export type API_TYPE_PAGE_SCROLL_TO = typeof uni.pageScrollTo export const PageScrollToProtocol: ApiProtocol = { - scrollTop: { - type: Number, - required: true, - }, - duration: { - type: Number, - }, + scrollTop: Number, + selector: String, + duration: Number, } const DEFAULT_DURATION = 300 export const PageScrollToOptions: ApiOptions = { diff --git a/packages/uni-h5/dist/uni-h5.esm.js b/packages/uni-h5/dist/uni-h5.esm.js index a6e64d1fc..7ce70a1ef 100644 --- a/packages/uni-h5/dist/uni-h5.esm.js +++ b/packages/uni-h5/dist/uni-h5.esm.js @@ -1,6 +1,6 @@ import {isFunction, extend, isPlainObject, isString, invokeArrayFns as invokeArrayFns$1, hyphenate, isArray, hasOwn as hasOwn$1, isObject as isObject$1, capitalize, toRawType, makeMap as makeMap$1, isPromise} from "@vue/shared"; import {injectHook, createVNode, inject, provide, reactive, computed, nextTick, getCurrentInstance, onBeforeMount, onMounted, onBeforeActivate, onBeforeDeactivate, openBlock, createBlock, mergeProps, toDisplayString, ref, defineComponent, resolveComponent, toHandlers, renderSlot, watch, onActivated, onBeforeUnmount, withModifiers, withDirectives, vShow, vModelDynamic, createCommentVNode, createTextVNode, Fragment, renderList, vModelText, watchEffect, withCtx, KeepAlive, resolveDynamicComponent} from "vue"; -import {once, passive, normalizeTarget, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, decodedQuery, plusReady, debounce, PRIMARY_COLOR as PRIMARY_COLOR$1, removeLeadingSlash, getLen, updateElementStyle, addFont} from "@dcloudio/uni-shared"; +import {once, passive, normalizeTarget, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, decodedQuery, plusReady, debounce, PRIMARY_COLOR as PRIMARY_COLOR$1, removeLeadingSlash, getLen, updateElementStyle, addFont, scrollTo} from "@dcloudio/uni-shared"; import {useRoute, createRouter, createWebHistory, createWebHashHistory, isNavigationFailure, RouterView} from "vue-router"; function applyOptions(options, instance2, publicThis) { Object.keys(options).forEach((name) => { @@ -4956,6 +4956,20 @@ const LoadFontFaceProtocol = { }, desc: Object }; +const API_PAGE_SCROLL_TO = "pageScrollTo"; +const PageScrollToProtocol = { + scrollTop: Number, + selector: String, + duration: Number +}; +const DEFAULT_DURATION = 300; +const PageScrollToOptions = { + formatArgs: { + duration(value, params) { + params.duration = Math.max(0, parseInt(value + "") || DEFAULT_DURATION); + } + } +}; const FRONT_COLORS = ["#ffffff", "#000000"]; const API_SET_NAVIGATION_BAR_COLOR = "setNavigationBarColor"; const SetNavigationBarColorOptions = { @@ -12320,6 +12334,10 @@ const hideNavigationBarLoading = defineAsyncApi(API_HIDE_NAVIGATION_BAR_LOADING, const setNavigationBarTitle = defineAsyncApi(API_SET_NAVIGATION_BAR_TITLE, (args, {resolve, reject}) => { setNavigationBar(getCurrentPageMeta(), API_SET_NAVIGATION_BAR_TITLE, args, resolve, reject); }, SetNavigationBarTitleProtocol); +const pageScrollTo = defineAsyncApi(API_PAGE_SCROLL_TO, ({scrollTop, selector, duration}, {resolve}) => { + scrollTo(selector || scrollTop || 0, duration); + resolve(); +}, PageScrollToProtocol, PageScrollToOptions); const showModal = defineAsyncApi(API_SHOW_MODAL, () => { }, ShowModalProtocol, ShowModalOptions); const showToast = defineAsyncApi(API_SHOW_TOAST, () => { @@ -12510,6 +12528,7 @@ var api = /* @__PURE__ */ Object.freeze({ showNavigationBarLoading, hideNavigationBarLoading, setNavigationBarTitle, + pageScrollTo, showModal, showToast, hideToast, @@ -13555,4 +13574,4 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { ]); } _sfc_main.render = _sfc_render; -export {_sfc_main$1 as AsyncErrorComponent, _sfc_main as AsyncLoadingComponent, _sfc_main$l as Audio, index$5 as Button, _sfc_main$k as Canvas, _sfc_main$j as Checkbox, _sfc_main$i as CheckboxGroup, _sfc_main$h as Editor, index$6 as Form, index$4 as Icon, index$3 as Image, _sfc_main$g as Input, _sfc_main$f as Label, LayoutComponent, _sfc_main$e as MovableView, _sfc_main$d as Navigator, index as PageComponent, _sfc_main$c as Progress, _sfc_main$b as Radio, _sfc_main$a as RadioGroup, ResizeSensor, _sfc_main$9 as RichText, _sfc_main$8 as ScrollView, _sfc_main$7 as Slider, _sfc_main$6 as SwiperItem, _sfc_main$5 as Switch, index$2 as Text, _sfc_main$4 as Textarea, UniServiceJSBridge$1 as UniServiceJSBridge, UniViewJSBridge$1 as UniViewJSBridge, _sfc_main$3 as Video, index$1 as View, addInterceptor, arrayBufferToBase64, base64ToArrayBuffer, canIUse, chooseFile, chooseImage, chooseVideo, clearStorage, clearStorageSync, closeSocket, connectSocket, createInnerAudioContext, createIntersectionObserver, createSelectorQuery, createVideoContext, cssBackdropFilter, cssConstant, cssEnv, cssVar, downloadFile, getApp$1 as getApp, getCurrentPages$1 as getCurrentPages, getFileInfo, getImageInfo, getLocation, getNetworkType, getStorage, getStorageInfo, getStorageInfoSync, getStorageSync, getSystemInfo, getSystemInfoSync, getVideoInfo, hideKeyboard, hideLoading, hideNavigationBarLoading, hideTabBar, hideTabBarRedDot, hideToast, loadFontFace, makePhoneCall, navigateBack, navigateTo, offAccelerometerChange, offCompassChange, offNetworkStatusChange, onAccelerometerChange, onCompassChange, onNetworkStatusChange, onSocketClose, onSocketError, onSocketMessage, onSocketOpen, onTabBarMidButtonTap, openDocument, index$7 as plugin, promiseInterceptor, reLaunch, redirectTo, removeInterceptor, removeStorage, removeStorageSync, removeTabBarBadge, request, sendSocketMessage, setNavigationBarColor, setNavigationBarTitle, setStorage, setStorageSync, setTabBarBadge, setTabBarItem, setTabBarStyle, setupApp, setupPage, showActionSheet, showLoading, showModal, showNavigationBarLoading, showTabBar, showTabBarRedDot, showToast, startAccelerometer, startCompass, stopAccelerometer, stopCompass, switchTab, uni$1 as uni, uploadFile, upx2px, useCustomEvent, usePageRoute, useSubscribe, vibrateLong, vibrateShort}; +export {_sfc_main$1 as AsyncErrorComponent, _sfc_main as AsyncLoadingComponent, _sfc_main$l as Audio, index$5 as Button, _sfc_main$k as Canvas, _sfc_main$j as Checkbox, _sfc_main$i as CheckboxGroup, _sfc_main$h as Editor, index$6 as Form, index$4 as Icon, index$3 as Image, _sfc_main$g as Input, _sfc_main$f as Label, LayoutComponent, _sfc_main$e as MovableView, _sfc_main$d as Navigator, index as PageComponent, _sfc_main$c as Progress, _sfc_main$b as Radio, _sfc_main$a as RadioGroup, ResizeSensor, _sfc_main$9 as RichText, _sfc_main$8 as ScrollView, _sfc_main$7 as Slider, _sfc_main$6 as SwiperItem, _sfc_main$5 as Switch, index$2 as Text, _sfc_main$4 as Textarea, UniServiceJSBridge$1 as UniServiceJSBridge, UniViewJSBridge$1 as UniViewJSBridge, _sfc_main$3 as Video, index$1 as View, addInterceptor, arrayBufferToBase64, base64ToArrayBuffer, canIUse, chooseFile, chooseImage, chooseVideo, clearStorage, clearStorageSync, closeSocket, connectSocket, createInnerAudioContext, createIntersectionObserver, createSelectorQuery, createVideoContext, cssBackdropFilter, cssConstant, cssEnv, cssVar, downloadFile, getApp$1 as getApp, getCurrentPages$1 as getCurrentPages, getFileInfo, getImageInfo, getLocation, getNetworkType, getStorage, getStorageInfo, getStorageInfoSync, getStorageSync, getSystemInfo, getSystemInfoSync, getVideoInfo, hideKeyboard, hideLoading, hideNavigationBarLoading, hideTabBar, hideTabBarRedDot, hideToast, loadFontFace, makePhoneCall, navigateBack, navigateTo, offAccelerometerChange, offCompassChange, offNetworkStatusChange, onAccelerometerChange, onCompassChange, onNetworkStatusChange, onSocketClose, onSocketError, onSocketMessage, onSocketOpen, onTabBarMidButtonTap, openDocument, pageScrollTo, index$7 as plugin, promiseInterceptor, reLaunch, redirectTo, removeInterceptor, removeStorage, removeStorageSync, removeTabBarBadge, request, sendSocketMessage, setNavigationBarColor, setNavigationBarTitle, setStorage, setStorageSync, setTabBarBadge, setTabBarItem, setTabBarStyle, setupApp, setupPage, showActionSheet, showLoading, showModal, showNavigationBarLoading, showTabBar, showTabBarRedDot, showToast, startAccelerometer, startCompass, stopAccelerometer, stopCompass, switchTab, uni$1 as uni, uploadFile, upx2px, useCustomEvent, usePageRoute, useSubscribe, vibrateLong, vibrateShort}; diff --git a/packages/uni-h5/src/service/api/index.ts b/packages/uni-h5/src/service/api/index.ts index 31b97661d..c885ce8b0 100644 --- a/packages/uni-h5/src/service/api/index.ts +++ b/packages/uni-h5/src/service/api/index.ts @@ -38,6 +38,7 @@ export * from './route/switchTab' export * from './ui/loadFontFace' export * from './ui/navigationBar' +export * from './ui/pageScrollTo' export * from './ui/popup' export * from './ui/tabBar' diff --git a/packages/uni-h5/src/service/api/ui/pageScrollTo.ts b/packages/uni-h5/src/service/api/ui/pageScrollTo.ts new file mode 100644 index 000000000..a3404c671 --- /dev/null +++ b/packages/uni-h5/src/service/api/ui/pageScrollTo.ts @@ -0,0 +1,18 @@ +import { + API_PAGE_SCROLL_TO, + API_TYPE_PAGE_SCROLL_TO, + defineAsyncApi, + PageScrollToOptions, + PageScrollToProtocol, +} from '@dcloudio/uni-api' +import { scrollTo } from '@dcloudio/uni-shared' + +export const pageScrollTo = defineAsyncApi( + API_PAGE_SCROLL_TO, + ({ scrollTop, selector, duration }, { resolve }) => { + scrollTo(selector! || scrollTop! || 0, duration!) + resolve() + }, + PageScrollToProtocol, + PageScrollToOptions +) diff --git a/packages/uni-shared/dist/uni-shared.cjs.js b/packages/uni-shared/dist/uni-shared.cjs.js index a792b325f..ddeab2310 100644 --- a/packages/uni-shared/dist/uni-shared.cjs.js +++ b/packages/uni-shared/dist/uni-shared.cjs.js @@ -44,6 +44,40 @@ function addFont(family, source, desc) { document.head.appendChild(style); resolve(); }); +} +function scrollTo(scrollTop, duration) { + if (shared.isString(scrollTop)) { + const el = document.querySelector(scrollTop); + if (el) { + scrollTop = el.getBoundingClientRect().top + window.pageYOffset; + } + } + if (scrollTop < 0) { + scrollTop = 0; + } + const documentElement = document.documentElement; + const { clientHeight, scrollHeight } = documentElement; + scrollTop = Math.min(scrollTop, scrollHeight - clientHeight); + if (duration === 0) { + // 部分浏览器(比如微信)中 scrollTop 的值需要通过 document.body 来控制 + documentElement.scrollTop = document.body.scrollTop = scrollTop; + return; + } + if (window.scrollY === scrollTop) { + return; + } + const scrollTo = (duration) => { + if (duration <= 0) { + window.scrollTo(0, scrollTop); + return; + } + const distaince = scrollTop - window.scrollY; + requestAnimationFrame(function () { + window.scrollTo(0, window.scrollY + (distaince / duration) * 10); + scrollTo(duration - 10); + }); + }; + scrollTo(duration); } function plusReady(callback) { @@ -288,5 +322,6 @@ exports.parseQuery = parseQuery; exports.passive = passive; exports.plusReady = plusReady; exports.removeLeadingSlash = removeLeadingSlash; +exports.scrollTo = scrollTo; exports.stringifyQuery = stringifyQuery; exports.updateElementStyle = updateElementStyle; diff --git a/packages/uni-shared/dist/uni-shared.d.ts b/packages/uni-shared/dist/uni-shared.d.ts index beeff4bb7..2e646ccc2 100644 --- a/packages/uni-shared/dist/uni-shared.d.ts +++ b/packages/uni-shared/dist/uni-shared.d.ts @@ -72,6 +72,9 @@ export declare function removeLeadingSlash(str: string): string; export declare const RESPONSIVE_MIN_WIDTH = 768; +declare function scrollTo_2(scrollTop: number | string, duration: number): void; +export { scrollTo_2 as scrollTo } + export declare function stringifyQuery(obj?: Record, encodeStr?: typeof encodeURIComponent): string; export declare const TABBAR_HEIGHT = 50; diff --git a/packages/uni-shared/dist/uni-shared.esm.js b/packages/uni-shared/dist/uni-shared.esm.js index 9b9044591..3e0695fc0 100644 --- a/packages/uni-shared/dist/uni-shared.esm.js +++ b/packages/uni-shared/dist/uni-shared.esm.js @@ -1,4 +1,4 @@ -import { isHTMLTag, isSVGTag, isPlainObject, isArray } from '@vue/shared'; +import { isString, isHTMLTag, isSVGTag, isPlainObject, isArray } from '@vue/shared'; function passive(passive) { return { passive }; @@ -40,6 +40,40 @@ function addFont(family, source, desc) { document.head.appendChild(style); resolve(); }); +} +function scrollTo(scrollTop, duration) { + if (isString(scrollTop)) { + const el = document.querySelector(scrollTop); + if (el) { + scrollTop = el.getBoundingClientRect().top + window.pageYOffset; + } + } + if (scrollTop < 0) { + scrollTop = 0; + } + const documentElement = document.documentElement; + const { clientHeight, scrollHeight } = documentElement; + scrollTop = Math.min(scrollTop, scrollHeight - clientHeight); + if (duration === 0) { + // 部分浏览器(比如微信)中 scrollTop 的值需要通过 document.body 来控制 + documentElement.scrollTop = document.body.scrollTop = scrollTop; + return; + } + if (window.scrollY === scrollTop) { + return; + } + const scrollTo = (duration) => { + if (duration <= 0) { + window.scrollTo(0, scrollTop); + return; + } + const distaince = scrollTop - window.scrollY; + requestAnimationFrame(function () { + window.scrollTo(0, window.scrollY + (distaince / duration) * 10); + scrollTo(duration - 10); + }); + }; + scrollTo(duration); } function plusReady(callback) { @@ -258,4 +292,4 @@ 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, stringifyQuery, updateElementStyle }; +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 }; diff --git a/packages/uni-shared/src/dom.ts b/packages/uni-shared/src/dom.ts index d6b1ecfcf..a1b2be57d 100644 --- a/packages/uni-shared/src/dom.ts +++ b/packages/uni-shared/src/dom.ts @@ -1,4 +1,5 @@ import { FontFaceDescriptors } from 'css-font-loading-module' +import { isString } from '@vue/shared' export function passive(passive: boolean) { return { passive } @@ -57,3 +58,38 @@ export function addFont( resolve() }) } + +export function scrollTo(scrollTop: number | string, duration: number) { + if (isString(scrollTop)) { + const el = document.querySelector(scrollTop) + if (el) { + scrollTop = el.getBoundingClientRect().top + window.pageYOffset + } + } + if (scrollTop < 0) { + scrollTop = 0 + } + const documentElement = document.documentElement + const { clientHeight, scrollHeight } = documentElement + scrollTop = Math.min(scrollTop as number, scrollHeight - clientHeight) + if (duration === 0) { + // 部分浏览器(比如微信)中 scrollTop 的值需要通过 document.body 来控制 + documentElement.scrollTop = document.body.scrollTop = scrollTop + return + } + if (window.scrollY === scrollTop) { + return + } + const scrollTo = (duration: number) => { + if (duration <= 0) { + window.scrollTo(0, scrollTop as number) + return + } + const distaince = (scrollTop as number) - window.scrollY + requestAnimationFrame(function () { + window.scrollTo(0, window.scrollY + (distaince / duration) * 10) + scrollTo(duration - 10) + }) + } + scrollTo(duration) +} -- GitLab