incomplete: non-uniform mobile layout (#73)
parent
b589393ab1
commit
c0f14de665
|
@ -103,9 +103,9 @@ import GlobalMixin from '../mixins/GlobalMixin';
|
|||
import moment from 'moment';
|
||||
|
||||
import { ViewerManager } from "../services/Viewer";
|
||||
import { getLayout } from "../services/Layout";
|
||||
import * as dav from "../services/DavRequests";
|
||||
import * as utils from "../services/Utils";
|
||||
import justifiedLayout from "justified-layout";
|
||||
import axios from '@nextcloud/axios'
|
||||
import Folder from "./frame/Folder.vue";
|
||||
import Tag from "./frame/Tag.vue";
|
||||
|
@ -715,17 +715,16 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
}
|
||||
|
||||
// Create justified layout with correct params
|
||||
const justify = justifiedLayout(day.detail.map(p => {
|
||||
const justify = getLayout(day.detail.map(p => {
|
||||
return {
|
||||
width: (this.squareMode ? null : p.w) || this.rowHeight,
|
||||
height: (this.squareMode ? null : p.h) || this.rowHeight,
|
||||
width: p.w || this.rowHeight,
|
||||
height: p.h || this.rowHeight,
|
||||
};
|
||||
}), {
|
||||
containerWidth: this.rowWidth,
|
||||
containerPadding: 0,
|
||||
boxSpacing: 0,
|
||||
targetRowHeight: this.rowHeight,
|
||||
targetRowHeightTolerance: 0.1,
|
||||
rowWidth: this.rowWidth,
|
||||
rowHeight: this.rowHeight,
|
||||
squareMode: this.squareMode,
|
||||
numCols: this.numCols,
|
||||
});
|
||||
|
||||
// Check if some rows were added
|
||||
|
@ -742,7 +741,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
const scrollY = this.getScrollY();
|
||||
|
||||
// Previous justified row
|
||||
let prevJustifyTop = justify.boxes[0]?.top || 0;
|
||||
let prevJustifyTop = justify[0]?.top || 0;
|
||||
|
||||
// Add all rows
|
||||
let dataIdx = 0;
|
||||
|
@ -756,7 +755,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
}
|
||||
|
||||
// Go to the next row
|
||||
const jbox = justify.boxes[dataIdx];
|
||||
const jbox = justify[dataIdx];
|
||||
if (jbox.top !== prevJustifyTop) {
|
||||
prevJustifyTop = jbox.top;
|
||||
rowIdx++;
|
||||
|
@ -765,11 +764,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
|
||||
// Set row height
|
||||
const row = this.list[rowIdx];
|
||||
const jH = Math.round(jbox.height);
|
||||
const jH = this.squareMode ? this.rowHeight : Math.round(jbox.height);
|
||||
const delta = jH - row.size;
|
||||
// If the difference is too small, it's not worth risking an adjustment
|
||||
// especially on square layouts on mobile. Also don't do this if animating.
|
||||
if (Math.abs(delta) > 5 && !isAnimating) {
|
||||
if (!isAnimating && Math.abs(delta) > 5) {
|
||||
rowSizeDelta += delta;
|
||||
row.size = jH;
|
||||
}
|
||||
|
@ -788,8 +787,8 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
|||
const setPos = () => {
|
||||
photo.dispWp = utils.round(jbox.width / this.rowWidth, 4, true);
|
||||
photo.dispXp = utils.round(jbox.left / this.rowWidth, 4, true);
|
||||
photo.dispH = this.squareMode ? utils.roundHalf(jbox.height) : 0;
|
||||
photo.dispY = 0;
|
||||
photo.dispH = 0;
|
||||
photo.dispRowNum = row.num;
|
||||
};
|
||||
if (photo.dispWp !== undefined) { // photo already displayed: animate
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
import justifiedLayout from "justified-layout";
|
||||
|
||||
/**
|
||||
* Generate the layout matrix.
|
||||
*
|
||||
* If we are in square mode, do this manually to get non-uniformity.
|
||||
* Otherwise, use flickr/justified-layout (at least for now).
|
||||
*/
|
||||
export function getLayout(
|
||||
input: { width: number, height: number }[],
|
||||
opts: {
|
||||
rowWidth: number,
|
||||
rowHeight: number,
|
||||
squareMode: boolean,
|
||||
numCols: number,
|
||||
}
|
||||
): {
|
||||
top: number,
|
||||
left: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}[] {
|
||||
if (!opts.squareMode) {
|
||||
return justifiedLayout((input), {
|
||||
containerPadding: 0,
|
||||
boxSpacing: 0,
|
||||
containerWidth: opts.rowWidth,
|
||||
targetRowHeight: opts.rowHeight,
|
||||
targetRowHeightTolerance: 0.1,
|
||||
}).boxes;
|
||||
}
|
||||
|
||||
// Binary flags
|
||||
const FLAG_USE = 1;
|
||||
const FLAG_USED = 2;
|
||||
const FLAG_USE4 = 4;
|
||||
|
||||
// Create 2d matrix to work in
|
||||
const origRowLen = Math.ceil(input.length / opts.numCols);
|
||||
const matrix: number[][] = new Array(origRowLen * 3); // todo: dynamic length
|
||||
for (let i = 0; i < matrix.length; i++) {
|
||||
matrix[i] = new Array(opts.numCols).fill(0);
|
||||
}
|
||||
|
||||
// Useful for debugging
|
||||
const printMatrix = () => {
|
||||
let str = '';
|
||||
for (let i = 0; i < matrix.length; i++) {
|
||||
const rstr = matrix[i].map(v => v.toString(2).padStart(4, '0')).join(' ');
|
||||
str += i.toString().padStart(2) + ' | ' + rstr + '\n';
|
||||
}
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
// Fill in the matrix
|
||||
let row = 0;
|
||||
let col = 0;
|
||||
let photoId = 0;
|
||||
while (photoId < input.length) {
|
||||
// Check if we reached the end of row
|
||||
if (col >= opts.numCols) {
|
||||
row++; col = 0;
|
||||
}
|
||||
|
||||
// Check if already used
|
||||
if (matrix[row][col] & FLAG_USED) {
|
||||
col++; continue;
|
||||
}
|
||||
|
||||
// Use this slot
|
||||
matrix[row][col] |= FLAG_USE;
|
||||
photoId++;
|
||||
|
||||
// Check if previous row has something used
|
||||
// or something beside this is used
|
||||
// We don't do these one after another
|
||||
if ((row > 0 && matrix[row-1].some(v => v & FLAG_USED)) ||
|
||||
(col > 0 && matrix[row][col-1] & FLAG_USED) ||
|
||||
(col < opts.numCols-1 && matrix[row][col+1] & FLAG_USED)
|
||||
) {
|
||||
col++; continue;
|
||||
}
|
||||
|
||||
// Check if we can use 4 blocks
|
||||
let canUse4 =
|
||||
// We have enough space
|
||||
(row + 1 < matrix.length && col+1 < opts.numCols) &&
|
||||
// Nothing used in vicinity (redundant check)
|
||||
!(matrix[row+1][col] & FLAG_USED) &&
|
||||
!(matrix[row][col+1] & FLAG_USED) &&
|
||||
!(matrix[row+1][col+1] & FLAG_USED) &&
|
||||
// This cannot end up being a widow (conservative)
|
||||
(input.length-photoId-1 >= ((opts.numCols-col-2) + (opts.numCols-2)));
|
||||
|
||||
// Use four with 60% probability
|
||||
if (canUse4 && Math.random() < 0.6) {
|
||||
matrix[row][col] |= FLAG_USE4;
|
||||
matrix[row+1][col] |= FLAG_USED;
|
||||
matrix[row][col+1] |= FLAG_USED;
|
||||
matrix[row+1][col+1] |= FLAG_USED;
|
||||
}
|
||||
|
||||
// Go ahead
|
||||
col++;
|
||||
}
|
||||
|
||||
// REMOVE BEFORE PUSH
|
||||
if (input.length == 10)
|
||||
printMatrix();
|
||||
|
||||
// Square layout matrix
|
||||
const absMatrix: {
|
||||
top: number,
|
||||
left: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}[] = [];
|
||||
|
||||
let currTop = 0;
|
||||
row = 0; col = 0; photoId = 0;
|
||||
while (photoId < input.length) {
|
||||
// Check if we reached the end of row
|
||||
if (col >= opts.numCols) {
|
||||
row++; col = 0;
|
||||
currTop += opts.rowHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if used
|
||||
if (!(matrix[row][col] & FLAG_USE)) {
|
||||
col++; continue;
|
||||
}
|
||||
|
||||
// Create basic object
|
||||
const sqsize = opts.rowHeight;
|
||||
const p = {
|
||||
top: currTop,
|
||||
left: col * sqsize,
|
||||
width: sqsize,
|
||||
height: sqsize,
|
||||
}
|
||||
|
||||
// Use twice the space
|
||||
if (matrix[row][col] & FLAG_USE4) {
|
||||
p.width *= 2;
|
||||
p.height *= 2;
|
||||
col += 2;
|
||||
} else {
|
||||
col += 1;
|
||||
}
|
||||
|
||||
absMatrix.push(p);
|
||||
photoId++;
|
||||
}
|
||||
|
||||
return absMatrix;
|
||||
}
|
Loading…
Reference in New Issue