拖拽+横向拖动拉伸
设置不打开直播(直播盒设置为自适应盒)不可上下拖动拉伸

上下拖动拉伸+对角拖动拉伸

该应用页面固定使用px单位
调用页面 :
属性draggable控制拖拽开启与关闭,属性resizable控制拖动拉伸开启与关闭;
javascript
<DraggableResizable
:draggable="true"
:resizable="true"
:resize-vertical="liveVisible"
:min-width="420"
:min-height="180"
:max-width-right-gap="10"
:max-height-bottom-reserve="160"
:position="position"
:size="{ width: panelSize.width, height: liveHeight }"
:drag-handle-ref="dragHandleRef"
:drag-target-ref="deskRef"
:resize-target-ref="panelRef"
@update:position="onPositionUpdate"
@update:size="onSizeUpdate"
/>
import DraggableResizable from './utility/DraggableResizable.vue'
DraggableResizable.vue
javascript
<template>
<!-- 仅渲染四角四边拖拽手柄,拖拽逻辑通过 ref 绑定到外部元素 -->
<template v-if="resizable">
<div
class="dr-resize-handle corner br"
@pointerdown.prevent.stop="onResizeDown('br', $event)"
/>
<div
class="dr-resize-handle corner bl"
@pointerdown.prevent.stop="onResizeDown('bl', $event)"
/>
<div
class="dr-resize-handle corner tr"
@pointerdown.prevent.stop="onResizeDown('tr', $event)"
/>
<div
class="dr-resize-handle corner tl"
@pointerdown.prevent.stop="onResizeDown('tl', $event)"
/>
<div
class="dr-resize-handle edge right"
@pointerdown.prevent.stop="onResizeDown('r', $event)"
/>
<div
class="dr-resize-handle edge left"
@pointerdown.prevent.stop="onResizeDown('l', $event)"
/>
<div
class="dr-resize-handle edge bottom"
@pointerdown.prevent.stop="onResizeDown('b', $event)"
/>
<div
class="dr-resize-handle edge top"
@pointerdown.prevent.stop="onResizeDown('t', $event)"
/>
</template>
</template>
<script setup lang="ts">
import {
defineEmits,
defineProps,
onBeforeUnmount,
onMounted,
watch,
withDefaults,
} from 'vue'
const props = withDefaults(
defineProps<{
draggable?: boolean
resizable?: boolean
containerSelector?: string
resizeVertical?: boolean
minWidth?: number
minHeight?: number
maxWidthRightGap?: number
maxHeightBottomReserve?: number
position: { x: number; y: number }
size: { width: number; height?: number }
dragHandleRef?: HTMLElement | null
dragTargetRef?: HTMLElement | null
resizeTargetRef?: HTMLElement | null
}>(),
{
draggable: false,
resizable: false,
containerSelector: '.c-map-wrapper',
resizeVertical: true,
minWidth: 360,
minHeight: 160,
maxWidthRightGap: 20,
maxHeightBottomReserve: 160,
dragHandleRef: null,
dragTargetRef: null,
resizeTargetRef: null,
}
)
const emit = defineEmits<{
'update:position': [payload: { x: number; y: number }]
'update:size': [payload: { width?: number; height?: number }]
}>()
let containerRect: DOMRect | null = null
let deskRect: DOMRect | null = null
let panelRect: DOMRect | null = null
let dragging = false
let startX = 0
let startY = 0
let originX = 0
let originY = 0
let resizing = false
let resizeStartX = 0
let resizeStartY = 0
let originWidth = 0
let originHeight = 0
let originDeskX = 0
let originDeskY = 0
let resizeDirection: 't' | 'b' | 'l' | 'r' | 'tl' | 'tr' | 'bl' | 'br' | null = null
function getContainer (): HTMLElement | null {
const ref = props.dragTargetRef
if (!ref) return null
return ref.closest(props.containerSelector) as HTMLElement | null
}
function onPointerDown (event: PointerEvent | MouseEvent) {
if (!props.draggable || !props.dragTargetRef) return
const target = event.target as HTMLElement
if (target.closest('button') || target.closest('a')) return
const container = getContainer()
if (!container) return
containerRect = container.getBoundingClientRect()
deskRect = props.dragTargetRef.getBoundingClientRect()
dragging = true
startX = 'clientX' in event ? event.clientX : 0
startY = 'clientY' in event ? event.clientY : 0
originX = props.position.x
originY = props.position.y
window.addEventListener('pointermove', onPointerMove)
window.addEventListener('pointerup', onPointerUp)
}
function onPointerMove (event: PointerEvent) {
if (!dragging || !containerRect || !deskRect) return
const deltaX = event.clientX - startX
const deltaY = event.clientY - startY
let nextX = originX + deltaX
let nextY = originY + deltaY
const maxX = containerRect.width - deskRect.width
const maxY = containerRect.height - deskRect.height
nextX = Math.min(Math.max(0, nextX), Math.max(0, maxX))
nextY = Math.min(Math.max(0, nextY), Math.max(0, maxY))
emit('update:position', { x: nextX, y: nextY })
}
function onPointerUp () {
dragging = false
window.removeEventListener('pointermove', onPointerMove)
window.removeEventListener('pointerup', onPointerUp)
}
function onResizeDown (
direction: 't' | 'b' | 'l' | 'r' | 'tl' | 'tr' | 'bl' | 'br',
event: PointerEvent | MouseEvent
) {
const container = getContainer()
if (!container || !props.dragTargetRef || !props.resizeTargetRef) return
const wantVertical = direction.includes('t') || direction.includes('b')
if (!props.resizeVertical && wantVertical && (direction === 't' || direction === 'b')) return
containerRect = container.getBoundingClientRect()
deskRect = props.dragTargetRef.getBoundingClientRect()
panelRect = props.resizeTargetRef.getBoundingClientRect()
if (!panelRect || !deskRect) return
resizing = true
resizeDirection = direction
resizeStartX = 'clientX' in event ? event.clientX : 0
resizeStartY = 'clientY' in event ? event.clientY : 0
originWidth = props.size.width
originHeight = props.size.height ?? 0
originDeskX = props.position.x
originDeskY = props.position.y
window.addEventListener('pointermove', onResizeMove)
window.addEventListener('pointerup', onResizeUp)
}
function onResizeMove (event: PointerEvent) {
if (!resizing || !containerRect || !panelRect || !resizeDirection) return
const deltaX = event.clientX - resizeStartX
const deltaY = event.clientY - resizeStartY
let nextWidth = originWidth
let nextHeight = originHeight
let nextX = originDeskX
const canResizeH = resizeDirection.includes('l') || resizeDirection.includes('r')
const canResizeV =
props.resizeVertical &&
(resizeDirection.includes('t') || resizeDirection.includes('b'))
if (canResizeH) {
if (resizeDirection.includes('r')) nextWidth = originWidth + deltaX
if (resizeDirection.includes('l')) {
nextWidth = originWidth - deltaX
nextX = originDeskX + deltaX
}
nextWidth = Math.max(props.minWidth, nextWidth)
const maxWidth = containerRect.width - nextX - props.maxWidthRightGap
nextWidth = Math.min(nextWidth, Math.max(props.minWidth, maxWidth))
if (resizeDirection.includes('l')) {
nextX = Math.max(
0,
Math.min(nextX, originDeskX + originWidth - props.minWidth)
)
}
}
if (canResizeV) {
if (resizeDirection.includes('b')) nextHeight = originHeight + deltaY
if (resizeDirection.includes('t')) nextHeight = originHeight - deltaY
const available = containerRect.height - originDeskY - props.maxHeightBottomReserve
const maxHeight = Math.max(props.minHeight, available)
nextHeight = Math.min(
Math.max(props.minHeight, nextHeight),
maxHeight
)
}
emit('update:position', { x: nextX, y: originDeskY })
emit('update:size', { width: nextWidth, height: nextHeight })
}
function onResizeUp () {
resizing = false
resizeDirection = null
window.removeEventListener('pointermove', onResizeMove)
window.removeEventListener('pointerup', onResizeUp)
}
function bindDrag () {
const el = props.dragHandleRef
if (!el) return
el.addEventListener('pointerdown', onPointerDown)
}
function unbindDrag () {
const el = props.dragHandleRef
if (!el) return
el.removeEventListener('pointerdown', onPointerDown)
}
onMounted(() => {
if (props.draggable && props.dragHandleRef) bindDrag()
})
onBeforeUnmount(() => {
unbindDrag()
onPointerUp()
onResizeUp()
})
watch(
() => [props.draggable, props.dragHandleRef] as const,
([draggable, ref]) => {
unbindDrag()
if (draggable && ref) bindDrag()
}
)
</script>
<style scoped lang="scss">
.dr-resize-handle {
position: absolute;
background: transparent;
}
.dr-resize-handle.corner {
width: 10px;
height: 10px;
}
.dr-resize-handle.edge {
z-index: 1;
}
.dr-resize-handle.corner.br {
right: 0;
bottom: 0;
cursor: se-resize;
}
.dr-resize-handle.corner.bl {
left: 0;
bottom: 0;
cursor: sw-resize;
}
.dr-resize-handle.corner.tr {
right: 0;
top: 0;
cursor: ne-resize;
}
.dr-resize-handle.corner.tl {
left: 0;
top: 0;
cursor: nw-resize;
}
.dr-resize-handle.edge.right {
top: 10px;
bottom: 10px;
right: 0;
width: 6px;
cursor: e-resize;
}
.dr-resize-handle.edge.left {
top: 10px;
bottom: 10px;
left: 0;
width: 6px;
cursor: w-resize;
}
.dr-resize-handle.edge.bottom {
left: 10px;
right: 10px;
bottom: 0;
height: 6px;
cursor: s-resize;
}
.dr-resize-handle.edge.top {
left: 10px;
right: 10px;
top: 0;
height: 6px;
cursor: n-resize;
}
</style>