123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- import { Teleport, computed, defineComponent, nextTick, onMounted, ref, watch, onActivated, onDeactivated, createVNode as _createVNode, vShow as _vShow, mergeProps as _mergeProps, withDirectives as _withDirectives } from "vue";
- import { pick, addUnit, closest, createNamespace, makeNumberProp, makeStringProp, windowWidth, windowHeight } from "../utils/index.mjs";
- import { useRect, useEventListener } from "@vant/use";
- import { useTouch } from "../composables/use-touch.mjs";
- import Icon from "../icon/index.mjs";
- const floatingBubbleProps = {
- gap: makeNumberProp(24),
- icon: String,
- axis: makeStringProp("y"),
- magnetic: String,
- offset: {
- type: Object,
- default: () => ({
- x: -1,
- y: -1
- })
- },
- teleport: {
- type: [String, Object],
- default: "body"
- }
- };
- const [name, bem] = createNamespace("floating-bubble");
- var stdin_default = defineComponent({
- name,
- inheritAttrs: false,
- props: floatingBubbleProps,
- emits: ["click", "update:offset", "offsetChange"],
- setup(props, {
- slots,
- emit,
- attrs
- }) {
- const rootRef = ref();
- const state = ref({
- x: 0,
- y: 0,
- width: 0,
- height: 0
- });
- const boundary = computed(() => ({
- top: props.gap,
- right: windowWidth.value - state.value.width - props.gap,
- bottom: windowHeight.value - state.value.height - props.gap,
- left: props.gap
- }));
- const dragging = ref(false);
- let initialized = false;
- const rootStyle = computed(() => {
- const style = {};
- const x = addUnit(state.value.x);
- const y = addUnit(state.value.y);
- style.transform = `translate3d(${x}, ${y}, 0)`;
- if (dragging.value || !initialized) {
- style.transition = "none";
- }
- return style;
- });
- const updateState = () => {
- if (!show.value) return;
- const {
- width,
- height
- } = useRect(rootRef.value);
- const {
- offset
- } = props;
- state.value = {
- x: offset.x > -1 ? offset.x : windowWidth.value - width - props.gap,
- y: offset.y > -1 ? offset.y : windowHeight.value - height - props.gap,
- width,
- height
- };
- };
- const touch = useTouch();
- let prevX = 0;
- let prevY = 0;
- const onTouchStart = (e) => {
- touch.start(e);
- dragging.value = true;
- prevX = state.value.x;
- prevY = state.value.y;
- };
- const onTouchMove = (e) => {
- e.preventDefault();
- touch.move(e);
- if (props.axis === "lock") return;
- if (!touch.isTap.value) {
- if (props.axis === "x" || props.axis === "xy") {
- let nextX = prevX + touch.deltaX.value;
- if (nextX < boundary.value.left) nextX = boundary.value.left;
- if (nextX > boundary.value.right) nextX = boundary.value.right;
- state.value.x = nextX;
- }
- if (props.axis === "y" || props.axis === "xy") {
- let nextY = prevY + touch.deltaY.value;
- if (nextY < boundary.value.top) nextY = boundary.value.top;
- if (nextY > boundary.value.bottom) nextY = boundary.value.bottom;
- state.value.y = nextY;
- }
- const offset = pick(state.value, ["x", "y"]);
- emit("update:offset", offset);
- }
- };
- useEventListener("touchmove", onTouchMove, {
- target: rootRef
- });
- const onTouchEnd = () => {
- dragging.value = false;
- nextTick(() => {
- if (props.magnetic === "x") {
- const nextX = closest([boundary.value.left, boundary.value.right], state.value.x);
- state.value.x = nextX;
- }
- if (props.magnetic === "y") {
- const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y);
- state.value.y = nextY;
- }
- if (!touch.isTap.value) {
- const offset = pick(state.value, ["x", "y"]);
- emit("update:offset", offset);
- if (prevX !== offset.x || prevY !== offset.y) {
- emit("offsetChange", offset);
- }
- }
- });
- };
- const onClick = (e) => {
- if (touch.isTap.value) emit("click", e);
- else e.stopPropagation();
- };
- onMounted(() => {
- updateState();
- nextTick(() => {
- initialized = true;
- });
- });
- watch([windowWidth, windowHeight, () => props.gap, () => props.offset], updateState, {
- deep: true
- });
- const show = ref(true);
- onActivated(() => {
- show.value = true;
- });
- onDeactivated(() => {
- if (props.teleport) {
- show.value = false;
- }
- });
- return () => {
- const Content = _withDirectives(_createVNode("div", _mergeProps({
- "class": bem(),
- "ref": rootRef,
- "onTouchstartPassive": onTouchStart,
- "onTouchend": onTouchEnd,
- "onTouchcancel": onTouchEnd,
- "onClickCapture": onClick,
- "style": rootStyle.value
- }, attrs), [slots.default ? slots.default() : _createVNode(Icon, {
- "name": props.icon,
- "class": bem("icon")
- }, null)]), [[_vShow, show.value]]);
- return props.teleport ? _createVNode(Teleport, {
- "to": props.teleport
- }, {
- default: () => [Content]
- }) : Content;
- };
- }
- });
- export {
- stdin_default as default,
- floatingBubbleProps
- };
|