Реализация жидкого интерфейса плитки JS

Я создаю сайт фотографии, и я хочу создать приятный "плиточный" интерфейс, который будет похож на интерфейс на новой версии MSN Money Now (обратите внимание - новую версию сайта можно посмотреть только на Windows 8 PCs) -http://t.money.msn.com/now/. Я попытался реализовать это в Javascript.

вот пример страницы с данными укажи: http://photoachiever.azurewebsites.net/en

Я создал группы плиток-каждые 2 блоки высокие, 2 блока широкие, которые могут содержать или одну большую квадратную плитку, 2 широких плитки или 4 небольших квадратных плитки. Теперь, поскольку я хочу, чтобы сайт был отзывчивым, я хотел вычислить на лету в Javascript оптимальный размер блока, так что всегда 100% пространства заполняются, а для более широких экранов, например, больше столбцов видно и так далее. Он работает одинаково на веб-сайте MSN Money, но есть два важных отличия:

1), Когда мои изображения загрузить первый раз, я просто вижу они в их наивысшем результате до точки, где загружаются все изображения и выполняется JS. MSN Money web просто отображает зеленую область, а позже появляются изображения, уже измененные соответствующим образом. 2) Когда я изменить размер окна, он далек от жидкости и caluclations и, главным образом, изменение размера изображения очень заметно видно. На деньги MSN, однако, изменение размера очень плавное, и даже изображения, похоже, просто изменяют размер без сбоев. Кроме того-им удалось изменить размер шрифтов гидравлически.

не могли бы вы объяснить мне, как сайт MSN Money достиг этих результатов? Я видел несколько подобных вопросов здесь на переполнении стека, но они никогда не требовали равной ширины и высоты отдельных плиток, которые мне действительно нужны для моего дизайна.

бонусный вопрос: не могли бы вы добавить некоторые объяснения того, как добиться отзывчивого анимированного оплавления divs? Пример нашел на http://www.brainyquote.com/ - при изменении размера окна, выглядит все цитаты, в анимированной форме.

изменить: Я прикрепляю свой текущий код, который далек от правильного (преформация очень низкая, и изображения сначала кажутся слишком большими, а их размер падает после загрузки).

первая часть кода (прикрепляет все события к плиткам и добавляет анимацию по щелчку):

function attachTileEvents() {
if ($(".tile-flow").size() >= 1) {
    $(window).resize(function () {
        delay(function () {
            resizeTiles();
        }, 100);
    });
    $(document).on("click", ".tile-flow .load-next-page", manualLoadContentDetection);
    $(window).on("scroll", scrollLoadContentDetection);
    $(document).on("touchend", scrollLoadContentDetection);
}
resizeTiles();
$(".tile .contents").each(function () {
    var tile = $(this).parent()[0]
    var mouse = { x: 0, y: 0, down: false };

    var maxRotation = 16;
    var minScale = 0.95;
    var setRotation = function (scaled) {
        //Rotations as percentages 
        var width = tile.offsetWidth;
        var height = tile.offsetHeight;
        var diag = Math.sqrt((width / 2) * (width / 2) + (height / 2) * (height / 2));
        var dist = Math.sqrt((mouse.x - (width / 2)) * (mouse.x - (width / 2)) + (mouse.y - (height / 2)) * (mouse.y - (height / 2)));
        var fract = 1.0;
        if (dist > 0) {
            fract = dist / diag;
        }
        var yRotation = (mouse.x - (width / 2)) / (width / 2);
        var xRotation = (mouse.y - (height / 2)) / (height / 2);

        if (scaled) {
            tile.style.webkitTransform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)" + " scale(" + (minScale + fract * (1 - minScale)) + ")";
            tile.style.mozTransform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)" + " scale(" + (minScale + fract * (1 - minScale)) + ")";
            tile.style.transform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)" + " scale(" + (minScale + fract * (1 - minScale)) + ")";
        } else {
            tile.style.webkitTransform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)";
            tile.style.mozTransform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)";
            tile.style.transform = "rotateX(" + -xRotation * maxRotation + "deg)" + " rotateY(" + yRotation * maxRotation + "deg)";
        }
    }
    var MouseDown = function (e) { mouse.x = e.offsetX; mouse.y = e.offsetY; mouse.down = true; setRotation(true); }
    var MouseUp = function (e) { if (mouse.down) { mouse.down = false; tile.style.webkitTransform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; tile.style.mozTransform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; tile.style.transform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; } }
    var MouseOut = function (e) { mouse.down = false; tile.style.webkitTransform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; tile.style.mozTransform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; tile.style.transform = "rotateX(0deg)" + " rotateY(0deg) scale(1.0)"; }
    var MouseMove = function (e) { mouse.x = e.offsetX; mouse.y = e.offsetY; if (mouse.down == true) { setRotation(false); } }
    $(tile).on("mousemove", MouseMove);
    $(tile).on("mousedown", MouseDown);
    $(tile).on("mouseup", MouseUp);
    $(tile).on("mouseout", MouseOut);
});}

и основная часть-изменение размера:

