diff options
| -rw-r--r-- | src/components/TimeLine.vue | 78 | ||||
| -rw-r--r-- | src/lib.ts | 10 |
2 files changed, 73 insertions, 15 deletions
diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue index 8c0bbb3..0c0e6d6 100644 --- a/src/components/TimeLine.vue +++ b/src/components/TimeLine.vue @@ -44,7 +44,7 @@ import IconButton from './IconButton.vue'; import MinusIcon from './icon/MinusIcon.vue'; // Other import {WRITING_MODE_HORZ, MIN_DATE, MAX_DATE, MONTH_SCALE, DAY_SCALE, SCALES, MIN_CAL_DATE, - HistDate, stepDate, inDateScale, getScaleRatio} from '../lib'; + HistDate, stepDate, inDateScale, getScaleRatio, getDaysInMonth, moduloPositive} from '../lib'; import {useStore} from '../store'; // Refs @@ -247,7 +247,8 @@ function panTimeline(scrollRatio: number){ let chgUnits = numUnits * scrollRatio; let newStart = startDate.value.clone(); let newEnd = endDate.value.clone(); - let [numStartSteps, numEndSteps, newStartOffset, newEndOffset] = getMovedBounds(chgUnits, chgUnits); + let [numStartSteps, numEndSteps, newStartOffset, newEndOffset] = + getMovedBounds(startOffset.value, endOffset.value, chgUnits, chgUnits); if (scrollRatio > 0){ while (true){ if (newEnd.equals(MAX_DATE, scale.value)){ @@ -261,7 +262,8 @@ function panTimeline(scrollRatio: number){ chgUnits = INITIAL_EXTRA_OFFSET - endOffset.value; newEndOffset = INITIAL_EXTRA_OFFSET; let extraStartSteps: number; - [extraStartSteps, , newStartOffset, ] = getMovedBounds(chgUnits, chgUnits); + [extraStartSteps, , newStartOffset, ] = + getMovedBounds(startOffset.value, endOffset.value, chgUnits, chgUnits); stepDate(newStart, scale.value, {count: extraStartSteps, inplace: true}); } else { if (numStartSteps > 0){ @@ -299,7 +301,8 @@ function panTimeline(scrollRatio: number){ chgUnits = -INITIAL_EXTRA_OFFSET + startOffset.value; newStartOffset = INITIAL_EXTRA_OFFSET; let extraEndSteps: number; - [, extraEndSteps, , newEndOffset] = getMovedBounds(chgUnits, chgUnits); + [, extraEndSteps, , newEndOffset] = + getMovedBounds(startOffset.value, endOffset.value, chgUnits, chgUnits); stepDate(newEnd, scale.value, {count: extraEndSteps, inplace: true}); } else { if (numEndSteps < 0){ @@ -366,7 +369,8 @@ function zoomTimeline(zoomRatio: number){ // Get new bounds let newStart = startDate.value.clone(); let newEnd = endDate.value.clone(); - let [numStartSteps, numEndSteps, newStartOffset, newEndOffset] = getMovedBounds(startChg, endChg); + let [numStartSteps, numEndSteps, newStartOffset, newEndOffset] = + getMovedBounds(startOffset.value, endOffset.value, startChg, endChg); if (zoomRatio <= 1){ // Zooming in stepDate(newStart, scale.value, {count: numStartSteps, inplace: true}); stepDate(newEnd, scale.value, {count: numEndSteps, inplace: true}); @@ -404,10 +408,55 @@ function zoomTimeline(zoomRatio: number){ console.log('Reached zoom out limit'); return; } else { + // Scale starting/ending offsets let newScale = SCALES[scaleIdx.value - 1]; let oldUnitsPerNew = getScaleRatio(scale.value, newScale); newStartOffset /= oldUnitsPerNew; newEndOffset /= oldUnitsPerNew; + // Shift starting and ending points to align with new scale + let newStartSubUnits = + (scale.value == DAY_SCALE) ? getDaysInMonth(newStart.year, newStart.month) : + (scale.value == MONTH_SCALE) ? 12 : + getScaleRatio(scale.value, newScale); + let newStartIdx = + (scale.value == DAY_SCALE) ? newStart.day - 1 : + (scale.value == MONTH_SCALE) ? newStart.month - 1 : + moduloPositive(newStart.year, newScale) / scale.value; + let startChg = newStartIdx / newStartSubUnits; + if (newStartOffset >= startChg){ + newStartOffset -= startChg; + } else { + startChg = 1 - startChg; + newStartOffset += startChg; + stepDate(newStart, newScale, {inplace: true}); + } + let newEndSubUnits = + (scale.value == DAY_SCALE) ? getDaysInMonth(newEnd.year, newEnd.month) : + (scale.value == MONTH_SCALE) ? 12 : + getScaleRatio(scale.value, newScale); + let newEndIdx = + (scale.value == DAY_SCALE) ? newEnd.day - 1 : + (scale.value == MONTH_SCALE) ? newEnd.month - 1 : + moduloPositive(newEnd.year, newScale) / scale.value; + let endChg = newEndIdx / newEndSubUnits; + if (newEndOffset + endChg < 1){ + newEndOffset += endChg; + } else { + endChg = 1 - endChg; + newEndOffset -= endChg; + stepDate(newEnd, newScale, {inplace: true}); + } + if (scale.value == DAY_SCALE){ + newStart.day = 1; + newEnd.day = 1; + } else if (scale.value == MONTH_SCALE){ + newStart.month = 1; + newEnd.month = 1; + } else { + newStart.year = Math.floor(newStart.year / newScale) * newScale; + newEnd.year = Math.floor(newEnd.year / newScale) * newScale; + } + // scaleIdx.value -= 1; } } else { // Possibly zoom in @@ -443,25 +492,26 @@ function zoomTimeline(zoomRatio: number){ startOffset.value = newStartOffset; endOffset.value = newEndOffset; } -function getMovedBounds(startChg: number, endChg: number): [number, number, number, number] { +function getMovedBounds( + startOffset: number, endOffset: number, startChg: number, endChg: number): [number, number, number, number] { // Returns a number of start and end steps to take, and new start and end offset values let numStartSteps: number; let numEndSteps: number; let newStartOffset: number; let newEndOffset: number; if (startChg >= 0){ - numStartSteps = Math.ceil(startChg - startOffset.value); - newStartOffset = (startOffset.value - startChg) - Math.floor(startOffset.value - startChg); + numStartSteps = Math.ceil(startChg - startOffset); + newStartOffset = (startOffset - startChg) - Math.floor(startOffset - startChg); } else { - numStartSteps = Math.ceil(startChg - startOffset.value); - newStartOffset = Math.abs((startChg - startOffset.value) % 1); + numStartSteps = Math.ceil(startChg - startOffset); + newStartOffset = Math.abs((startChg - startOffset) % 1); } if (endChg >= 0){ - numEndSteps = Math.floor(endChg + endOffset.value); - newEndOffset = (endOffset.value + endChg) % 1; + numEndSteps = Math.floor(endChg + endOffset); + newEndOffset = (endOffset + endChg) % 1; } else { - numEndSteps = Math.floor(endChg + endOffset.value); - newEndOffset = (endOffset.value + endChg) - Math.floor(endOffset.value + endChg); + numEndSteps = Math.floor(endChg + endOffset); + newEndOffset = (endOffset + endChg) - Math.floor(endOffset + endChg); } return [numStartSteps, numEndSteps, newStartOffset, newEndOffset]; } @@ -7,7 +7,12 @@ export const WRITING_MODE_HORZ = window.getComputedStyle(document.body)['writing-mode' as any].startsWith('horizontal'); // Used with ResizeObserver callbacks, to determine which resized dimensions are width and height -// For calendar conversion. Same as in backend/hist_data/cal.py +// Similar to %, but for negative LHS, return a positive offset from a lower RHS multiple +export function moduloPositive(x: number, y: number){ + return x - Math.floor(x / y) * y; +} + +// For calendar conversion. Mostly copied from backend/hist_data/cal.py export function gregorianToJdn(year: number, month: number, day: number): number { if (year < 0){ year += 1; @@ -61,6 +66,9 @@ export function julianToGregorian(year: number, month: number, day: number): [nu export function gregorianToJulian(year: number, month: number, day: number): [number, number, number] { return jdnToJulian(gregorianToJdn(year, month, day)); } +export function getDaysInMonth(year: number, month: number){ + return gregorianToJdn(year, month + 1, 1) - gregorianToJdn(year, month, 1); +} // For date representation export class HistDate { |
