| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- <template>
- <movable-area v-if="inited" class="suspension-area" :style="areaStyle">
- <movable-view
- class="suspension-view"
- direction="all"
- :x="x"
- :y="y"
- :inertia="inertia"
- :out-of-bounds="true"
- :disabled="false"
- :scale="false"
- :style="{
- width: width,
- height: height,
- }"
- @click="handleClick">
- <slot>
- <image src="https://yujin-szyy.oss-cn-chengdu.aliyuncs.com/szyy/images-plt/common/btn_add_logo.png" mode="widthFix" class="w-120 h-120"></image>
- </slot>
- </movable-view>
- </movable-area>
- </template>
- <script setup lang="ts">
- /**
- * Props
- * - width: 组件宽度字符串,支持 rpx/px,例如 '120rpx'
- * - height: 组件高度字符串,支持 rpx/px,例如 '120rpx'
- * - right: 距右侧偏移,支持 rpx/px,例如 '20rpx'
- * - bottom: 距底部偏移,支持 rpx/px,例如 '200rpx'
- * - inertia/snapThreshold/bgColor/borderRadius: 其他配置
- * 行为:初始化后根据屏幕宽高与 right/bottom 计算 x/y,默认右下角,避免渲染闪动
- */
- // 定义props
- interface Props {
- width?: string; // 宽度 rpx/px 字符串
- height?: string; // 高度 rpx/px 字符串
- right?: string; // 距右侧 rpx/px 字符串
- bottom?: string; // 距底部 rpx/px 字符串
- inertia?: boolean;
- bgColor?: string;
- borderRadius?: string;
- }
- const props = withDefaults(defineProps<Props>(), {
- width: '120rpx', // 默认宽度
- height: '120rpx', // 默认高度
- right: '10rpx', // 默认右侧间距
- bottom: '280rpx', // 默认底部间距
- inertia: false,
- bgColor: 'transparent',
- borderRadius: '0',
- });
- // 响应式状态
- const x = ref<number>(0); // movable-view的x坐标(距离左边)
- const y = ref<number>(0); // movable-view的y坐标(距离顶部)
- const inited = ref<boolean>(false); // 控制渲染时机,避免闪动
- // 屏幕尺寸
- const screenWidth = ref<number>(0);
- const screenHeight = ref<number>(0);
- // 将字符串尺寸转换为像素(px),使用 uView 的换算
- function toPx(val?: string | number): number {
- if (val === undefined || val === null) return 0;
- if (typeof val === 'number') return val;
- return (uni as any).$u.getPx(val as string);
- }
- // movable-area 样式
- const areaStyle = computed(() => {
- return {
- width: `${screenWidth.value}px`,
- height: `${screenHeight.value}px`,
- position: 'fixed',
- top: '0',
- left: '0',
- zIndex: '999',
- };
- });
- // 初始化时获取屏幕尺寸
- onMounted(() => {
- const systemInfo = uni.getSystemInfoSync();
- screenWidth.value = systemInfo.windowWidth;
- screenHeight.value = systemInfo.windowHeight;
- // 根据 right/bottom 计算初始位置(默认右下角)
- const widthPx = toPx(props.width);
- const heightPx = toPx(props.height);
- const rightPx = toPx(props.right);
- const bottomPx = toPx(props.bottom);
- x.value = Math.max(0, screenWidth.value - rightPx - widthPx);
- y.value = Math.max(0, screenHeight.value - bottomPx - heightPx);
- inited.value = true;
- });
- // click 事件抛出
- const emit = defineEmits<{
- (e: 'click', payload: { x: number; y: number }): void;
- }>();
- // 点击事件
- const handleClick = () => {
- emit('click', { x: x.value, y: y.value });
- };
- </script>
- <style>
- .suspension-area {
- /* 移除 pointer-events: none,确保可以正常接收拖动事件 */
- pointer-events: none;
- }
- .suspension-view {
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
- background-color: v-bind('props.bgColor');
- border-radius: v-bind('props.borderRadius');
- pointer-events: auto;
- }
- </style>
|