diff --git a/src/components/RichTextEditor.vue b/src/components/RichTextEditor.vue index d4d87e0..a396113 100644 --- a/src/components/RichTextEditor.vue +++ b/src/components/RichTextEditor.vue @@ -606,7 +606,9 @@ const dragState = ref({ currentY: 0, longPressTimer: null, isLongPress: false, - indicator: null + indicator: null, + lastCheckTime: 0, + lastMoveTime: 0 }) // 插入图片 @@ -814,7 +816,7 @@ const handleTouchStart = (e) => { dragState.value.startX = e.touches[0].clientX dragState.value.startY = e.touches[0].clientY - // 设置长按检测定时器(400毫秒) + // 设置长按检测定时器(300毫秒) dragState.value.longPressTimer = setTimeout(() => { dragState.value.isLongPress = true dragState.value.draggedImage = img @@ -823,10 +825,10 @@ const handleTouchStart = (e) => { // 添加拖拽样式 img.classList.add('dragging') - img.style.opacity = '0.85' - img.style.transform = 'scale(0.99)' + img.style.opacity = '0.9' + img.style.transform = 'scale(0.98)' img.style.zIndex = '999' - img.style.transition = 'all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94)' + img.style.transition = 'all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)' // 添加拖拽指示器 const indicator = document.createElement('div') @@ -836,32 +838,33 @@ const handleTouchStart = (e) => { indicator.style.left = '50%' indicator.style.transform = 'translate(-50%, -50%)' indicator.style.padding = '8px 16px' - indicator.style.background = 'rgba(0, 0, 0, 0.75)' + indicator.style.background = 'rgba(0, 0, 0, 0.8)' indicator.style.color = 'white' indicator.style.borderRadius = '16px' - indicator.style.fontSize = '13px' + indicator.style.fontSize = '14px' + indicator.style.fontWeight = '500' indicator.style.zIndex = '1000' indicator.style.opacity = '0' - indicator.style.transition = 'opacity 0.2s ease' + indicator.style.transition = 'opacity 0.15s ease-out' indicator.textContent = '拖拽排序' document.body.appendChild(indicator) // 渐显指示器 setTimeout(() => { indicator.style.opacity = '1' - }, 10) + }, 5) // 保存指示器引用以便后续移除 dragState.value.indicator = indicator // 添加震动反馈(如果设备支持) if (navigator.vibrate) { - navigator.vibrate(15) + navigator.vibrate(10) } // 阻止页面滚动 e.preventDefault() - }, 400) // 400毫秒长按触发拖拽 + }, 300) // 300毫秒长按触发拖拽 } // 处理触摸移动事件 @@ -901,14 +904,20 @@ const handleTouchMove = (e) => { const deltaY = dragState.value.currentY - dragState.value.startY // 更新图片位置,添加缓动效果 - const easeFactor = 0.7 - img.style.transform = `translateY(${deltaY * easeFactor}px) scale(0.99)` + const easeFactor = 0.9 // 调整缓动因子使拖拽更跟手 + img.style.transform = `translateY(${deltaY * easeFactor}px) scale(0.98)` - // 使用requestAnimationFrame优化性能 - requestAnimationFrame(() => { - // 检查与其他图片的碰撞,实现排序 + // 使用节流优化,避免过于频繁的检查 + if (!dragState.value.lastMoveTime) { + dragState.value.lastMoveTime = 0 + } + + const now = Date.now() + // 限制检查频率为每25ms一次,提高响应速度 + if (now - dragState.value.lastMoveTime >= 25) { + dragState.value.lastMoveTime = now checkAndSwapImages(img, deltaY) - }) + } } // 处理触摸结束事件 @@ -928,24 +937,25 @@ const handleTouchEnd = (e) => { const img = dragState.value.draggedImage // 添加释放动画 - img.style.transition = 'all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94)' + img.style.transition = 'all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)' img.style.transform = 'translateY(0) scale(1)' img.style.opacity = '1' // 移除拖拽指示器 if (dragState.value.indicator) { const indicator = dragState.value.indicator + indicator.style.transition = 'opacity 0.15s ease-out' indicator.style.opacity = '0' setTimeout(() => { if (indicator.parentNode) { indicator.parentNode.removeChild(indicator) } - }, 250) + }, 150) } // 添加震动反馈(如果设备支持) if (navigator.vibrate) { - navigator.vibrate(8) + navigator.vibrate(5) } // 延迟重置样式以显示动画 @@ -955,7 +965,7 @@ const handleTouchEnd = (e) => { img.style.zIndex = '' img.style.transition = '' } - }, 250) + }, 200) // 重置状态 dragState.value.isLongPress = false @@ -985,19 +995,20 @@ const handleTouchCancel = (e) => { const img = dragState.value.draggedImage // 添加取消动画 - img.style.transition = 'all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94)' + img.style.transition = 'all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)' img.style.transform = 'translateY(0) scale(1)' img.style.opacity = '1' // 移除拖拽指示器 if (dragState.value.indicator) { const indicator = dragState.value.indicator + indicator.style.transition = 'opacity 0.15s ease-out' indicator.style.opacity = '0' setTimeout(() => { if (indicator.parentNode) { indicator.parentNode.removeChild(indicator) } - }, 250) + }, 150) } // 延迟重置样式以显示动画 @@ -1007,7 +1018,7 @@ const handleTouchCancel = (e) => { img.style.zIndex = '' img.style.transition = '' } - }, 250) + }, 200) // 重置状态 dragState.value.isLongPress = false @@ -1026,7 +1037,7 @@ const checkAndSwapImages = (draggedImg, deltaY) => { // 计算拖拽图片的中心位置 const draggedRect = draggedImg.getBoundingClientRect() - const draggedCenterY = draggedRect.top + draggedRect.height / 2 + deltaY * 0.8 // 添加缓动因子 + const draggedCenterY = draggedRect.top + draggedRect.height / 2 + deltaY * 0.9 // 调整缓动因子以匹配触摸移动 // 查找最近的图片进行交换 for (let i = 0; i < allImages.length; i++) { @@ -1037,23 +1048,11 @@ const checkAndSwapImages = (draggedImg, deltaY) => { const targetCenterY = targetRect.top + targetRect.height / 2 // 检查是否与目标图片重叠,使用更精确的碰撞检测 - const overlapThreshold = (draggedRect.height + targetRect.height) * 0.4 + const overlapThreshold = (draggedRect.height + targetRect.height) * 0.4 // 调整阈值使交换更灵敏 const distance = Math.abs(draggedCenterY - targetCenterY) if (distance < overlapThreshold) { - // 添加接近效果 - targetImg.style.transition = 'all 0.2s ease' - targetImg.style.transform = 'scale(1.02)' - targetImg.style.boxShadow = '0 0 0 2px var(--primary)' - - // 短暂延迟后恢复 - setTimeout(() => { - targetImg.style.transform = '' - targetImg.style.boxShadow = '' - targetImg.style.transition = '' - }, 200) - - // 交换位置 + // 直接交换位置,移除视觉反馈以避免闪烁 swapImages(draggedImg, targetImg) break } @@ -1067,42 +1066,12 @@ const swapImages = (img1, img2) => { // 如果两张图片在同一父元素中 if (parent1 === parent2) { - // 添加交换动画 - img1.style.transition = 'transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)' - img2.style.transition = 'transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)' - - // 获取当前位置 - const rect1 = img1.getBoundingClientRect() - const rect2 = img2.getBoundingClientRect() - - // 计算位移 - const translateY1 = rect2.top - rect1.top - const translateY2 = rect1.top - rect2.top - - // 应用位移 - img1.style.transform = `translateY(${translateY1}px) scale(0.99)` - img2.style.transform = `translateY(${translateY2}px) scale(0.99)` - - // 交换DOM位置 + // 直接交换DOM位置,避免复杂的动画导致的闪烁 const tempMarker = document.createElement('div') parent1.insertBefore(tempMarker, img1) parent1.insertBefore(img1, img2) parent1.insertBefore(img2, tempMarker) tempMarker.remove() - - // 重置变换 - setTimeout(() => { - img1.style.transform = 'scale(0.99)' - img2.style.transform = 'scale(0.99)' - - // 短暂延时后移除过渡效果 - setTimeout(() => { - img1.style.transition = '' - img2.style.transition = '' - img1.style.transform = '' - img2.style.transform = '' - }, 250) - }, 250) } else { // 不同父元素的情况(更复杂,需要特殊处理) // 这里简化处理,实际项目中可能需要更复杂的逻辑 @@ -1680,13 +1649,12 @@ defineExpose({ } :deep(.editor-content .editor-image.dragging) { - opacity: 0.85; - transform: scale(0.99); + opacity: 0.9; + transform: scale(0.98); z-index: 999; - transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: transform 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.15s ease; /* 优化过渡效果 */ box-shadow: 0 12px 25px rgba(0, 0, 0, 0.22); - will-change: transform; - filter: brightness(1.03); + will-change: transform, opacity; /* 提示浏览器优化这些属性 */ user-select: none; -webkit-user-select: none; -moz-user-select: none;