From dad39a9e363448386d99da358e2f3233a2c3a920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cazhewang=E2=80=9D?= Date: Mon, 1 Dec 2025 19:15:49 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20floating-panel=E6=8B=96=E5=8A=A8?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/config/sidebar.js | 4 + example/app.mpx | 3 +- example/common/config.ts | 3 +- example/pages/floating-panel/index.mpx | 42 ++++ .../theme/components/floating-panel.styl | 20 ++ .../src/components/floating-panel/css.rn.styl | 2 + .../src/components/floating-panel/css.styl | 37 +++ .../floating-panel/floating-panel.ts | 211 ++++++++++++++++++ .../src/components/floating-panel/index.mpx | 45 ++++ 9 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 example/pages/floating-panel/index.mpx create mode 100644 packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl create mode 100644 packages/mpx-cube-ui/src/components/floating-panel/css.rn.styl create mode 100644 packages/mpx-cube-ui/src/components/floating-panel/css.styl create mode 100644 packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts create mode 100644 packages/mpx-cube-ui/src/components/floating-panel/index.mpx diff --git a/docs/config/sidebar.js b/docs/config/sidebar.js index a28fae0..e5b3f97 100644 --- a/docs/config/sidebar.js +++ b/docs/config/sidebar.js @@ -154,6 +154,10 @@ module.exports = [ { title: 'ActionSheet 操作列表', path: '/components/base/action-sheet' + }, + { + title: 'FloatingPanel 悬浮面板', + path: '/components/base/floating-panel' } ] }, diff --git a/example/app.mpx b/example/app.mpx index 31ac28e..876de38 100644 --- a/example/app.mpx +++ b/example/app.mpx @@ -49,7 +49,8 @@ "./pages/switch/index", "./pages/loading/index", "./pages/input/index", - "./pages/action-sheet/index" + "./pages/action-sheet/index", + "./pages/floating-panel/index" ] } diff --git a/example/common/config.ts b/example/common/config.ts index 51b4910..c0544bd 100644 --- a/example/common/config.ts +++ b/example/common/config.ts @@ -72,7 +72,8 @@ export default { 'dialog', 'modal', 'tip', - 'action-sheet' + 'action-sheet', + 'floating-panel' ] }, { diff --git a/example/pages/floating-panel/index.mpx b/example/pages/floating-panel/index.mpx new file mode 100644 index 0000000..74ab40e --- /dev/null +++ b/example/pages/floating-panel/index.mpx @@ -0,0 +1,42 @@ + + + + + + + diff --git a/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl b/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl new file mode 100644 index 0000000..62bb727 --- /dev/null +++ b/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl @@ -0,0 +1,20 @@ +// @type floating-panel +$floating-panel-z-index := 999 +$floating-panel-top-left-radius := 7px +$floating-panel-top-right-radius := 7px +$floating-panel-bottom-right-radius := 0 +$floating-panel-bottom-left-radius := 0 +$floating-panel-radius := $floating-panel-top-left-radius $floating-panel-top-right-radius $floating-panel-bottom-right-radius $floating-panel-bottom-left-radius // 边框圆角 +$floating-panel-bgc := $var(color-white) +$floating-panel-header-height := 30px +$floating-panel-bar-width := 20px +$floating-panel-bar-height := 3px +$floating-panel-bar-color := #c8c9cc + + +// @type floating-panel-bottom +$floating-panel-padding-top := 0 +$floating-panel-padding-right := 0 +$floating-panel-padding-bottom := 20px +$floating-panel-padding-left := 0 +$floating-panel-padding := $floating-panel-padding-top $floating-panel-padding-right $floating-panel-padding-bottom $floating-panel-padding-left diff --git a/packages/mpx-cube-ui/src/components/floating-panel/css.rn.styl b/packages/mpx-cube-ui/src/components/floating-panel/css.rn.styl new file mode 100644 index 0000000..983d66a --- /dev/null +++ b/packages/mpx-cube-ui/src/components/floating-panel/css.rn.styl @@ -0,0 +1,2 @@ +@require "../../common/stylus/variable.styl" +@require "../../common/stylus/mixin.styl" \ No newline at end of file diff --git a/packages/mpx-cube-ui/src/components/floating-panel/css.styl b/packages/mpx-cube-ui/src/components/floating-panel/css.styl new file mode 100644 index 0000000..e8289e0 --- /dev/null +++ b/packages/mpx-cube-ui/src/components/floating-panel/css.styl @@ -0,0 +1,37 @@ +@require "../../common/stylus/variable.styl" +@require "../../common/stylus/mixin.styl" +@require "../../common/stylus/theme/components/floating-panel.styl" + +.cube-floating-panel + position fixed + z-index $floating-panel-z-index + left 0 + bottom 0 + width 100vw + display flex + flex-direction column + background-color $floating-panel-bgc + border-radius $floating-panel-radius +.cube-floating-panel-extender + position absolute + bottom -100vh + height 100vh + width 100vw + background-color inherit + pointer-events none +.cube-floating-panel-header + display flex + align-items center + justify-content center + height $floating-panel-header-height +.cube-floating-panel-header-bar + width $floating-panel-bar-width + height $floating-panel-bar-height + border-radius 3px + background-color $floating-panel-bar-color +.cube-floating-panel-content + flex 1 + overflow-y auto +.cube-floating-panel-safe-area-bottom + padding-all($var(floating-panel-padding-top), $var(floating-panel-padding-right), $var(floating-panel-padding-bottom), $var(floating-panel-padding-left)) + safe-area-mixin-extra(padding-bottom, bottom, $var(floating-panel-safe-padding), true) \ No newline at end of file diff --git a/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts b/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts new file mode 100644 index 0000000..9d769f8 --- /dev/null +++ b/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts @@ -0,0 +1,211 @@ +import { createComponent } from '../../common/helper/create-component' + +const EVENT_INPUT = 'input' +const WINDOW_HEIGHT = window.innerHeight +const DEFAULT_HEIGHT = 100 +const DEFAULT_EXPAND_HEIGHT = Math.round(WINDOW_HEIGHT * 0.6) +const DAMP = 0.2 + +const regExp = /^-?\d+(\.\d+)?$/ +const addUnit = (value: number | string) => (regExp.test('' + value) ? value + 'px' : value) +export function closest(arr: number[], target: number) { + return arr.reduce((pre, cur) => + Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur + ) +} + +createComponent({ + properties: { + // 当前面板的显示高度 + height: { + type: Number, + optionalTypes: [String], + value: 0 + }, + // 设置自定义锚点, 单位 px + anchors: { + type: Array, + value: [DEFAULT_HEIGHT, DEFAULT_EXPAND_HEIGHT] + }, + // 拖拽结束时是否吸附到预设锚点 + magnetic: { + type: Boolean, + value: true + }, + // 动画时长,单位秒,设置为 0 可以禁用动画 + duration: { + type: Number, + optionalTypes: [String], + value: 0.3 + }, + // 允许拖拽内容容器 + contentDraggable: { + type: Boolean, + value: true + }, + // 当不拖拽时,是否锁定背景滚动 + lockScroll: { + type: Boolean, + value: false + }, + // 是否开启底部安全区适配 + safeAreaInsetBottom: { + type: Boolean, + value: true + } + }, + data: { + heightVal: 0, + dragging: false, + startY: 0, + startPageY: 0, + deltaY: 0, + maxScroll: -1, + contentScrollTop: 0 + }, + computed: { + _anchors() { + const list: number[] = Array.isArray(this.anchors) ? this.anchors : [] + const min = list[0] ?? DEFAULT_HEIGHT + const max = list.length ? list[list.length - 1] : DEFAULT_EXPAND_HEIGHT + return list.length >= 2 ? list : [min, max] + }, + boundary() { + const list: number[] = this._anchors + return { + min: list[0] ?? DEFAULT_HEIGHT, + max: list[list.length - 1] ?? DEFAULT_EXPAND_HEIGHT + } + }, + rootStyle() { + const h = this.heightVal + const { max } = this.boundary + return { + height: addUnit(max), + transform: `translateY(calc(100% + ${addUnit(-h)}))`, + transition: this.dragging + ? 'none' + : `transform ${this.duration}s cubic-bezier(0.18, 0.89, 0.32, 1.28)` + } + } + }, + watch: { + height: { + handler(newVal: number | string) { + const val = +newVal || 0 + const { min, max } = this.boundary + this.heightVal = Math.round(Math.max(min, Math.min(max, val))) + }, + immediate: true + }, + anchors: { + handler() { + this.heightVal = Math.round(closest(this._anchors, this.heightVal)) + } + } + }, + methods: { + ease(moveY: number) { + const absDistance = Math.abs(moveY) + const { min, max } = this.boundary + if (absDistance > max) { + return -(max + (absDistance - max) * DAMP) + } + if (absDistance < min) { + return -(min - (min - absDistance) * DAMP) + } + return moveY + }, + onHeaderTouchStart(e: TouchEvent) { + const touch = e.touches && e.touches[0] + this.dragging = true + this.startPageY = touch ? touch.pageY : 0 + this.startY = -this.heightVal + this.maxScroll = -1 + }, + onHeaderTouchMove(e: TouchEvent) { + const touch = e.touches && e.touches[0] + if (!touch) return + this.deltaY = touch.pageY - this.startPageY + const moveY = this.deltaY + this.startY + const val = Math.round(-this.ease(moveY)) + this.heightVal = val + this.triggerEvent(EVENT_INPUT, { value: val }) + }, + onHeaderTouchEnd() { + this.maxScroll = -1 + this.dragging = false + const { min, max } = this.boundary + if (this.magnetic) { + this.heightVal = this._anchors && this._anchors.length + ? closest(this._anchors, this.heightVal) + : Math.max(min, Math.min(max, this.heightVal)) + } else { + this.heightVal = Math.max(min, Math.min(max, this.heightVal)) + } + this.heightVal = Math.round(this.heightVal) + this.triggerEvent(EVENT_INPUT, { value: this.heightVal }) + }, + onContentScroll(e: any) { + const detail = e && e.detail + const fromDetail = typeof detail?.scrollTop === 'number' ? detail.scrollTop : undefined + const fromTarget = e ? (e.target?.scrollTop ?? e.currentTarget?.scrollTop) : undefined + const st = typeof fromDetail === 'number' ? fromDetail : fromTarget + this.contentScrollTop = typeof st === 'number' ? st : 0 + }, + onContentTouchStart(e: TouchEvent) { + if (!this.contentDraggable) return + const touch = e.touches && e.touches[0] + this.dragging = true + this.startPageY = touch ? touch.pageY : 0 + this.startY = -this.heightVal + this.maxScroll = -1 + }, + onContentTouchMove(e: TouchEvent) { + if (!this.contentDraggable) return + const touch = e.touches && e.touches[0] + if (!touch) return + this.deltaY = touch.pageY - this.startPageY + this.maxScroll = Math.max(this.maxScroll, this.contentScrollTop) + if (-this.startY < this.boundary.max) { + const moveY = this.deltaY + this.startY + const val = Math.round(-this.ease(moveY)) + this.heightVal = val + this.triggerEvent(EVENT_INPUT, { value: val }) + return + } + if (!(this.contentScrollTop <= 0 && this.deltaY > 0) || this.maxScroll > 0) { + return + } + const moveY = this.deltaY + this.startY + const val = Math.round(-this.ease(moveY)) + this.heightVal = val + this.triggerEvent(EVENT_INPUT, { value: val }) + }, + onContentTouchEnd() { + if (!this.contentDraggable) return + this.maxScroll = -1 + this.dragging = false + const prev = -this.startY + const { min, max } = this.boundary + if (this.magnetic) { + this.heightVal = this._anchors && this._anchors.length + ? closest(this._anchors, this.heightVal) + : Math.max(min, Math.min(max, this.heightVal)) + } else { + this.heightVal = Math.max(min, Math.min(max, this.heightVal)) + } + this.heightVal = Math.round(this.heightVal) + if (this.heightVal !== prev) { + this.triggerEvent(EVENT_INPUT, { value: this.heightVal }) + } + }, + preventTouchMove(e: TouchEvent) { + if (this.lockScroll || this.dragging) { + if (e && typeof e.preventDefault === 'function') { + e.preventDefault() + } + } + } + } +}) diff --git a/packages/mpx-cube-ui/src/components/floating-panel/index.mpx b/packages/mpx-cube-ui/src/components/floating-panel/index.mpx new file mode 100644 index 0000000..58ea574 --- /dev/null +++ b/packages/mpx-cube-ui/src/components/floating-panel/index.mpx @@ -0,0 +1,45 @@ + + + + + + + From 9cee0eab527c9985ec45500e29a2c62c5b32e478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cazhewang=E2=80=9D?= Date: Mon, 1 Dec 2025 21:48:41 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=E5=AE=8C=E6=88=90floating-panel?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pages/floating-panel/README.md | 41 +++++++ .../floating-panel/floating-panel-anchors.mpx | 34 ++++++ .../floating-panel-contentDraggable.mpx | 37 ++++++ .../floating-panel/floating-panel-default.mpx | 31 +++++ example/pages/floating-panel/index.mpx | 6 +- .../__snapshots__/floating-panel.spec.js.snap | 5 + .../floating-panel/floating-panel.spec.js | 106 ++++++++++++++++++ .../theme/components/floating-panel.styl | 31 ++--- .../src/components/floating-panel/css.styl | 18 +-- .../floating-panel/floating-panel.ts | 75 +++---------- .../src/components/floating-panel/index.mpx | 17 ++- 11 files changed, 309 insertions(+), 92 deletions(-) create mode 100644 example/pages/floating-panel/README.md create mode 100644 example/pages/floating-panel/floating-panel-anchors.mpx create mode 100644 example/pages/floating-panel/floating-panel-contentDraggable.mpx create mode 100644 example/pages/floating-panel/floating-panel-default.mpx create mode 100644 packages/mpx-cube-ui/__tests__/components/floating-panel/__snapshots__/floating-panel.spec.js.snap create mode 100644 packages/mpx-cube-ui/__tests__/components/floating-panel/floating-panel.spec.js diff --git a/example/pages/floating-panel/README.md b/example/pages/floating-panel/README.md new file mode 100644 index 0000000..a3d9070 --- /dev/null +++ b/example/pages/floating-panel/README.md @@ -0,0 +1,41 @@ +## Cube-FloatingPanel + + + +### 介绍 + +浮层组件,浮动在页面底部的面板,可以上下拖动来浏览内容,常用于提供额外的功能或信息。支持使用`wx:model`对数据双向绑定。 + + + +## 示例 + + + +### 基础用法 + +FloatingPanel 的默认高度为 `100px`,用户可以拖动来展开面板,使高度达到 `60%` 的屏幕高度。 + + + + + + +### 自定义锚点 + +你可以通过 `anchors` 属性来设置 FloatingPanel 的锚点位置,并通过 `wx:model` 来控制当前面板的显示高度。 + +比如,使面板的高度在 `100px`、`40%` 屏幕高度和 `70%` 屏幕高度三个位置停靠: + + + + + + +### 仅头部拖拽 + +默认情况下,FloatingPanel 的头部区域和内容区域都可以被拖拽,你可以通过 `contentDraggable` 属性来禁用内容区域的拖拽。 + + + + \ No newline at end of file diff --git a/example/pages/floating-panel/floating-panel-anchors.mpx b/example/pages/floating-panel/floating-panel-anchors.mpx new file mode 100644 index 0000000..063ef90 --- /dev/null +++ b/example/pages/floating-panel/floating-panel-anchors.mpx @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file diff --git a/example/pages/floating-panel/floating-panel-contentDraggable.mpx b/example/pages/floating-panel/floating-panel-contentDraggable.mpx new file mode 100644 index 0000000..6501376 --- /dev/null +++ b/example/pages/floating-panel/floating-panel-contentDraggable.mpx @@ -0,0 +1,37 @@ + + + + + + + diff --git a/example/pages/floating-panel/floating-panel-default.mpx b/example/pages/floating-panel/floating-panel-default.mpx new file mode 100644 index 0000000..52fa3e1 --- /dev/null +++ b/example/pages/floating-panel/floating-panel-default.mpx @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/example/pages/floating-panel/index.mpx b/example/pages/floating-panel/index.mpx index 74ab40e..0746a3b 100644 --- a/example/pages/floating-panel/index.mpx +++ b/example/pages/floating-panel/index.mpx @@ -4,9 +4,10 @@ wx:model="{{ height }}" wx:model-prop="height" contentDraggable="{{ contentDraggable }}" + magnetic="{{ magnetic }}" > - height: {{ height }} + height: {{ height }} px @@ -18,8 +19,9 @@ createPage({ data: { - height: '100px', + height: 100, contentDraggable: true, + magnetic: true, letters: [ 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z' diff --git a/packages/mpx-cube-ui/__tests__/components/floating-panel/__snapshots__/floating-panel.spec.js.snap b/packages/mpx-cube-ui/__tests__/components/floating-panel/__snapshots__/floating-panel.spec.js.snap new file mode 100644 index 0000000..7b560be --- /dev/null +++ b/packages/mpx-cube-ui/__tests__/components/floating-panel/__snapshots__/floating-panel.spec.js.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`component floating-panel unit test props check default matchSnapshot 1`] = `""`; + +exports[`component floating-panel unit test slot render check matchSnapshot 1`] = `"
"`; diff --git a/packages/mpx-cube-ui/__tests__/components/floating-panel/floating-panel.spec.js b/packages/mpx-cube-ui/__tests__/components/floating-panel/floating-panel.spec.js new file mode 100644 index 0000000..76cafb8 --- /dev/null +++ b/packages/mpx-cube-ui/__tests__/components/floating-panel/floating-panel.spec.js @@ -0,0 +1,106 @@ +const simulate = require('@mpxjs/miniprogram-simulate') + +describe('component floating-panel unit test', () => { + const componentId = simulate.loadMpx('src/components/floating-panel/index.mpx') + + function newComponent(props) { + const component = simulate.render(componentId, props) + component.attach(document.createElement('parent')) + return component + } + + async function _snapTest(component) { + expect(component.dom.innerHTML).toMatchSnapshot() + } + + const baseProps = { + height: 100, + anchors: [100, 500], + duration: 0.3 + } + + describe('props check default', () => { + const component = newComponent(baseProps) + + it('matchSnapshot', () => { + _snapTest(component) + }) + + it('should render correct initial style', () => { + const root = component.querySelector('.cube-floating-panel') + // height: anchors[max] -> 500px + expect(root.dom.style.height).toBe('500px') + // transform: translateY(calc(100% + -100px)) because initial height is 100 + expect(root.dom.style.transform).toBe('translateY(calc(100% + -100px))') + }) + }) + + describe('slot render check', () => { + const component = simulate.render(simulate.load({ + usingComponents: { + 'cube-floating-panel': componentId + }, + template: ` + + test + ` + })) + const parent = document.createElement('parent') + component.attach(parent) + it('matchSnapshot', () => { + _snapTest(component) + }) + + it('should render correct slot content', () => { + const content = component.querySelector('.slot-node').dom.innerHTML + expect(content).toBe('test') + }) + }) + + describe('props check safeAreaInsetBottom', () => { + const props = Object.assign({}, baseProps, { + safeAreaInsetBottom: false + }) + const component = newComponent(props) + it('should not have safe-area class', () => { + const root = component.querySelector('.cube-floating-panel') + expect(root.dom.classList.contains('cube-floating-panel-safe-area-bottom')).toBe(false) + }) + }) + + describe('props check contentDraggable', () => { + it('should drag when contentDraggable is true (default)', async () => { + // This is covered by default props, but we can test on content element + const component = newComponent(baseProps) + const content = component.querySelector('.cube-floating-panel-content') + const start = { touches: [{ pageY: 500 }] } + const move = { touches: [{ pageY: 450 }] } + const inputHandler = jest.fn() + component.addEventListener('input', inputHandler) + + content.dispatchEvent('touchstart', start) + content.dispatchEvent('touchmove', move) + await simulate.sleep(10) + + expect(inputHandler).toHaveBeenCalled() + }) + + it('should not drag when contentDraggable is false', async () => { + const props = Object.assign({}, baseProps, { + contentDraggable: false + }) + const component = newComponent(props) + const content = component.querySelector('.cube-floating-panel-content') + const start = { touches: [{ pageY: 500 }] } + const move = { touches: [{ pageY: 450 }] } + const inputHandler = jest.fn() + component.addEventListener('input', inputHandler) + + content.dispatchEvent('touchstart', start) + content.dispatchEvent('touchmove', move) + await simulate.sleep(10) + + expect(inputHandler).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl b/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl index 62bb727..2b6302f 100644 --- a/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl +++ b/packages/mpx-cube-ui/src/common/stylus/theme/components/floating-panel.styl @@ -1,20 +1,21 @@ // @type floating-panel -$floating-panel-z-index := 999 -$floating-panel-top-left-radius := 7px -$floating-panel-top-right-radius := 7px -$floating-panel-bottom-right-radius := 0 -$floating-panel-bottom-left-radius := 0 +$floating-panel-z-index := 999 // 浮层z-index +$floating-panel-top-left-radius := 16px // 浮层左上角圆角 +$floating-panel-top-right-radius := 16px // 浮层右上角圆角 +$floating-panel-bottom-right-radius := 0 // 浮层右下角圆角 +$floating-panel-bottom-left-radius := 0 // 浮层左下角圆角 $floating-panel-radius := $floating-panel-top-left-radius $floating-panel-top-right-radius $floating-panel-bottom-right-radius $floating-panel-bottom-left-radius // 边框圆角 -$floating-panel-bgc := $var(color-white) -$floating-panel-header-height := 30px -$floating-panel-bar-width := 20px -$floating-panel-bar-height := 3px -$floating-panel-bar-color := #c8c9cc +$floating-panel-bgc := $var(color-white) // 浮层背景颜色 +$floating-panel-header-height := 30px // 浮层标题高度 +$floating-panel-bar-width := 20px // 浮层拖动条宽度 +$floating-panel-bar-height := 3px // 浮层拖动条高度 +$floating-panel-bar-color := #c8c9cc // 浮层拖动条颜色 +$floating-panel-border-color := #c8c9cc // 浮层边框颜色 // @type floating-panel-bottom -$floating-panel-padding-top := 0 -$floating-panel-padding-right := 0 -$floating-panel-padding-bottom := 20px -$floating-panel-padding-left := 0 -$floating-panel-padding := $floating-panel-padding-top $floating-panel-padding-right $floating-panel-padding-bottom $floating-panel-padding-left +$floating-panel-padding-top := 0 // 浮层区域顶部内边距 +$floating-panel-padding-right := 0 // 浮层内容区域右侧内边距 +$floating-panel-padding-bottom := 20px // 浮层内容区域底部内边距 +$floating-panel-padding-left := 0 // 浮层内容区域左侧内边距 +$floating-panel-padding := $floating-panel-padding-top $floating-panel-padding-right $floating-panel-padding-bottom $floating-panel-padding-left // 浮层内容区域内边距 diff --git a/packages/mpx-cube-ui/src/components/floating-panel/css.styl b/packages/mpx-cube-ui/src/components/floating-panel/css.styl index e8289e0..398804c 100644 --- a/packages/mpx-cube-ui/src/components/floating-panel/css.styl +++ b/packages/mpx-cube-ui/src/components/floating-panel/css.styl @@ -4,14 +4,14 @@ .cube-floating-panel position fixed - z-index $floating-panel-z-index + z-index $var(floating-panel-z-index) left 0 bottom 0 width 100vw display flex flex-direction column - background-color $floating-panel-bgc - border-radius $floating-panel-radius + background-color $var(floating-panel-bgc) + border-radius $var(floating-panel-radius) .cube-floating-panel-extender position absolute bottom -100vh @@ -23,15 +23,15 @@ display flex align-items center justify-content center - height $floating-panel-header-height + height $var(floating-panel-header-height) .cube-floating-panel-header-bar - width $floating-panel-bar-width - height $floating-panel-bar-height - border-radius 3px - background-color $floating-panel-bar-color + width $var(floating-panel-bar-width) + height $var(floating-panel-bar-height) + border-radius $var(floating-panel-bar-radius) + background-color $var(floating-panel-bar-color) .cube-floating-panel-content flex 1 overflow-y auto .cube-floating-panel-safe-area-bottom padding-all($var(floating-panel-padding-top), $var(floating-panel-padding-right), $var(floating-panel-padding-bottom), $var(floating-panel-padding-left)) - safe-area-mixin-extra(padding-bottom, bottom, $var(floating-panel-safe-padding), true) \ No newline at end of file + safe-area-mixin-extra(padding-bottom, bottom, $var(floating-panel-safe-padding), true) diff --git a/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts b/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts index 9d769f8..5f26bc6 100644 --- a/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts +++ b/packages/mpx-cube-ui/src/components/floating-panel/floating-panel.ts @@ -1,7 +1,8 @@ +import mpx from '@mpxjs/core' import { createComponent } from '../../common/helper/create-component' const EVENT_INPUT = 'input' -const WINDOW_HEIGHT = window.innerHeight +const WINDOW_HEIGHT = mpx.getSystemInfoSync().windowHeight const DEFAULT_HEIGHT = 100 const DEFAULT_EXPAND_HEIGHT = Math.round(WINDOW_HEIGHT * 0.6) const DAMP = 0.2 @@ -15,6 +16,9 @@ export function closest(arr: number[], target: number) { } createComponent({ + options: { + multipleSlots: true + }, properties: { // 当前面板的显示高度 height: { @@ -43,15 +47,15 @@ createComponent({ type: Boolean, value: true }, - // 当不拖拽时,是否锁定背景滚动 - lockScroll: { - type: Boolean, - value: false - }, // 是否开启底部安全区适配 safeAreaInsetBottom: { type: Boolean, value: true + }, + // 不展示默认拖动条 + hideHeaderBar: { + type: Boolean, + value: false } }, data: { @@ -116,23 +120,26 @@ createComponent({ } return moveY }, - onHeaderTouchStart(e: TouchEvent) { + onTouchStart(e: TouchEvent) { const touch = e.touches && e.touches[0] + console.log('onTouchStart', { touch, touches: e.touches }) this.dragging = true this.startPageY = touch ? touch.pageY : 0 this.startY = -this.heightVal this.maxScroll = -1 }, - onHeaderTouchMove(e: TouchEvent) { + onTouchMove(e: TouchEvent) { const touch = e.touches && e.touches[0] + console.log('onTouchMove', { touch, touches: e.touches, startPageY: this.startPageY, startY: this.startY, heightVal: this.heightVal }) if (!touch) return this.deltaY = touch.pageY - this.startPageY const moveY = this.deltaY + this.startY const val = Math.round(-this.ease(moveY)) + console.log('onTouchMove val', val, 'moveY', moveY, 'deltaY', this.deltaY) this.heightVal = val this.triggerEvent(EVENT_INPUT, { value: val }) }, - onHeaderTouchEnd() { + onTouchEnd() { this.maxScroll = -1 this.dragging = false const { min, max } = this.boundary @@ -155,57 +162,11 @@ createComponent({ }, onContentTouchStart(e: TouchEvent) { if (!this.contentDraggable) return - const touch = e.touches && e.touches[0] - this.dragging = true - this.startPageY = touch ? touch.pageY : 0 - this.startY = -this.heightVal - this.maxScroll = -1 + this.onTouchStart(e) }, onContentTouchMove(e: TouchEvent) { if (!this.contentDraggable) return - const touch = e.touches && e.touches[0] - if (!touch) return - this.deltaY = touch.pageY - this.startPageY - this.maxScroll = Math.max(this.maxScroll, this.contentScrollTop) - if (-this.startY < this.boundary.max) { - const moveY = this.deltaY + this.startY - const val = Math.round(-this.ease(moveY)) - this.heightVal = val - this.triggerEvent(EVENT_INPUT, { value: val }) - return - } - if (!(this.contentScrollTop <= 0 && this.deltaY > 0) || this.maxScroll > 0) { - return - } - const moveY = this.deltaY + this.startY - const val = Math.round(-this.ease(moveY)) - this.heightVal = val - this.triggerEvent(EVENT_INPUT, { value: val }) - }, - onContentTouchEnd() { - if (!this.contentDraggable) return - this.maxScroll = -1 - this.dragging = false - const prev = -this.startY - const { min, max } = this.boundary - if (this.magnetic) { - this.heightVal = this._anchors && this._anchors.length - ? closest(this._anchors, this.heightVal) - : Math.max(min, Math.min(max, this.heightVal)) - } else { - this.heightVal = Math.max(min, Math.min(max, this.heightVal)) - } - this.heightVal = Math.round(this.heightVal) - if (this.heightVal !== prev) { - this.triggerEvent(EVENT_INPUT, { value: this.heightVal }) - } - }, - preventTouchMove(e: TouchEvent) { - if (this.lockScroll || this.dragging) { - if (e && typeof e.preventDefault === 'function') { - e.preventDefault() - } - } + this.onTouchMove(e) } } }) diff --git a/packages/mpx-cube-ui/src/components/floating-panel/index.mpx b/packages/mpx-cube-ui/src/components/floating-panel/index.mpx index 58ea574..7d27239 100644 --- a/packages/mpx-cube-ui/src/components/floating-panel/index.mpx +++ b/packages/mpx-cube-ui/src/components/floating-panel/index.mpx @@ -6,21 +6,20 @@ > - - - + + From 326afbe20e741490739c0a560c0ccd67518c511b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cazhewang=E2=80=9D?= Date: Tue, 2 Dec 2025 11:29:04 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96demo=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pages/floating-panel/index.mpx | 9 ++++++--- .../src/components/floating-panel/floating-panel.ts | 5 +---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/pages/floating-panel/index.mpx b/example/pages/floating-panel/index.mpx index 0746a3b..eace275 100644 --- a/example/pages/floating-panel/index.mpx +++ b/example/pages/floating-panel/index.mpx @@ -1,14 +1,14 @@