big: transform position
parent
61737e4830
commit
644377d12a
|
@ -53,8 +53,12 @@
|
|||
class="photo-row"
|
||||
:style="{ height: item.size + 'px', width: rowWidth + 'px' }">
|
||||
|
||||
<div class="photo" v-for="photo in item.photos" :key="photo.fileid"
|
||||
:style="{ width: photo.dispWp + '%' }">
|
||||
<div class="photo" v-for="photo of item.photos" :key="photo.fileid"
|
||||
: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"
|
||||
:data="photo"
|
||||
|
@ -67,6 +71,7 @@
|
|||
<Photo v-else
|
||||
:data="photo"
|
||||
:day="item.day"
|
||||
:key="photo.fileid"
|
||||
@select="selectionManager.selectPhoto"
|
||||
@delete="deleteFromViewWithAnimation"
|
||||
@clickImg="clickPhoto" />
|
||||
|
@ -331,7 +336,9 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
row.photos[j] = {
|
||||
flag: this.c.FLAG_PLACEHOLDER,
|
||||
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;
|
||||
|
@ -534,6 +541,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
// Create header for this day
|
||||
const head: IHeadRow = {
|
||||
id: `${day.dayid}-head`,
|
||||
num: -1,
|
||||
size: 40,
|
||||
type: IRowType.HEAD,
|
||||
selected: false,
|
||||
|
@ -642,6 +650,8 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
* @param isAnimating prevents glitches due to height changes
|
||||
*/
|
||||
processDay(dayId: number, data: IPhoto[], isAnimating=false) {
|
||||
if (!data) return;
|
||||
|
||||
const head = this.heads[dayId];
|
||||
const day = head.day;
|
||||
this.loadedDays.add(dayId);
|
||||
|
@ -657,6 +667,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
day.count = data.length;
|
||||
day.detail = data;
|
||||
|
||||
// Reset rows including placeholders
|
||||
for (const row of head.day.rows || []) {
|
||||
row.photos = [];
|
||||
}
|
||||
|
||||
// Create justified layout with correct params
|
||||
const justify = justifiedLayout(day.detail.map(p => {
|
||||
return {
|
||||
|
@ -671,13 +686,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
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
|
||||
let addedRows: IRow[] = [];
|
||||
|
||||
|
@ -735,7 +743,33 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
utils.convertFlags(photo);
|
||||
|
||||
// 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
|
||||
dataIdx++;
|
||||
|
@ -807,6 +841,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
// Create new row
|
||||
const row = {
|
||||
id: `${day.dayid}-${day.rows.length}`,
|
||||
num: day.rows.length,
|
||||
photos: [],
|
||||
type: rowType,
|
||||
size: this.rowHeight,
|
||||
|
@ -831,8 +866,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
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
|
||||
for (const day of updatedDays) {
|
||||
const newDetail = day.detail.filter(p => !delPhotosSet.has(p));
|
||||
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>
|
||||
|
@ -926,12 +926,14 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
}
|
||||
|
||||
.photo-row > .photo {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
cursor: pointer;
|
||||
vertical-align: top;
|
||||
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 {
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
'selected': (data.flag & c.FLAG_SELECTED),
|
||||
'placeholder': (data.flag & c.FLAG_PLACEHOLDER),
|
||||
'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),
|
||||
}">
|
||||
|
||||
|
@ -70,9 +68,6 @@ export default class Photo extends Mixins(GlobalMixin) {
|
|||
return undefined;
|
||||
} else if (this.data.flag & this.c.FLAG_LOAD_FAIL) {
|
||||
return errorsvg;
|
||||
} else if (this.data.flag & this.c.FLAG_FORCE_RELOAD) {
|
||||
this.data.flag &= ~this.c.FLAG_FORCE_RELOAD;
|
||||
return undefined;
|
||||
} else {
|
||||
return this.url;
|
||||
}
|
||||
|
@ -211,19 +206,6 @@ export default class Photo extends Mixins(GlobalMixin) {
|
|||
transform: scale(0.9);
|
||||
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
|
||||
|
|
10
src/types.ts
10
src/types.ts
|
@ -45,6 +45,14 @@ export type IPhoto = {
|
|||
h?: number;
|
||||
/** Grid display width percentage */
|
||||
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 */
|
||||
d?: IDay;
|
||||
/** Video flag from server */
|
||||
|
@ -84,6 +92,8 @@ export interface ITag extends IPhoto {
|
|||
export type IRow = {
|
||||
/** Vue Recycler identifier */
|
||||
id?: string;
|
||||
/** Row ID from head */
|
||||
num: number;
|
||||
/** Day ID */
|
||||
dayId: number;
|
||||
/** Refrence to day object */
|
||||
|
|
Loading…
Reference in New Issue