Motion blur while scrolling.

This commit is contained in:
Leo Vasanko
2025-09-24 13:55:43 -06:00
parent c134d8875c
commit 258d0ba02c

View File

@@ -46,6 +46,26 @@ const viewportHeight = ref(600)
const rowHeight = ref(64) const rowHeight = ref(64)
const rowProbe = ref(null) const rowProbe = ref(null)
let rowProbeObserver = null let rowProbeObserver = null
// Scrolling blur effect
const blurAmount = ref(0) // pixels
let _lastBlurPos = 0
let _blurFrame = null
function _updateMotionBlur() {
const pos = scrollTop.value || 0
if (_lastBlurPos) {
blurAmount.value = 0.5 * Math.abs(pos - _lastBlurPos)
}
_lastBlurPos = pos
_blurFrame = requestAnimationFrame(_updateMotionBlur)
}
const viewportBlurStyle = computed(() => {
return blurAmount.value > 0
? { filter: 'url(#cal-vert-blur)', willChange: 'filter' }
: { filter: 'none' }
})
const baseDate = computed(() => new Date(1970, 0, 4 + calendarStore.config.first_day)) const baseDate = computed(() => new Date(1970, 0, 4 + calendarStore.config.first_day))
const selection = ref({ startDate: null, dayCount: 0 }) const selection = ref({ startDate: null, dayCount: 0 })
const isDragging = ref(false) const isDragging = ref(false)
@@ -387,6 +407,10 @@ onMounted(() => {
onBeforeUnmount(() => { onBeforeUnmount(() => {
clearInterval(timer) clearInterval(timer)
}) })
// Start motion blur loop
_lastBlurPos = scrollTop.value || 0
_blurFrame = requestAnimationFrame(_updateMotionBlur)
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -403,6 +427,7 @@ onBeforeUnmount(() => {
} }
} }
document.removeEventListener('pointerlockchange', handlePointerLockChange) document.removeEventListener('pointerlockchange', handlePointerLockChange)
if (_blurFrame) cancelAnimationFrame(_blurFrame)
}) })
const handleDayMouseDown = (d) => { const handleDayMouseDown = (d) => {
@@ -506,6 +531,15 @@ window.addEventListener('resize', () => {
<template> <template>
<div class="calendar-view-root" :dir="rtl && 'rtl'"> <div class="calendar-view-root" :dir="rtl && 'rtl'">
<div ref="rowProbe" class="row-height-probe" aria-hidden="true"></div> <div ref="rowProbe" class="row-height-probe" aria-hidden="true"></div>
<!-- Inline SVG filter for vertical motion blur -->
<svg width="0" height="0" aria-hidden="true" focusable="false" class="motion-blur-defs">
<defs>
<!-- stdDeviation: x y; keep a tiny epsilon on X so some browsers don't drop the filter entirely -->
<filter id="cal-vert-blur" color-interpolation-filters="sRGB" x="-10%" width="120%" y="-10%" height="120%">
<feGaussianBlur :stdDeviation="`${0.001} ${blurAmount.toFixed(2)}`" edgeMode="duplicate" />
</filter>
</defs>
</svg>
<div class="wrap"> <div class="wrap">
<HeaderControls <HeaderControls
:reference-date="centerVisibleDateStr" :reference-date="centerVisibleDateStr"
@@ -520,7 +554,7 @@ window.addEventListener('resize', () => {
@year-change="handleHeaderYearChange" @year-change="handleHeaderYearChange"
/> />
<div class="calendar-container"> <div class="calendar-container">
<div class="calendar-viewport" ref="viewport"> <div class="calendar-viewport" ref="viewport" :style="viewportBlurStyle">
<div class="calendar-content" :style="{ height: contentHeight + 'px' }"> <div class="calendar-content" :style="{ height: contentHeight + 'px' }">
<div <div
class="weeks-wrapper" class="weeks-wrapper"