Реализация жидкого интерфейса плитки 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;