| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- <template>
- <Popup
- v-bind="props"
- position="bottom"
- round
- :size="`${currentHeight}px`"
- :show="show"
- :noTransition="touching"
- @close="onCancelClick"
- >
- <FlexCol
- position="relative"
- backgroundColor="white"
- :innerStyle="{
- ...themeStyles.bottomSheet.value,
- height: `100%`,
- width: `100%`,
- ...innerStyle,
- }"
- >
- <view
- v-if="enableDrag"
- :style="themeStyles.dragHandleContainer.value"
- @touchstart="onDragStart"
- @touchmove="onDragMove"
- @touchend="onDragEnd"
- >
- <view
- :style="{
- ...themeStyles.dragHandle.value,
- width: themeContext.resolveSize(dragHandleSize),
- backgroundColor: themeContext.resolveThemeColor(dragHandleColor),
- }"
- />
- </view>
- <scroll-view
- :scroll-y="true"
- :style="{
- height: `100%`,
- }"
- >
- <slot name="content" :close="onCancelClick" />
- </scroll-view>
- </FlexCol>
- </Popup>
- </template>
- <script setup lang="ts">
- import { ref } from 'vue';
- import { propGetThemeVar, useTheme, type ViewStyle } from '../theme/ThemeDefine';
- import { DynamicColor, DynamicSize } from '../theme/ThemeTools';
- import Popup from './Popup.vue';
- import FlexCol from '../layout/FlexCol.vue';
- import type { PopupProps } from './Popup.vue';
- export interface BottomSheetProps extends Omit<PopupProps, 'onClose'|'position'|'position'|'size'> {
- /**
- * 是否显示
- * @default true
- */
- show: boolean;
- /**
- * 拖动把手颜色
- * @default 'grey'
- */
- dragHandleColor?: string;
- /**
- * 拖动把手大小
- * @default 100
- */
- dragHandleSize?: number;
- /**
- * 拖动时使高度吸附到指定高度
- * @default undefined
- */
- dragSnapHeights?: number[]|undefined;
- /**
- * 是否启用拖动
- * @default true
- */
- enableDrag?: boolean;
- /**
- * 底部弹窗大小(px)
- * @default '300
- */
- height?: number;
- /**
- * 拖动最大高度(px)
- * @default 1000
- */
- dragMaxHeight?: number;
- /**
- * 拖动最小高度(px)
- * @default 100
- */
- dragMinHeight?: number;
- innerStyle?: ViewStyle;
- }
- export interface BottomSheetExpose {
- setDragHeight: (height: number) => void;
- getDragHeight: () => number;
- setDragHeightToMax: () => void;
- setDragHeightToMin: () => void;
- }
- const emit = defineEmits([ 'close', 'select' ]);
- const props = withDefaults(defineProps<BottomSheetProps>(), {
- enableDrag: true,
- dragHandleColor: () => propGetThemeVar('BottomSheetDragHandleColor', 'grey'),
- dragHandleSize: () => propGetThemeVar('BottomSheetDragHandleSize', 200),
- centerWidth: () => propGetThemeVar('BottomSheetCenterWidth', '600rpx'),
- height: () => propGetThemeVar('BottomSheetHeight', 300),
- dragMaxHeight: () => propGetThemeVar('BottomSheetDragMaxHeight', 1000),
- dragMinHeight: () => propGetThemeVar('BottomSheetDragMinHeight', 100),
- });
- const themeContext = useTheme();
- const themeStyles = themeContext.useThemeStyles({
- bottomSheet: {
- borderTopLeftRadius: DynamicSize('BottomSheetBorderRadius', 10),
- borderTopRightRadius: DynamicSize('BottomSheetBorderRadius', 10),
- backgroundColor: DynamicColor('BottomSheetBackgroundColor', 'white'),
- },
- dragHandleContainer: {
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- },
- dragHandle: {
- height: DynamicSize('BottomSheetDragHandleSize', 10),
- borderRadius: DynamicSize('BottomSheetDragHandleBorderRadius', 10),
- marginTop: DynamicSize('BottomSheetDragHandleMarginVertical', 25),
- marginBottom: DynamicSize('BottomSheetDragHandleMarginVertical', 25),
- },
- });
- const currentHeight = ref(props.height);
- const touching = ref(false);
- let touchStartY = 0;
- let touchStartHeight = 0;
- function onDragStart(e: TouchEvent) {
- if (!props.enableDrag) return;
- e.preventDefault();
- e.stopPropagation();
- touchStartY = e.touches[0].clientY;
- touchStartHeight = currentHeight.value;
- touching.value = true;
- }
- function onDragMove(e: TouchEvent) {
- if (!props.enableDrag || !touching.value) return;
- e.preventDefault();
- e.stopPropagation();
- const deltaY = e.touches[0].clientY - touchStartY;
- currentHeight.value = Math.max(props.dragMinHeight,
- Math.min(props.dragMaxHeight, touchStartHeight - deltaY)
- );
- }
- function onDragEnd(e: TouchEvent) {
- if (!props.enableDrag) return;
- e.preventDefault();
- e.stopPropagation();
- touching.value = false;
- //吸附到指定高度
- if (props.dragSnapHeights) {
- // 使当前高度吸附到最近的指定高度
- const snapHeights = props.dragSnapHeights.slice().sort((a, b) => a - b);
- let nearest = snapHeights[0];
- let minDist = Math.abs(currentHeight.value - snapHeights[0]);
- for (let i = 1; i < snapHeights.length; i++) {
- const dist = Math.abs(currentHeight.value - snapHeights[i]);
- if (dist < minDist) {
- minDist = dist;
- nearest = snapHeights[i];
- }
- }
- currentHeight.value = nearest;
- }
- }
- function onCancelClick() {
- emit('close');
- }
- defineExpose<BottomSheetExpose>({
- setDragHeight: (height: number) => {
- currentHeight.value = height;
- },
- setDragHeightToMax: () => {
- currentHeight.value = props.dragMaxHeight;
- },
- setDragHeightToMin: () => {
- currentHeight.value = props.dragMinHeight;
- },
- getDragHeight: () => {
- return currentHeight.value;
- },
- });
- </script>
|