big: transform position

old-stable24
Varun Patil 2022-10-16 15:47:14 -07:00
parent 61737e4830
commit 644377d12a
3 changed files with 62 additions and 68 deletions

View File

@ -53,8 +53,12 @@
class="photo-row" class="photo-row"
:style="{ height: item.size + 'px', width: rowWidth + 'px' }"> :style="{ height: item.size + 'px', width: rowWidth + 'px' }">
<div class="photo" v-for="photo in item.photos" :key="photo.fileid" <div class="photo" v-for="photo of item.photos" :key="photo.fileid"
:style="{ width: photo.dispWp + '%' }"> :style="{
height: photo.dispH ? photo.dispH + 'px' : undefined,
width: photo.dispWp * rowWidth + 'px',
transform: 'translateX(' + photo.dispXp * rowWidth + 'px) translateY(' + photo.dispY + 'px)',
}">
<Folder v-if="photo.flag & c.FLAG_IS_FOLDER" <Folder v-if="photo.flag & c.FLAG_IS_FOLDER"
:data="photo" :data="photo"
@ -67,6 +71,7 @@
<Photo v-else <Photo v-else
:data="photo" :data="photo"
:day="item.day" :day="item.day"
:key="photo.fileid"
@select="selectionManager.selectPhoto" @select="selectionManager.selectPhoto"
@delete="deleteFromViewWithAnimation" @delete="deleteFromViewWithAnimation"
@clickImg="clickPhoto" /> @clickImg="clickPhoto" />
@ -331,7 +336,9 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
row.photos[j] = { row.photos[j] = {
flag: this.c.FLAG_PLACEHOLDER, flag: this.c.FLAG_PLACEHOLDER,
fileid: Math.random(), fileid: Math.random(),
dispWp: utils.round(100 / this.numCols, 2, true), dispWp: utils.round(1 / this.numCols, 4, true),
dispXp: utils.round(j / this.numCols, 4, true),
dispY: 0,
}; };
} }
delete row.pct; delete row.pct;
@ -534,6 +541,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
// Create header for this day // Create header for this day
const head: IHeadRow = { const head: IHeadRow = {
id: `${day.dayid}-head`, id: `${day.dayid}-head`,
num: -1,
size: 40, size: 40,
type: IRowType.HEAD, type: IRowType.HEAD,
selected: false, selected: false,
@ -642,6 +650,8 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
* @param isAnimating prevents glitches due to height changes * @param isAnimating prevents glitches due to height changes
*/ */
processDay(dayId: number, data: IPhoto[], isAnimating=false) { processDay(dayId: number, data: IPhoto[], isAnimating=false) {
if (!data) return;
const head = this.heads[dayId]; const head = this.heads[dayId];
const day = head.day; const day = head.day;
this.loadedDays.add(dayId); this.loadedDays.add(dayId);
@ -657,6 +667,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
day.count = data.length; day.count = data.length;
day.detail = data; day.detail = data;
// Reset rows including placeholders
for (const row of head.day.rows || []) {
row.photos = [];
}
// Create justified layout with correct params // Create justified layout with correct params
const justify = justifiedLayout(day.detail.map(p => { const justify = justifiedLayout(day.detail.map(p => {
return { return {
@ -671,13 +686,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
targetRowHeightTolerance: 0.1, targetRowHeightTolerance: 0.1,
}); });
// Reset rows including placeholders
if (head.day?.rows) {
for (const row of head.day.rows) {
row.photos = [];
}
}
// Check if some rows were added // Check if some rows were added
let addedRows: IRow[] = []; let addedRows: IRow[] = [];
@ -735,7 +743,33 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
utils.convertFlags(photo); utils.convertFlags(photo);
// Get aspect ratio // Get aspect ratio
photo.dispWp = utils.round(100 * jbox.width / this.rowWidth, 2, true); const setPos = () => {
photo.dispWp = utils.round(jbox.width / this.rowWidth, 4, true);
photo.dispXp = utils.round(jbox.left / this.rowWidth, 4, true);
photo.dispY = 0;
photo.dispH = 0;
photo.dispRowNum = row.num;
};
if (photo.dispWp !== undefined) { // photo already displayed: animate
window.setTimeout(setPos, 50);
if (photo.dispRowNum !== undefined &&
photo.dispRowNum !== row.num &&
photo.dispRowNum >= 0 &&
photo.dispRowNum < day.rows.length
) { // Row change animation
const start = Math.min(photo.dispRowNum, row.num);
const end = Math.max(photo.dispRowNum, row.num);
const sizeDelta = day.rows.slice(start, end).reduce((acc, r) => {
acc += r.size;
return acc;
}, 0);
photo.dispY = sizeDelta * (photo.dispRowNum < row.num ? -1 : 1);
photo.dispH = day.rows[photo.dispRowNum].size;
}
} else {
setPos();
}
// Move to next index of photo // Move to next index of photo
dataIdx++; dataIdx++;
@ -807,6 +841,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
// Create new row // Create new row
const row = { const row = {
id: `${day.dayid}-${day.rows.length}`, id: `${day.dayid}-${day.rows.length}`,
num: day.rows.length,
photos: [], photos: [],
type: rowType, type: rowType,
size: this.rowHeight, size: this.rowHeight,
@ -831,8 +866,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
/** /**
* Delete elements from main view with some animation * Delete elements from main view with some animation
* This function looks horribly slow, probably isn't that bad
* in all practical scenarios.
* *
* This is also going to update day.detail for you and make * This is also going to update day.detail for you and make
* a call to processDay so just pass it the list of ids to * a call to processDay so just pass it the list of ids to
@ -860,44 +893,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
// clear selection at this point // clear selection at this point
this.selectionManager.clearSelection(delPhotos); this.selectionManager.clearSelection(delPhotos);
// Speculate day reflow for animation
const exitedLeft = new Set<IPhoto>();
for (const day of updatedDays) {
let nextExit = false;
for (const row of day.rows) {
for (const photo of row.photos) {
if (photo.flag & this.c.FLAG_LEAVING) {
nextExit = true;
} else if (nextExit) {
photo.flag |= this.c.FLAG_EXIT_LEFT;
exitedLeft.add(photo);
}
}
}
}
// wait for 200ms
await new Promise(resolve => setTimeout(resolve, 200));
// Reflow all touched days // Reflow all touched days
for (const day of updatedDays) { for (const day of updatedDays) {
const newDetail = day.detail.filter(p => !delPhotosSet.has(p)); const newDetail = day.detail.filter(p => !delPhotosSet.has(p));
this.processDay(day.dayid, newDetail, true); this.processDay(day.dayid, newDetail, true);
} }
// Enter from right all photos that exited left
exitedLeft.forEach((photo: any) => {
photo.flag &= ~this.c.FLAG_EXIT_LEFT;
photo.flag |= this.c.FLAG_ENTER_RIGHT;
});
// wait for 200ms
await new Promise(resolve => setTimeout(resolve, 200));
// Clear enter right flags
exitedLeft.forEach((photo: any) => {
photo.flag &= ~this.c.FLAG_ENTER_RIGHT;
});
} }
} }
</script> </script>
@ -926,12 +926,14 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
} }
.photo-row > .photo { .photo-row > .photo {
display: inline-block; display: block;
position: relative; position: absolute;
top: 0; left: 0;
cursor: pointer; cursor: pointer;
vertical-align: top;
height: 100%; height: 100%;
transition: width 0.2s ease-in-out; // reflow justification transition: width 0.2s ease-in-out,
height 0.2s ease-in-out,
transform 0.2s ease-in-out; // reflow
} }
.head-row { .head-row {

View File

@ -4,8 +4,6 @@
'selected': (data.flag & c.FLAG_SELECTED), 'selected': (data.flag & c.FLAG_SELECTED),
'placeholder': (data.flag & c.FLAG_PLACEHOLDER), 'placeholder': (data.flag & c.FLAG_PLACEHOLDER),
'leaving': (data.flag & c.FLAG_LEAVING), 'leaving': (data.flag & c.FLAG_LEAVING),
'exit-left': (data.flag & c.FLAG_EXIT_LEFT),
'enter-right': (data.flag & c.FLAG_ENTER_RIGHT),
'error': (data.flag & c.FLAG_LOAD_FAIL), 'error': (data.flag & c.FLAG_LOAD_FAIL),
}"> }">
@ -70,9 +68,6 @@ export default class Photo extends Mixins(GlobalMixin) {
return undefined; return undefined;
} else if (this.data.flag & this.c.FLAG_LOAD_FAIL) { } else if (this.data.flag & this.c.FLAG_LOAD_FAIL) {
return errorsvg; return errorsvg;
} else if (this.data.flag & this.c.FLAG_FORCE_RELOAD) {
this.data.flag &= ~this.c.FLAG_FORCE_RELOAD;
return undefined;
} else { } else {
return this.url; return this.url;
} }
@ -211,19 +206,6 @@ export default class Photo extends Mixins(GlobalMixin) {
transform: scale(0.9); transform: scale(0.9);
opacity: 0; opacity: 0;
} }
&.exit-left {
transition: all 0.2s ease-in;
transform: translateX(-20%);
opacity: 0.8;
}
&.enter-right {
animation: enter-right 0.2s ease-out forwards;
}
}
@keyframes enter-right {
from { transform: translateX(20%); opacity: 0.8; }
to { transform: translateX(0); opacity: 1; }
} }
// Distance of icon from border // Distance of icon from border

View File

@ -45,6 +45,14 @@ export type IPhoto = {
h?: number; h?: number;
/** Grid display width percentage */ /** Grid display width percentage */
dispWp?: number; dispWp?: number;
/** Grid display height (forced) */
dispH?: number;
/** Grid display X percentage */
dispXp?: number;
/** Grid display Y px */
dispY?: number;
/** Grid display row id (relative to head) */
dispRowNum?: number;
/** Reference to day object */ /** Reference to day object */
d?: IDay; d?: IDay;
/** Video flag from server */ /** Video flag from server */
@ -84,6 +92,8 @@ export interface ITag extends IPhoto {
export type IRow = { export type IRow = {
/** Vue Recycler identifier */ /** Vue Recycler identifier */
id?: string; id?: string;
/** Row ID from head */
num: number;
/** Day ID */ /** Day ID */
dayId: number; dayId: number;
/** Refrence to day object */ /** Refrence to day object */