Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Какой алгоритм используется для отображения нескольких фото в виде мозаики?


Например, как в ВК при загрузке фотографий. Речь идет о таком отображении:



UPD:
Возможно, для вывода изображения используется алгоритм TreeMap. Но не ясно, как задавать "веса" изображениям.
    


Ответы

Ответ 1



Алгоритм с сохранением порядка следования картинок. Определяем количество строк как sqrt( сумма отношений сторон всех картинок / отношение сторон блока ), где отношение сторон определяется как ширина делённая на высоту. Определяем высоту строки как высоту блока делённую на число строк. Определяем общую длину всех картинок масштабированных к высоте строки. Заполняем строки так чтобы ширина строки была максимально приближена к ширине оставшихся картинок делённой на количество оставшихся строк. Перераспределяем высоты строк. Для этого для каждой строки считаем новую высоту так чтобы ширина строки стала равной ширине блока. разности между новыми и старыми высотам строк суммируем отдельно по переполненным и недозаполненным строкам. В зависимости от того какая разность больше пропорционально уменьшаем изменения высот в одной из этих групп, чтобы эти суммы сравнялись. (Галочка в примере отменяет последнее выравнивание, что позволяет сохранить соотношения сторон картинок, но нарушает высоту блока). Для строк у которых осталось свободное пространство (до ширины блока) масштабируе картинки так, чтобы занять всю ширину блока, после чего обрезаем их по высоте с центрированием по вертикали. В строках, где нет свободного пространства масштабируем картинки так, чтобы их высот была равна высоте строки, и обрезаем их по ширине с центрированием по горизонтали пропорционально их отношению сторон так, чтобы общая ширина строки была равна ширине блока. var iimgs=document.getElementById("iimgs").childNodes; var c=document.getElementById("c"); var images=[]; function packimgs(freeH) { var padding = 5; var divWidth = 510+padding; var divHeight = 350+padding; var w=0; for(var i=0;i 1.5*w/nrows) nrows-= Math.round(images[i].aspect*nrows/w)-1; if(nrows<1) nrows=1; var rows= []; var rowHeight= divHeight/nrows - padding; w= rowHeight*w + padding*images.length; for(var j=0; j0 && Math.abs(rowWidth-w/(nrows-1-j)) < Math.abs(rowWidth-w/(nrows-1-j)+imgWidth*2)) { rowWidth=0; j++; } rows[j].push(images[i]); rows[j].space-= imgWidth; rowWidth+= imgWidth; w-= imgWidth; } // Подгон высот var dhp=0, dhm=0, pn=0; for(j=0; jrowHeight) { dhp+= rows[j].height-rowHeight; pn++; } else dhm+= rowHeight-rows[j].height; } // второй этап подгона высот (пропустить если не нужно точно соблюдать высоту блока) if(!freeH&&dhp!=dhm) for(j=0; jrowHeight && dhp>dhm) rows[j].height= rowHeight + (rows[j].height-rowHeight)*dhm/dhp; else if(rows[j].height0? rowHeight*rowWidth1/rowWidth : rows[j].height; for(i=0;i0) { cell.style.height= rows[j].height+"px"; rows[j][i].style.marginTop= (rows[j].height-rowHeight1)/2+"px"; } else { var cwidth= rows[j][i].aspect*rowWidth1*rowHeight/rowWidth; cell.style.width= cwidth+"px"; rows[j][i].style.marginLeft= (cwidth-rows[j][i].width)/2+"px"; } } } } function chlimit(i) { c.innerHTML=""; packimgs(i.checked); } images.loaded=0; for(i=0;i
Алгоритм с изменением порядка картинок. Определяем количество строк как sqrt( сумма отношений сторон всех картинок / отношение сторон блока ), где отношение сторон определяется как ширина делённая на высоту. Определяем высоту строки как высоту блока делённую на число строк. Сортируем картинки по соотношению сторон, ставя в начало самые широкие. Распределяем картинки по строкам, помещая очередную картинку в строку с наибольши свободным пространством, которое определяется как ширина блока минус сумма ширин картинок смасштабированных так, чтобы их высота была равна высоте строки. Для строк у которых осталось свободное пространство (до ширины блока) масштабируе картинки так, чтобы занять всю ширину блока, после чего обрезаем их по высоте с центрированием по вертикали. В строках, где нет свободного пространства масштабируем картинки так, чтобы их высот была равна высоте строки, и обрезаем их по ширине с центрированием по горизонтали пропорционально их отношению сторон так, чтобы общая ширина строки была равна ширине блока. var iimgs=document.getElementById("iimgs").childNodes; var c=document.getElementById("c"); var images=[]; function packimgs() { var divWidth = 510; var divHeight = 350; var padding = 5; var w=0; for(var i=0;i 1.5*w/nrows) nrows-= Math.round(images[i].aspect*nrows/w)-1; if(nrows<1) nrows=1; var rows= []; var rowHeight= (divHeight-padding*(nrows-1))/nrows; for(var j=0; j1) rows[maxj].space-= padding; } for(j=0; j0? rowHeight*rowWidth1/rowWidth : rowHeight; for(i=0;i0) { cell.style.height= rowHeight+"px"; rows[j][i].style.marginTop= (rowHeight-rowHeight1)/2+"px"; } else { var cwidth= rows[j][i].width*rowWidth1/rowWidth; cell.style.width= cwidth+"px"; rows[j][i].style.marginLeft= (cwidth-rows[j][i].width)/2+"px"; } } } } for(i=0;i


Ответ 2



Я так и не нашел универсального решения, но опишу как это работает сейчас, може быть это кому-то поможет. Сейчас я исходя из кол-ва картиночек делю высоту (350 пикселей) на N-ое кол-во строк. Максимум - три, т.к. картиночек может быть всего 10. Далее, я заполняю строки следующим образом: Считаю для каждой строки суммарное соотношение сторон В строку у которой это значение самое низкое добавляю картинку Если картинки еще есть - пункт 1. Теперь я задаю ширину картинкам (высота, напомню, это просто 350 / кол-во строк). Делаю я это так: Выбираю очередную строку Достаю очередную картинку Ширина картинки = 510 * (соотношение_сторон_картинки / суммарное_соотношение_сторон_картинок_в_сроке) И т.д. Ну и код, который работает по этому алгоритму: 'use strict'; angular.module('imageGrid').controller('imageGridController', function($scope) { var divWidth = 510; var divHeight = 350; var padding = 5; var imagesList = $scope.photos; angular.forEach(imagesList, function(image, index) { image.aspectRatio = image.width / image.height; image.blockWidth = 0; image.blockHeight = 0; image.marginBottom = 0; image.marginRight = 0; }, this); var sumOfWidth = function(set) { var totalWidth = 0; if (set.length > 0) { for (var i = 0; i < set.length; i++) { totalWidth += set[i].width; } } return totalWidth; } var sumOfAspectRatio = function(set) { var totalAspectRatio = 0; if (set.length > 0) { for (var i = 0; i < set.length; i++) { totalAspectRatio += set[i].aspectRatio; } } return totalAspectRatio; } var medianAspectRatio = function(set) { return this.sumOfAspectRatio(set); } var linearPartition = function(set) { var rows = Math.min(3, Math.ceil(set.length / 3)); var rowsArray = []; for (var i = 0; i < rows; i++) { rowsArray.push([]); } for (var l = 0; l < set.length; l++) { var rowsAspectRatio = []; for (var i = 0; i < rowsArray.length; i++) { rowsAspectRatio.push(sumOfAspectRatio(rowsArray[i])); } var minIndex = 0, minValue = Infinity; for (var i = 0; i < rowsArray.length; i++) { if (rowsAspectRatio[i] < minValue) { minValue = rowsAspectRatio[i]; minIndex = i; } } rowsArray[minIndex].push(set[l]); } return rowsArray; } var setByRows = linearPartition(imagesList); var rowsCount = setByRows.length; var virtualHeight = Math.floor(divHeight / rowsCount); for (var i = 0; i < setByRows.length; i++) { var rowAspectRatio = sumOfAspectRatio(setByRows[i]); var imagesCount = setByRows[i].length; var virtualWidth = imagesCount > 1 ? divWidth - (imagesCount - 1) * padding : divWidth; for (var l = 0; l < imagesCount; l++) { var image = setByRows[i][l]; image.blockWidth = Math.floor(virtualWidth / imagesCount); image.blockHeight = virtualHeight; } } var resultList = []; for (var i = 0; i < rowsCount; i++) { var imagesCount = setByRows[i].length; for (var l = 0; l < setByRows[i].length; l++) { var image = setByRows[i][l]; image.marginBottom = rowsCount > 1 && i < rowsCount - 1 ? padding : 0; image.marginRight = imagesCount > 1 && l < imagesCount - 1 ? padding : 0; resultList.push(image); } } $scope.imageList = resultList; }); Обновление 01.09.2016 16:50 По просьбе, поясню, что в моем понимании "не универсальное решение". Мой алгоритм не умеет разбивать картинки максимально эффективно. Вот так отображается три картинки с помощью моего алгоритма: Заметьте, у всех одинаковая ширина. Первая картинка (красная), обрезается очень очень сильно, т.к. у нее самая большая ширина из всех картинок. А вот так эти же картинки отображает ВК: Не трудно заметить, что в этом варианте расположения отображается на много большая часть первой картинки (возможно, и всех остальных). Именно из за этого я и считаю свой алгоритм "не универсальным".

Ответ 3



Посмотрите в сторону вот этих решений, не уверен что это именно то что вам нужно, но, думаю, может помочь в решении вашей проблемы. rowGrid.js Ещё скрипт, но уже с некоторым описанием(ENG)

Ответ 4



Все строки кроме первой имеют равную высоту. Сама высота значения не имеет, важн пропорции, поэтому изначально можно взять любую. Для этой высоты перебрать число строк и количество картинок в них. Оставшиеся картинки отправить в первую строку. Вариант с наиболее равномерным расположением выбрать как результат.

Комментариев нет:

Отправить комментарий