var TileSizes = { wideWidth: 0, singleWidth: 0, margin: 0 };
function resizeTiles() {
var rowColumnNumber = 2;
var width = $(window).width();
if (width >= 2500) {
    rowColumnNumber = 7;
}
else if (width >= 2000) {
    rowColumnNumber = 6;
} else if (width >= 1600) {
    rowColumnNumber = 5;
} else if (width >= 1280) {
    rowColumnNumber = 4;
} else if (width >= 768) {
    rowColumnNumber = 3;
} else if (width >= 480) {
    rowColumnNumber = 2;
} else {
    rowColumnNumber = 1;
}
var totalWidth = $(".tile-flow").width() - 17; //compensate for the scrollbar
//calculate the margin size : 5% of the flow width
var margin = Math.round(totalWidth * 0.05 / rowColumnNumber);
var wideSize = Math.floor((totalWidth - margin * (rowColumnNumber - 1)) / rowColumnNumber);
var halfSize = Math.floor((wideSize - margin) / 2);
var quaterSize = Math.floor(halfSize * 2.5 / 3);
var heightSize = Math.floor(halfSize * 2 / 2.0);
var doubleHeightSize = heightSize * 2 + margin;
var detailsSize = quaterSize * 2 + margin;
TileSizes.wideWidth = doubleHeightSize;
TileSizes.singleWidth = heightSize;
TileSizes.margin = margin;
$(".big-square-tile").width(doubleHeightSize);
$(".big-square-tile").height(doubleHeightSize);
$(".wide-tile").width(doubleHeightSize);
$(".small-tile").width(halfSize);
$(".tile-flow .col .small-tile:even").css("margin-right", margin);
$(".small-tile").height(heightSize);
$(".wide-tile").height(heightSize);
$(".col").width(doubleHeightSize);
$(".col").css("margin-right", margin);
$(".col:nth-child(" + rowColumnNumber + "n)").css("margin-right", 0);
//all tiles get bottom margin

var how = 0;
$(".wide-tile .contents footer").each(function () {
    if ((how % 4 == 0) || (how % 4 == 1)) {
        $(this).width(TileSizes.singleWidth - 20);
    } else {
        $(this).height(75);
    }
    if (how % 4 == 0) {
        $(this).css("left", TileSizes.wideWidth);
    } else if (how % 4 == 1) {
        $(this).css("left", -TileSizes.singleWidth);
    }
    else if (how % 4 == 2) {
        $(this).css("top", TileSizes.singleWidth);
    } else {
        $(this).css("top", -95);
    }
    how = how + 1;
});

$(".big-square-tile .contents footer").each(function () {
    $(this).height(75);
    if (how % 2 == 0) {
        $(this).css("top", TileSizes.wideWidth);
    } else {
        $(this).css("top", -95);
    }
    how = how + 1;
});

$(".small-tile .contents footer").each(function () {
    $(this).width(TileSizes.singleWidth - 20);
    $(this).height(TileSizes.singleWidth - 20);
    if (how % 4 == 0) {
        $(this).css("left", TileSizes.singleWidth);
    } else if (how % 4 == 1) {
        $(this).css("left", -TileSizes.singleWidth);
    }
    else if (how % 4 == 2) {
        $(this).css("top", TileSizes.singleWidth);
    } else {
        $(this).css("top", -TileSizes.singleWidth);
    }
    how = how + 1;
});

$(".tile").css("margin-bottom", margin);
//resize images    
var imageList = Array();
$(".big-square-tile img").each(function () {
    imageList.push($(this));
    var img = new Image();
    img.onload = function () {
        var originalHeight = this.height;
        var originalWidth = this.width;
        var index = parseInt(this.id.replace("RESIZINGBIG", ""));
        if (originalHeight > originalWidth) {
            imageList[index].css("height", "auto");
            imageList[index].css("width", "100%");
        } else {
            imageList[index].css("height", "100%");
            imageList[index].css("width", "auto");
        }
    }
    img.id = "RESIZINGBIG" + (imageList.length - 1);
    img.src = $(this).attr('src');
});

$(".small-tile img").each(function () {
    imageList.push($(this));
    var img = new Image();
    img.onload = function () {
        var originalHeight = this.height;
        var originalWidth = this.width;
        var index = parseInt(this.id.replace("RESIZINGSMALL", ""));
        if (originalHeight > originalWidth) {
            imageList[index].css("height", "auto");
            imageList[index].css("width", "100%");
        } else {
            imageList[index].css("height", "100%");
            imageList[index].css("width", "auto");
        }
    }
    img.id = "RESIZINGSMALL" + (imageList.length - 1);
    img.src = $(this).attr('src');
});

$(".wide-tile img").each(function () {
    $(this).css("height", "auto");
    $(this).css("width", "100%");
});}

и вот пример того, как выглядит HTML-код сейчас:

