* @type {import('vue').Ref<HTMLElement>}
* @description 用于获取容器的 ref
const containerRef = ref()
function getPositions (ev) {
if (ev instanceof MouseEvent) {
x: ev.touches[0].clientX,
* @param {MouseEvent|TouchEvent} ev 事件对象
function onPressDown (ev) {
if (ev instanceof MouseEvent && ev.button !== 0) {
document.body.classList.add('select-none')
Object.assign(position, getPositions(ev))
document.addEventListener('pointerup', onPressUp, { once: true })
document.addEventListener('pointermove', onPressMove, { passive: true })
* @param {MouseEvent|TouchEvent} ev 事件对象
function onPressMove (ev) {
const { x, y } = getPositions(ev)
const elStyle = containerRef.value.style
const offsetX = x - position.x
const offsetY = y - position.y
const [, currentX = 0, currentY = 0] = elStyle.transform.match(/translate3d\((.+?)px, (.+?)px, 0px\)/) ?? []
elStyle.transform = `translate3d(${~~currentX + offsetX}px, ${~~currentY + offsetY}px, 0)`
Object.assign(position, { x, y })
document.body.classList.remove('select-none')
document.removeEventListener('pointermove', onPressMove)