The implementation of the liquid interface JS-tiles

I am creating a website for photography, and I want to create a nice "tiled" interface that will look like the interface in the new version of MSN Money Now (note - the new version of the website can only be viewed on Windows 8) - http://t.money .msn.com / now / . I tried to implement this in Javascript.

Here is an example of a page with pre-populated data: http://photoachiever.azurewebsites.net/en

I created Tile groups - each 2 units high, 2 units wide, which can contain either one large square tile, two wide tiles, or four small square tiles. Now, since I want the site to be responsive, I wanted to calculate the optimal block size on the fly in Javascript so that 100% of the space is always filled, and for wider screens, for example, more columns are visible, etc. It works the same on the MSN Money website, but there are two important differences:

1) When my images are uploaded for the first time, I just see them in their highest result right up to the moment when all images are uploaded and JS is executed. On the MSN Money network, only the green area is displayed, and the images appear later, they are already significantly changed. 2) When I resize the window, it is far from fluidity and very noticeable are the cullations and, mainly, the resizing of the image. However, in MSN Money, resizing is very smooth, and even the images seem to just resize without glitches. In addition, they managed to change the font size.

Could you explain to me how the MSN Money website achieved these results? I saw several similare questions here on Stack Overflow, but they never required the equal width and height of the individual fragments that I really needed for my design.

Bonus question: Could you add some explanations on how to achieve a responsive animated div overflow? An example found at http://www.brainyquote.com/ - when you resize a window, it converts all quotes in an animated way.

Edit: I am tying my current code that is far from correct (the preformation is very low and the images appear too large and their size drops after they are downloaded).

The first part of the code (attaches all events to fragments and adds animation when clicked):

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); });} 

And the main part is resizing:

 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%"); });} 

And here is an example of what the HTML looks like:

 <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> 
+6
source share
3 answers

If I were you, I would use Isotope for the main layout and add a slide show and click events along its side. You can insert any content that you like. jQuery Isotope.

Updated working model

Full result

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(); }); }); 

Proof of Concept - Animations Inside an Isotope

Please note that this animation is a complete fine-tuning of kludge before use.

  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'); } }); 

This is a very cool plugin for layout, sorting and filtering. This will give you tiles and animations as main features.

Fluid isotope

Download images with plugin

Endless scrolling

Added animation inside isotope, open updated jsFiddles above

+7
source

@MZetko I understand and respect your desire to realize it on yours.

@ apaul34208 pointed in the right direction when Isotope suggested. This term is used for this kind of grid. You do not need to use jQuery, but it would be useful to see how it does what it does to do it ... :)

I am currently implementing a Wordpress website with the Studiofolio template, and although I think it would be great to do this, I am happy that I spent this money on this. Now I can finish the setup and move on to the next project. Hooray!

+1
source

1) Download images

By default, hide images . You can do this by setting their sizes to 1px
(using display:none may cause loading problems). At the end of your css:

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

This will be overwritten by your specific styles for each element based on each element after your calculations.

To have a background at boot time, you need to use a background color other than white :-)
(for example, look at the div.wide-tile rule). At the end of your css:

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

This will have more specificity than your white background, so it will override them.

To avoid flickering, I would hide all the tiles until their initial position was known (I did not change your js).

[ working demo ] (be sure to use an empty cache)

2) Reunion with animation

Basically, you should refuse to use float for this to work and use absolutely positioned elements (floating cannot be animated).

After that, you can simply use the CSS transitions on the tiles ( top , left ), so when you recount your positions inside the resize event handler, they will move to their new positions with nice animation.

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

[ working demo ]

+1
source

All Articles