Skip to content

Commit

Permalink
feat(fab): support draggable
Browse files Browse the repository at this point in the history
  • Loading branch information
novlan1 committed Aug 8, 2024
1 parent 4dcd95d commit bafd01c
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/fab/fab.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ buttonProps | Object | - | Typescript:`ButtonProps`,[Button API Documents](.
icon | Slot / Function | - | Typescript:`TNode`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
style | String | right: 16px; bottom: 32px; | \- | N
text | String | - | \- | N
draggable | Boolean | false | set draggable | N
onClick | Function | | Typescript:`(context: {e: MouseEvent}) => void`<br/> | N

### Fab Events
Expand Down
1 change: 1 addition & 0 deletions src/fab/fab.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ buttonProps | Object | - | 透传至 Button 组件。TS 类型:`ButtonProps`
icon | Slot / Function | - | 图标。TS 类型:`TNode`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
style | String | right: 16px; bottom: 32px; | 悬浮按钮的样式,常用于调整位置 | N
text | String | - | 文本内容 | N
draggable | Boolean | false | 是否可拖动 | N
onClick | Function | | TS 类型:`(context: {e: MouseEvent}) => void`<br/>悬浮按钮点击事件 | N

### Fab Events
Expand Down
110 changes: 108 additions & 2 deletions src/fab/fab.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent } from 'vue';
import { defineComponent, ref, computed, onMounted, watch } from 'vue';
import config from '../config';
import FabProps from './props';
import { useTNodeJSX } from '../hooks/tnode';
Expand All @@ -15,15 +15,121 @@ export default defineComponent({
const renderTNodeJSX = useTNodeJSX();

const fabClass = usePrefixClass('fab');
const fabRef = ref();

const handleClick = (e: MouseEvent) => {
props.onClick?.({ e });
};

const mounted = ref(false);
const btnSwitchPos = ref({
x: 16,
y: 32,
});
const switchPos = ref({
hasMoved: false, // exclude click event
x: btnSwitchPos.value.x, // right
y: btnSwitchPos.value.y, // bottom
startX: 0,
startY: 0,
endX: 0,
endY: 0,
});

const onTouchStart = (e: TouchEvent) => {
switchPos.value.startX = e.touches[0].pageX;
switchPos.value.startY = e.touches[0].pageY;
};

const onTouchMove = (e: TouchEvent) => {
e.stopPropagation();
e.preventDefault();

if (!props.draggable) {
return;
}

if (e.touches.length <= 0) {
return;
}
const offsetX = e.touches[0].pageX - switchPos.value.startX;
const offsetY = e.touches[0].pageY - switchPos.value.startY;
const x = Math.floor(switchPos.value.x - offsetX);
const y = Math.floor(switchPos.value.y - offsetY);
btnSwitchPos.value.x = x;
btnSwitchPos.value.y = y;
switchPos.value.endX = x;
switchPos.value.endY = y;
switchPos.value.hasMoved = true;
};

const onTouchEnd = (e: TouchEvent) => {
if (!switchPos.value.hasMoved) {
return;
}
switchPos.value.startX = 0;
switchPos.value.startY = 0;
switchPos.value.hasMoved = false;
setSwitchPosition(switchPos.value.endX, switchPos.value.endY);
};

const setSwitchPosition = (switchX: number, switchY: number) => {
switchPos.value.x = switchX;
switchPos.value.y = switchY;
btnSwitchPos.value.x = switchX;
btnSwitchPos.value.y = switchY;
};

const fabStyle = computed(() => ({
right: `${btnSwitchPos.value.x}px`,
bottom: `${btnSwitchPos.value.y}px`,
}));

onMounted(() => {
mounted.value = true;
resetDraggableParams();
});

const getFabOriginStyle = () => {
const info = window.getComputedStyle(fabRef.value);
const { right, bottom } = info || {};
const getNumber = (num: string) => num.replace(/[^\d]/g, '');

return {
right: +(getNumber(right) || 0),
bottom: +(getNumber(bottom) || 0),
};
};

const resetDraggableParams = () => {
const { right, bottom } = getFabOriginStyle();

btnSwitchPos.value.x = right;
btnSwitchPos.value.y = bottom;

switchPos.value.x = right;
switchPos.value.y = bottom;
};

watch(
() => props.style,
() => {
resetDraggableParams();
},
);

return () => {
const icon = () => renderTNodeJSX('icon');
return (
<div class={fabClass.value} style={props.style} onClick={handleClick}>
<div
class={fabClass.value}
style={mounted.value && props.draggable ? { ...fabStyle.value } : props.style}
onClick={handleClick}
onTouchstart={onTouchStart}
onTouchmove={onTouchMove}
onTouchend={onTouchEnd}
ref={fabRef}
>
<TButton
size="large"
theme="primary"
Expand Down
5 changes: 5 additions & 0 deletions src/fab/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ export default {
},
/** 悬浮按钮点击事件 */
onClick: Function as PropType<TdFabProps['onClick']>,
/** 是否可拖拽 */
draggable: {
type: Boolean,
default: false,
}
};

0 comments on commit bafd01c

Please sign in to comment.