<div class="tile-flow">
    <div class="tile-row">
        <div class="col">
            <div class="tile big-square-tile">
                <div class="contents">
                    <img src="~/Images/Test/5.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
        </div>
        <div class="col">
            <div class="tile small-tile">
                <div class="contents">
                    <img src="~/Images/Test/2.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
            <div class="tile small-tile">
                <div class="contents">
                    <img src="~/Images/Test/3.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
            <div class="tile wide-tile">
                <div class="contents">
                    <img src="~/Images/Test/4.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
        </div>
        <div class="col">
            <div class="tile big-square-tile">
                <div class="contents">
                    <img src="~/Images/Test/6.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>

            </div>
        </div>
        <div class="col">
            <div class="tile wide-tile">
                <div class="contents">
                    <img src="~/Images/Test/1.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
            <div class="tile wide-tile">
                <div class="contents">
                    <img src="~/Images/Test/7.jpg" />
                    <footer>
                        <h1>Test</h1>
                        <span class="author">by Test</span>
                    </footer>
                </div>
            </div>
        </div>
</div>
</div>   

3 ответов


на вашем месте я бы использовал Isotope для базового макета и добавил слайд-шоу и нажмите события вдоль него. Вы можете вставить любой контент, который вам нравится. изотоп jQuery.

Обновлена Рабочая Модель

полный результат страницы

JS

$(function () {

    var $container = $('#container');

    $container.imagesLoaded(function () {
        $container.isotope({
            itemSelector: '.photo'
        });
    });
});


var $container = $('#container');
// initialize Isotope
$container.isotope({
    // options...
    resizable: false, // disable normal resizing
    // set columnWidth to a percentage of container width
    masonry: {
        columnWidth: $container.width() / 5
    }
});

// update columnWidth on window resize
$(window).smartresize(function () {
    $container.isotope({
        // update columnWidth to a percentage of container width
        masonry: {
            columnWidth: $container.width() / 5
        }
    });
});

//click function

    $(function () {
        $('.photo').click(function () {
            $(this).toggleClass('red');
        });
    });

//hover function

    $(function () {
        $('#photo1').hover(function () {
            $('#info1').fadeToggle();
        });
    });

доказательство концепции - анимации внутри изотопа

обратите внимание, что эта анимация-total kludge тонкая настройка перед использованием.

 function animatie() {
     var d = 0;
     for (var i = 0; i < 3; ++i) {
         var b = "#info" + i;
         $(b).css('background', 'silver');
         $(b).hide().delay(d).slideDown(1000).delay(3000).slideUp(1000);
         d += 5000;
     }
 }
 animatie();
 window.setInterval(animatie, 15000);

 $(function () {
     for (var i = 0; i < 3; ++i) {
         var z = '.box' + i;
         var divHeight = $(z).height();
         $(z).css('max-height', divHeight + 'px');
         $(z).css('max-height', divHeight + 'px');
         $(z).css('overflow', 'hidden');
     }
 });
 $(window).resize(function () {
     for (var i = 0; i < 3; ++i) {
         var z = '.box' + i;
         var divHeight = $(z).height();
         $(z).css('max-height', divHeight + 'px');
         $(z).css('overflow', 'hidden');
     }
 });

Это очень классный плагин для раскладки, сортировки и фильтрации. Это даст вам плитки и анимации в качестве основных функций.

Жидкий Изотоп

Изображения Загружены Плагин

Бесконечная Прокрутка

добавлена анимация внутри изотопа, Проверьте обновленные jsFiddles выше


@MZetko я понимаю и уважаю ваше желание реализовать его на вашем on.

@apaul34208 указал в правильном направлении, когда он предложил Isotope. Это термин, используемый для такого рода макет сетки. Вам не нужно использовать jQuery, но было бы полезно посмотреть, как он делает то, что он делает, чтобы это сделать... :)

в настоящее время я реализую сайт Wordpress с Studiofolio шаблон, и хотя я думаю, что было бы интересно сделать это сам я рад, что потратил на это деньги. Теперь я могу закончить настройку и перейти к следующему проекту. Ура!


1) загрузка изображений

вы должны скрыть изображения по умолчанию. Вы можете сделать это, установив их размеры 1px
(используя display:none может вызвать проблемы с загрузкой). В конце вашего css:

div.tile img { width:1px; height:1px }

это будет перезаписано вашими стилями плитки на основе каждого элемента после выполнения ваших вычислений.

чтобы иметь фон во время загрузки, вы должны использовать цвет фона, отличный от белый :-)
(например, посмотрите на свой div.wide-tile правило). В конце вашего css:

.col .tile { background-color: #2440b2; }

это будет иметь большую специфику, чем ваши белые фоны, поэтому он будет переопределять их.

чтобы избежать мерцания, я бы спрятал все плитки, пока их начальное положение не будет известно (я не изменял ваш js).

[рабочая демо] (обязательно используйте пустой кэш)

2) Reisizing с анимация

в основном вы должны отказаться от использования поплавков для этого, чтобы работать и использовать абсолютно позиционированных элементов вместо этого (поплавки не могут быть анимированы).

после этого вы можете просто использовать CSS-переходы на плитку (top, left) поэтому, когда вы пересчитываете свои позиции внутри обработчика событий изменения размера, они будут скользить на свои новые позиции с хорошей анимацией.

-webkit-transition: left .5s, top .5s;
-moz-transition: left .5s, top .5s;
transition: left .5s, top .5s;

[рабочая демо]