diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/TimeLine.vue | 158 |
1 files changed, 72 insertions, 86 deletions
diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue index 6199617..ccf9d9b 100644 --- a/src/components/TimeLine.vue +++ b/src/components/TimeLine.vue @@ -14,23 +14,23 @@ <!-- Main line (unit horizontal line that gets transformed, with extra length to avoid gaps when panning) --> <line :stroke="store.color.alt" stroke-width="2px" x1="-1" y1="0" x2="2" y2="0" :style="mainlineStyles"/> <!-- Tick markers --> - <template v-for="date, idx in ticks.dates" :key="date.toInt()"> - <line v-if="date.equals(MIN_DATE, scale) || date.equals(MAX_DATE, scale)" + <template v-for="tick in ticks" :key="tick.date.toInt()"> + <line v-if="tick.date.equals(MIN_DATE, scale) || tick.date.equals(MAX_DATE, scale)" :x1="vert ? -store.endTickSz / 2 : 0" :y1="vert ? 0 : -store.endTickSz / 2" :x2="vert ? store.endTickSz / 2 : 0" :y2="vert ? 0 : store.endTickSz / 2" :stroke="store.color.alt" :stroke-width="`${store.endTickSz}px`" - :style="tickStyles(idx)" class="animate-fadein"/> + :style="tickStyles(tick)" class="animate-fadein"/> <line v-else :x1="vert ? -store.tickLen / 2 : 0" :y1="vert ? 0 : -store.tickLen / 2" :x2="vert ? store.tickLen / 2 : 0" :y2="vert ? 0 : store.tickLen / 2" :stroke="store.color.alt" stroke-width="1px" - :style="tickStyles(idx)" class="animate-fadein"/> + :style="tickStyles(tick)" class="animate-fadein"/> </template> <!-- Tick labels --> - <text v-for="date, idx in ticks.dates" :key="date.toInt()" + <text v-for="tick in ticks" :key="tick.date.toInt()" x="0" y="0" :text-anchor="vert ? 'start' : 'middle'" dominant-baseline="middle" - :fill="store.color.textDark" :style="tickLabelStyles(idx)" class="text-sm animate-fadein"> - {{date.toDisplayString()}} + :fill="store.color.textDark" :style="tickLabelStyles(tick)" class="text-sm animate-fadein"> + {{tick.date.toDisplayString()}} </text> <!-- Event lines --> <line v-for="id in eventLines.keys()" :key="id" @@ -220,13 +220,16 @@ function initScale(){ // Initialises to smallest usable scale // Tick data const tickLabelMargin = computed(() => props.vert ? 20 : 30); // Distance from label to mainline const tickLabelWidth = computed(() => store.mainlineBreadth - store.largeTickLen / 2 - tickLabelMargin.value); -type Ticks = { - dates: HistDate[], // One for each tick to render - startIdx: number, // Index of first major tick (ignored if 'dates' is empty) - endIdx: number, // Index of last visible tick - minorsPerMajor: number, // Minor ticks per major tick - minorTickStep: number, // Number of minor units between minor ticks -}; +class Tick { + date: HistDate; + major: boolean; // False if tick is on the minor scale + offset: number; // Distance from start of visible timeline, in major units + constructor(date: HistDate, major: boolean, offset: number){ + this.date = date; + this.major = major; + this.offset = offset; + } +} function getNumDisplayUnits({inclOffsets=true} = {}): number { // Get num major units in display range let unitDiff = getUnitDiff(startDate.value, endDate.value, scale.value); if (inclOffsets){ @@ -234,27 +237,21 @@ function getNumDisplayUnits({inclOffsets=true} = {}): number { // Get num major } return unitDiff; } -const ticks = computed((): Ticks => { +const ticks = computed((): Tick[] => { + let ticks: Tick[] = []; if (!mounted.value){ - return {dates: [], startIdx: 0, endIdx: 0, minorsPerMajor: 0, minorTickStep: 0}; + return ticks; } - // Ticks fields - let dates: HistDate[] = []; - let startIdx: number; - let endIdx: number; - let minorsPerMajor: number; - let minorTickStep: number; - // Determine minor-tick fields let numUnits = getNumDisplayUnits(); + // Determine minor-tick params + let minorUnitsPerMajor = getScaleRatio(minorScale.value, scale.value, true); + let minorTickStep = 0;// Number of minor units between minor ticks + let minorTicksPerMajor = 0;// Minor ticks per major tick if (hasMinorScale.value){ - let minorUnitsPerMajor = getScaleRatio(minorScale.value, scale.value, true); let majorUnitSz = availLen.value / getNumDisplayUnits(); let minorUnitSz = majorUnitSz / minorUnitsPerMajor; - minorTickStep = Math.ceil(store.minTickSep / minorUnitSz); - minorsPerMajor = Math.ceil(minorUnitsPerMajor / minorTickStep) - 1; - } else { - minorTickStep = 0; - minorsPerMajor = 0; + minorTickStep = Math.ceil(store.minTickSep / minorUnitSz); // TODO: Account for minorUnitSz being larger? + minorTicksPerMajor = Math.ceil(minorUnitsPerMajor / minorTickStep) - 1; } // Get before-startDate ticks (including start-offset ticks and hidden ticks) let panUnits = Math.floor(getNumDisplayUnits() * store.scrollRatio); // Potential shift distance upon a pan action @@ -264,89 +261,89 @@ const ticks = computed((): Ticks => { break; } date = stepDate(date, scale.value, {forward: false}); - // Add minor dates - let minorDates: HistDate[] = []; + // Add minor ticks + let minorTicks: Tick[] = []; let tempDate = date; - for (let j = 0; j < minorsPerMajor; j++){ + for (let j = 0; j < minorTicksPerMajor; j++){ tempDate = stepDate(tempDate, minorScale.value, {count: minorTickStep}); - minorDates.push(tempDate); + let offset = startOffset.value - (i + 1) + (j + 1) * minorTickStep / minorUnitsPerMajor; + minorTicks.push(new Tick(tempDate, false, offset)); } - minorDates.reverse(); - dates.push(...minorDates); + minorTicks.reverse(); + ticks.push(...minorTicks); // Add major date - dates.push(date); + ticks.push(new Tick(date, true, startOffset.value - (i + 1))); } - dates.reverse(); - startIdx = dates.length; + ticks.reverse(); // Get startDate-to-endDate ticks date = startDate.value.clone(); let numMajorUnits = getNumDisplayUnits({inclOffsets: false}); for (let i = 0; i <= numMajorUnits; i++){ - dates.push(date); + ticks.push(new Tick(date, true, startOffset.value + i)); if (i == numMajorUnits){ break; } - // Add minor dates + // Add minor ticks let nextMajor = stepDate(date, scale.value); - if (i < numMajorUnits){ - for (let j = 0; j < minorsPerMajor; j++){ - date = stepDate(date, minorScale.value, {count: minorTickStep}); - dates.push(date); - } + for (let j = 0; j < minorTicksPerMajor; j++){ + date = stepDate(date, minorScale.value, {count: minorTickStep}); + let offset = startOffset.value + i + (j + 1) * minorTickStep / minorUnitsPerMajor; + ticks.push(new Tick(date, false, offset)); } date = nextMajor; } - endIdx = dates.length - 1; // Get after-endDate ticks (including end-offset ticks and hidden ticks) + let endDateOffset = ticks[ticks.length - 1].offset; for (let i = 0; i < panUnits + Math.ceil(endOffset.value); i++){ if (MAX_DATE.equals(date, scale.value)){ break; } let nextMajor = stepDate(date, scale.value); - // Add minor dates - for (let j = 0; j < minorsPerMajor; j++){ + // Add minor ticks + for (let j = 0; j < minorTicksPerMajor; j++){ date = stepDate(date, minorScale.value, {count: minorTickStep}); - dates.push(date); + let offset = endDateOffset + i + (j + 1) * minorTickStep / minorUnitsPerMajor; + ticks.push(new Tick(date, false, offset)); } // Add major date date = nextMajor; - dates.push(date); + ticks.push(new Tick(date, true, endDateOffset + (i + 1))); } // Get hidden ticks that might transition in after zooming - let datesBefore: HistDate[] = []; - let datesAfter: HistDate[] = []; + let ticksBefore: Tick[] = []; + let ticksAfter: Tick[] = []; if (scaleIdx.value > 0 && availLen.value / (numUnits * store.zoomRatio) < store.minTickSep){ // If zoom-out would decrease scale let zoomUnits = numUnits * (store.zoomRatio - 1); // Potential shift distance upon a zoom-out if (zoomUnits > panUnits){ let zoomedScale = SCALES[scaleIdx.value - 1]; let unitsPerZoomedUnit = getScaleRatio(scale.value, zoomedScale); - date = dates[0]; + date = ticks[0].date; + let offset = ticks[0].offset; // Get preceding ticks for (let i = 0; i < (zoomUnits - panUnits) / unitsPerZoomedUnit; i++){ date = stepDate(date, zoomedScale, {forward: false}); if (date.isEarlier(MIN_DATE, scale.value)){ break; } - datesBefore.push(date); + ticksBefore.push(new Tick(date, true, offset - (i + 1) * unitsPerZoomedUnit)); } - datesBefore.reverse(); + ticksBefore.reverse(); // Get following ticks - date = dates[dates.length - 1]; + date = ticks[ticks.length - 1].date; + offset = ticks[ticks.length - 1].offset; for (let i = 0; i < (zoomUnits - panUnits) / unitsPerZoomedUnit; i++){ date = stepDate(date, zoomedScale); if (MAX_DATE.isEarlier(date, scale.value)){ break; } - datesAfter.push(date); + ticksAfter.push(new Tick(date, true, offset + (i + 1) * unitsPerZoomedUnit)); } } } // - dates = [...datesBefore, ...dates, ...datesAfter]; - startIdx += datesBefore.length; - endIdx += datesBefore.length; - return {dates, startIdx, endIdx, minorsPerMajor, minorTickStep}; + ticks = [...ticksBefore, ...ticks, ...ticksAfter]; + return ticks; }); // For displayed events @@ -993,44 +990,33 @@ const mainlineStyles = computed(() => { transitionTimingFunction: 'ease-out', }; }); -function getTickPxOffset(idx: number): [number, boolean] { // Return major-axis offset, and true if minor tick - // TODO: Replace with something cleaner - let numMajorTicks = Math.ceil((ticks.value.endIdx - ticks.value.startIdx + 1) / (ticks.value.minorsPerMajor + 1)); - let majorTickIdxOffset = moduloPositive((idx - ticks.value.startIdx), (ticks.value.minorsPerMajor + 1)); - let majorTickIdx = (idx - ticks.value.startIdx) - majorTickIdxOffset; - let unitOffset = majorTickIdx / (ticks.value.minorsPerMajor + 1) + startOffset.value; - if (majorTickIdxOffset > 0){ - let minorUnitsPerMajor = getScaleRatio(minorScale.value, scale.value, true); - unitOffset += majorTickIdxOffset * (ticks.value.minorTickStep / minorUnitsPerMajor); - } - let pxOffset = unitOffset / (numMajorTicks - 1 + startOffset.value + endOffset.value) * availLen.value; - return [pxOffset, majorTickIdxOffset > 0]; -} -function tickStyles(idx: number){ - let [offset, isMinor] = getTickPxOffset(idx); - let scaleFactor = isMinor ? 1 : store.largeTickLen / store.tickLen; +function tickStyles(tick: Tick){ + let numMajorUnits = getNumDisplayUnits(); + let pxOffset = tick.offset / numMajorUnits * availLen.value; + let scaleFactor = tick.major ? store.largeTickLen / store.tickLen : 1; return { transform: props.vert ? - `translate(${mainlineOffset.value}px, ${offset}px) scale(${scaleFactor})` : - `translate(${offset}px, ${mainlineOffset.value}px) scale(${scaleFactor})`, + `translate(${mainlineOffset.value}px, ${pxOffset}px) scale(${scaleFactor})` : + `translate(${pxOffset}px, ${mainlineOffset.value}px) scale(${scaleFactor})`, transitionProperty: skipTransition.value ? 'none' : 'transform, opacity', transitionDuration: store.transitionDuration + 'ms', transitionTimingFunction: 'linear', - opacity: (offset >= 0 && offset <= availLen.value) ? 1 : 0, + opacity: (pxOffset >= 0 && pxOffset <= availLen.value) ? 1 : 0, } } -function tickLabelStyles(idx: number){ - let [offset, isMinor] = getTickPxOffset(idx); - let offset2 = isMinor ? 0 : 20; +function tickLabelStyles(tick: Tick){ + let numMajorUnits = getNumDisplayUnits(); + let pxOffset = tick.offset / numMajorUnits * availLen.value; + let pxOffset2 = tick.major ? 20 : 0; let labelSz = props.vert ? store.tickLabelHeight : tickLabelWidth.value; return { transform: props.vert ? - `translate(${mainlineOffset.value + tickLabelMargin.value + offset2}px, ${offset}px)` : - `translate(${offset}px, ${mainlineOffset.value + tickLabelMargin.value + offset2}px)`, + `translate(${mainlineOffset.value + tickLabelMargin.value + pxOffset2}px, ${pxOffset}px)` : + `translate(${pxOffset}px, ${mainlineOffset.value + tickLabelMargin.value + pxOffset2}px)`, transitionProperty: skipTransition.value ? 'none' : 'transform, opacity', transitionDuration: store.transitionDuration + 'ms', transitionTimingFunction: 'linear', - display: (offset >= labelSz && offset <= availLen.value - labelSz) ? 'block' : 'none', + display: (pxOffset >= labelSz && pxOffset <= availLen.value - labelSz) ? 'block' : 'none', } } function eventStyles(eventId: number){ |
