Major new version #2
@ -115,20 +115,25 @@ const contentHeight = computed(() => {
|
||||
|
||||
const visibleWeeks = ref([])
|
||||
let lastScrollRange = { startVW: null, endVW: null }
|
||||
let pendingRebuild = false
|
||||
|
||||
let rebuildTimer = null
|
||||
let lastReason = null
|
||||
const REBUILD_DEBOUNCE_MS = 40
|
||||
|
||||
function scheduleRebuild(reason) {
|
||||
if (pendingRebuild) return
|
||||
pendingRebuild = true
|
||||
const cb = () => {
|
||||
pendingRebuild = false
|
||||
rebuildVisibleWeeks(reason)
|
||||
}
|
||||
lastReason = lastReason ? lastReason + ',' + reason : reason
|
||||
if (rebuildTimer) return
|
||||
rebuildTimer = setTimeout(() => {
|
||||
const r = lastReason
|
||||
rebuildTimer = null
|
||||
lastReason = null
|
||||
const fn = () => rebuildVisibleWeeks(r)
|
||||
if ('requestIdleCallback' in window) {
|
||||
requestIdleCallback(cb, { timeout: 120 })
|
||||
requestIdleCallback(fn, { timeout: 120 })
|
||||
} else {
|
||||
requestAnimationFrame(cb)
|
||||
requestAnimationFrame(fn)
|
||||
}
|
||||
}, REBUILD_DEBOUNCE_MS)
|
||||
}
|
||||
|
||||
const scrollManager = createScrollManager({ viewport, scheduleRebuild })
|
||||
@ -152,7 +157,7 @@ const monthScrollManager = createMonthScrollManager({
|
||||
setScrollTop,
|
||||
})
|
||||
|
||||
const { handleMonthScrollMouseDown, handleMonthScrollTouchStart, handleMonthScrollWheel } =
|
||||
const { handleMonthScrollPointerDown, handleMonthScrollTouchStart, handleMonthScrollWheel } =
|
||||
monthScrollManager
|
||||
|
||||
const initialScrollTop = computed(() => {
|
||||
@ -178,7 +183,7 @@ function rebuildVisibleWeeks(reason) {
|
||||
)
|
||||
const startVW = Math.max(minVirtualWeek.value, startIdx + minVirtualWeek.value)
|
||||
const endVW = Math.min(maxVirtualWeek.value, endIdx + minVirtualWeek.value)
|
||||
console.log('[CalendarView] rebuildVisibleWeeks', {
|
||||
console.debug('[CalendarView] rebuildVisibleWeeks', {
|
||||
reason,
|
||||
currentScrollTop,
|
||||
startIdx,
|
||||
@ -229,13 +234,6 @@ function measureFromProbe() {
|
||||
const topVirtualWeek = Math.floor(scrollTop.value / oldH) + minVirtualWeek.value
|
||||
rowHeight.value = newH
|
||||
const newScrollTop = (topVirtualWeek - minVirtualWeek.value) * newH
|
||||
console.log('[CalendarView] measureFromProbe rowHeight change', {
|
||||
oldH,
|
||||
newH,
|
||||
topVirtualWeek,
|
||||
oldScrollTop: scrollTop.value,
|
||||
newScrollTop,
|
||||
})
|
||||
setScrollTop(newScrollTop, 'row-height-change')
|
||||
}
|
||||
}
|
||||
@ -712,8 +710,8 @@ window.addEventListener('resize', () => {
|
||||
height: `calc(var(--row-h) * ${monthWeek.monthLabel?.weeksSpan || 1})`,
|
||||
top: (monthWeek.top || 0) + 'px',
|
||||
}"
|
||||
@mousedown="handleMonthScrollMouseDown"
|
||||
@touchstart="handleMonthScrollTouchStart"
|
||||
@pointerdown="handleMonthScrollPointerDown"
|
||||
@touchstart.prevent="handleMonthScrollTouchStart"
|
||||
@wheel="handleMonthScrollWheel"
|
||||
>
|
||||
<span :class="{ bottomup: shouldRotateMonth(monthWeek.monthLabel?.text) }">{{
|
||||
@ -805,6 +803,7 @@ header h1 {
|
||||
overflow: hidden;
|
||||
cursor: ns-resize;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.month-label > span {
|
||||
|
@ -156,79 +156,79 @@ export function createMonthScrollManager({
|
||||
contentHeight,
|
||||
setScrollTop,
|
||||
}) {
|
||||
let isMonthScrolling = false
|
||||
let monthScrollStartY = 0
|
||||
let monthScrollStartScroll = 0
|
||||
let dragging = false
|
||||
let startY = 0
|
||||
let startScroll = 0
|
||||
const SPEED = 10
|
||||
|
||||
const handleMonthScrollMouseDown = (e) => {
|
||||
if (e.button !== 0) return
|
||||
isMonthScrolling = true
|
||||
monthScrollStartY = e.clientY
|
||||
monthScrollStartScroll = viewport.value?.scrollTop || 0
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!isMonthScrolling) return
|
||||
const deltaY = e.clientY - monthScrollStartY
|
||||
const newScrollTop = monthScrollStartScroll - deltaY * 10
|
||||
function applyDrag(clientY, reason) {
|
||||
const deltaY = clientY - startY
|
||||
const newScrollTop = startScroll - deltaY * SPEED
|
||||
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
|
||||
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll))
|
||||
const clamped = Math.max(0, Math.min(newScrollTop, maxScroll))
|
||||
setScrollTop(clamped, reason)
|
||||
}
|
||||
|
||||
setScrollTop(clampedScroll, 'month-scroll-drag')
|
||||
function endDrag() {
|
||||
dragging = false
|
||||
window.removeEventListener('pointermove', onPointerMove, true)
|
||||
window.removeEventListener('pointerup', onPointerUp, true)
|
||||
window.removeEventListener('pointercancel', onPointerUp, true)
|
||||
window.removeEventListener('touchmove', onTouchMove)
|
||||
window.removeEventListener('touchend', onTouchEnd)
|
||||
window.removeEventListener('touchcancel', onTouchEnd)
|
||||
}
|
||||
|
||||
function onPointerMove(e) {
|
||||
if (!dragging) return
|
||||
applyDrag(e.clientY, 'month-scroll-drag')
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isMonthScrolling = false
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
function onPointerUp() {
|
||||
if (dragging) endDrag()
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
function onTouchMove(e) {
|
||||
if (!dragging) return
|
||||
const t = e.touches[0]
|
||||
applyDrag(t.clientY, 'month-scroll-touch')
|
||||
e.preventDefault()
|
||||
}
|
||||
function onTouchEnd() {
|
||||
if (dragging) endDrag()
|
||||
}
|
||||
|
||||
const handleMonthScrollTouchStart = (e) => {
|
||||
function handleMonthScrollPointerDown(e) {
|
||||
if (e.button !== undefined && e.button !== 0) return
|
||||
e.preventDefault()
|
||||
dragging = true
|
||||
startY = e.clientY
|
||||
startScroll = viewport.value?.scrollTop || 0
|
||||
window.addEventListener('pointermove', onPointerMove, true)
|
||||
window.addEventListener('pointerup', onPointerUp, true)
|
||||
window.addEventListener('pointercancel', onPointerUp, true)
|
||||
}
|
||||
|
||||
function handleMonthScrollTouchStart(e) {
|
||||
if (e.touches.length !== 1) return
|
||||
isMonthScrolling = true
|
||||
monthScrollStartY = e.touches[0].clientY
|
||||
monthScrollStartScroll = viewport.value?.scrollTop || 0
|
||||
|
||||
const handleTouchMove = (e) => {
|
||||
if (!isMonthScrolling || e.touches.length !== 1) return
|
||||
const deltaY = e.touches[0].clientY - monthScrollStartY
|
||||
const newScrollTop = monthScrollStartScroll - deltaY * 10
|
||||
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
|
||||
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll))
|
||||
|
||||
setScrollTop(clampedScroll, 'month-scroll-touch')
|
||||
dragging = true
|
||||
const t = e.touches[0]
|
||||
startY = t.clientY
|
||||
startScroll = viewport.value?.scrollTop || 0
|
||||
window.addEventListener('touchmove', onTouchMove, { passive: false })
|
||||
window.addEventListener('touchend', onTouchEnd, { passive: false })
|
||||
window.addEventListener('touchcancel', onTouchEnd, { passive: false })
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
isMonthScrolling = false
|
||||
document.removeEventListener('touchmove', handleTouchMove)
|
||||
document.removeEventListener('touchend', handleTouchEnd)
|
||||
}
|
||||
|
||||
document.addEventListener('touchmove', handleTouchMove, { passive: false })
|
||||
document.addEventListener('touchend', handleTouchEnd)
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const handleMonthScrollWheel = (e) => {
|
||||
function handleMonthScrollWheel(e) {
|
||||
const currentScroll = viewport.value?.scrollTop || 0
|
||||
const newScrollTop = currentScroll + e.deltaY * 10
|
||||
const newScrollTop = currentScroll + e.deltaY * SPEED
|
||||
const maxScroll = Math.max(0, contentHeight.value - viewportHeight.value)
|
||||
const clampedScroll = Math.max(0, Math.min(newScrollTop, maxScroll))
|
||||
|
||||
setScrollTop(clampedScroll, 'month-scroll-wheel')
|
||||
const clamped = Math.max(0, Math.min(newScrollTop, maxScroll))
|
||||
setScrollTop(clamped, 'month-scroll-wheel')
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
return {
|
||||
handleMonthScrollMouseDown,
|
||||
handleMonthScrollTouchStart,
|
||||
handleMonthScrollWheel,
|
||||
}
|
||||
return { handleMonthScrollPointerDown, handleMonthScrollTouchStart, handleMonthScrollWheel }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user