jQuery Animation expanding Divs in Circle - javascript

I don't know how to search for this in google or at least here at stackoverflow. Maybe you guys have a word for this animation to search for or something how to call it.
Anyway, my idea is the following. You have a simple div and when you click on it, from the center of the div there should grow 4 other divs which have a little distance to the clicked div and are placed on a half circle. Maybe the image can help your imagination to follow my idea:
Does anyone of you have an idea how to code this?
Edit: Added Code from Comment, while .squadplayerinfo are the spread divs, .playercard the outer div and .field the outer outer div
function calculateDistance(elem, mouseX, mouseY) {
return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left + (elem.width() / 2)), 2) + Math.pow(mouseY - (elem.offset().top + (elem.height() / 2)), 2)));
}
$(document).on("mousemove", ".field", function(e) {
var mX, mY, distance,
$element = $(".playercard");
mX = e.pageX;
mY = e.pageY;
distance = calculateDistance($element, mX, mY);
if(distance > 100) {
$(".squadplayerinfo").each(function(index) {
(index == 1 || index == 2) ? radius = 100 : radius = 75;
angle = toRadians(360 / $(".squadplayerinfo").length * (index + 0.5) / 2);
$(this).animate({
opacity: 0,
right: "+=" + calcX(angle),
top: "+=" + calcY(angle)
}, 750, function() {
// done animating
});
});
}
});

I played with the idea for a few minutes and I think this is what you are looking for:
// javascript
var amount = $(".expand").length;
var radius = 200;
$("#trigger").click(function() {
var x, y, angle;
$(".expand").each(function(index) {
angle = toRadians(360 / amount * index);
x = calcX(angle);
y = calcY(angle);
$(this).animate({
right: "-=" + x,
top: "-=" + y
}, 2000, function() {
// done animating
});
});
});
function calcX(angleRadians) {
return Math.floor(radius * Math.cos(angleRadians));
}
function calcY(angleRadians) {
return Math.floor(radius * Math.sin(angleRadians));
}
function toRadians(angleDegrees) {
return angleDegrees * Math.PI / 180;
}
/* css */
.expand {
position: absolute;
top: 50%;
right: 50%;
}
#trigger {
position: absolute;
top: 200px;
right: 200px;
}
<!-- html -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<button id="trigger">click me</button>
It is dynamically computes the angles based on the amount of divs and the radius can be controlled with ease :)
Hope this helped... Good luck
EDIT
I have noticed you only want your divs to spread on the top half of the screen, so pls change this line:
angle = toRadians(360 / amount * index);
to this one:
angle = toRadians(360 / amount * (index + 0.5) / 2);
EDIT#2
Since HansMartin has edited his post, this is a snippet which will move the div when the mouse will come near the divs (or any other div specified). This snippet is using this answer to check proximity to the div.
// javascript
var amount = $(".expand").length;
var radius = 200;
var first_animation = true;
$('#check-mouse-area').mousemove(function(event) {
if (first_animation) {
if (isNear($(".expand"), 200, event)) {
first_animation = false;
var x, y, angle;
$(".expand").each(function(index) {
angle = toRadians(360 / amount * (index + 0.5) / 2);
x = calcX(angle);
y = calcY(angle);
$(this).animate({
right: "-=" + x,
top: "-=" + y
}, 2000, function() {
// done animating
});
});
}
}
});
function calcX(angleRadians) {
return Math.floor(radius * Math.cos(angleRadians));
}
function calcY(angleRadians) {
return Math.floor(radius * Math.sin(angleRadians));
}
function toRadians(angleDegrees) {
return angleDegrees * Math.PI / 180;
}
function isNear(element, distance, event) {
var left = element.offset().left - distance,
top = element.offset().top - distance,
right = left + element.width() + 2 * distance,
bottom = top + element.height() + 2 * distance,
x = event.pageX,
y = event.pageY;
return (x > left && x < right && y > top && y < bottom);
}
.expand {
position: absolute;
top: 50%;
right: 50%;
}
#check-mouse-area {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
}
<!-- html -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="check-mouse-area"></div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
<div class="expand">a</div>
The main change is from
$("#trigger").click(function () {
to
$('#check-mouse-area').mousemove(function (event) {
...
if (isNear($(".expand"), 200, event)) {
Since the mouse mousemove event is called a lot I added the first animation boolean so that the animation will only fire once.

Related

How can I fix the warping issue in jQuery?

What my blue fish should do: Move around to random positions on the screen and when you touch it with the mouse, it should flee to a random location fast, then continue swimming.
What my blue fish does: After fleeing, it warps to a random location.
How can I fix the warping, so that the fish continues swimming from its current position after fleeing?
/*globals $:false, window:false*/
$("#blueFishId").animate({}, function() {
blueFish(this)
});
function blueFish(IdRef) {
var x = Math.floor(Math.random() * ($(window).width() - $(IdRef).width()))
var y = Math.floor(Math.random() * ($(window).height() - $(IdRef).height()))
$(IdRef).delay(500).animate({
top: y,
left: x
}, 5000, function() {
blueFish(IdRef);
});
}
$("#blueFishId").on("mouseenter", function() {
var x = Math.floor(Math.random() * ($(window).width() - $("#blueFishId").width()))
var y = Math.floor(Math.random() * ($(window).height() - $("#blueFishId").height()))
$("#blueFishId").animate({
top: y,
left: x
}, 1000);
});
img {
position: relative;
}
#blueFishId {
height: auto;
width: auto;
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="blueFish" id="blueFishId" src="images/fish2.png">
To fix this you need to clear the queued animation on mouseover and you also need to stop the current animation that's executing. You can achieve this by calling stop() and also putting the delay() call after the animate(). Also note that you can DRY up the logic by just calling your function from within mouseenter after you stop() the current animation. Try this:
var $fish = $('#blueFishId');
function moveFish($fish) {
var x = Math.floor(Math.random() * ($(window).width() - $fish.width()))
var y = Math.floor(Math.random() * ($(window).height() - $fish.height()))
$fish.animate({
top: y,
left: x
}, 5000, function() {
moveFish($fish);
}).delay(500);
}
$fish.on("mouseenter", function() {
$fish.stop(true);
moveFish($fish);
});
moveFish($fish);
img {
position: relative;
}
#blueFishId {
height: auto;
width: auto;
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="blueFish" id="blueFishId" src="images/fish2.png">
You may also want to consider making the fish move a greater distance on mouseenter to make it look more like it's 'fleeing' the mouse.

Resize div with drag handle when rotated

I could find similar questions involving jQuery UI lib, or only css with no handle to drag, but nothing with pure maths.
What I try to perform is to have a resizable and rotatable div. So far so easy and I could do it.
But it gets more complicate when rotated, the resize handle does calculation in opposite way: it decreases the size instead of increasing when dragging away from shape.
Apart from the calculation, I would like to be able to change the cursor of the resize handle according to the rotation to always make sense.
For that I was thinking to detect which quadrant is the resize handle in and apply a class to change cursor via css.
I don't want to reinvent the wheel, but I want to have a lightweight code and simple UI. So my requirement is jQuery but nothing else. no jQuery UI.
I could develop until achieving this but it's getting too mathematical for me now.. I am quite stuck that's why I need your help to detect when the rotation is enough to have the calculation reversed.
Eventually I am looking for UX improvement if anyone has an idea or better examples to show me!
Here is my code and a Codepen to try: http://codepen.io/anon/pen/rrAWJA
<html>
<head>
<style>
html, body {height: 100%;}
#square {
width: 100px;
height: 100px;
margin: 20% auto;
background: orange;
position: relative;
}
.handle * {
position: absolute;
width: 20px;
height: 20px;
background: turquoise;
border-radius: 20px;
}
.resize {
bottom: -10px;
right: -10px;
cursor: nwse-resize;
}
.rotate {
top: -10px;
right: -10px;
cursor: alias;
}
</style>
<script type="text/javascript" src="js/jquery.js"></script>
<script>
$(document).ready(function()
{
new resizeRotate('#square');
});
var resizeRotate = function(targetElement)
{
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function()
{
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
bottomRight = {x: topLeft.left + self.target.width(), y: topLeft.top + self.target.height()},
delta = {x: e.pageX - bottomRight.x, y: e.pageY - bottomRight.y};
self.target.css({width: '+=' + delta.x, height: '+=' + delta.y});
})
.one('mouseup', function(e)
{
// When releasing handle, round up width and height values :)
self.target.css({width: parseInt(self.target.width()), height: parseInt(self.target.height())});
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
center = {x: topLeft.left + self.target.width() / 2, y: topLeft.top + self.target.height() / 2},
rad = Math.atan2(e.pageX - center.x, e.pageY - center.y),
deg = (rad * (180 / Math.PI) * -1) + 135;
self.currentRotation = deg;
// console.log(rad, deg);
self.target.css({transform: 'rotate(' + (deg)+ 'deg)'});
})
.one('mouseup', function(e)
{
$(document).off('mousemove');
// console.log(self.positions[parseInt(self.currentRotation/90-45)]);
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation/90-45)]);
});
});
};
self.init = function()
{
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
</script>
</head>
<body>
<div id="all">
<div id="square"></div>
</div>
</body>
</html>
Thanks for the help!
Here is a modification of your code that achieves what you want:
$(document).ready(function() {
new resizeRotate('#square');
});
var resizeRotate = function(targetElement) {
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.w = parseInt(self.target.width());
self.h = parseInt(self.target.height());
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function() {
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset();
var centerX = topLeft.left + self.target.width() / 2;
var centerY = topLeft.top + self.target.height() / 2;
var mouseRelativeX = e.pageX - centerX;
var mouseRelativeY = e.pageY - centerY;
//reverse rotation
var rad = self.currentRotation * Math.PI / 180;
var s = Math.sin(rad);
var c = Math.cos(rad);
var mouseLocalX = c * mouseRelativeX + s * mouseRelativeY;
var mouseLocalY = -s * mouseRelativeX + c * mouseRelativeY;
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
self.target.css({
width: self.w,
height: self.h
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset(),
center = {
x: topLeft.left + self.target.width() / 2,
y: topLeft.top + self.target.height() / 2
},
rad = Math.atan2(e.pageX - center.x, center.y - e.pageY) - Math.atan(self.w / self.h),
deg = rad * 180 / Math.PI;
self.currentRotation = deg;
self.target.css({
transform: 'rotate(' + (deg) + 'deg)'
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation / 90 - 45)]);
});
});
};
self.init = function() {
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
The major changes are the following:
In the resize event, the mouse position is transformed to the local coordinate system based on the current rotation. The size is then determined by the position of the mouse in the local system.
The rotate event accounts for the aspect ratio of the box (the - Math.atan(self.w / self.h) part).
If you want to change the cursor based on the current rotation, check the angle of the handle (i.e. self.currentRotation + Math.atan(self.w / self.h) * 180 / Math.PI). E.g. if you have a cursor per quadrant, just check if this value is between 0..90, 90..180 and so on. You may want to check the documentation if and when negative numbers are returned by atan2.
Note: the occasional flickering is caused by the box not being vertically centered.
Nico's answer is mostly correct, but the final result calculation is incorrect -> which is causing the flickering BTW. What is happening is the increase or decrease in size is being doubled by the 2x multiplication. The original half-width should be added to the new half-width to calculate the correct new width and height.
Instead of this:
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
It should be this:
self.w = (self.target.width() / 2) + mouseLocalX;
self.h = (self.target.height() / 2) + mouseLocalY;

mousemove parallax only move slightly instead of mouse position

I'm wanting the plane and rocket to only move approx 5% from their original place when the mouse hits the hero-panel area.
this current code makes both images follow and offset where the mouse position is.
Please assist.
$(document).ready(function () {
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 1);
parallax(e, document.getElementById('rocket'), 2);
});
});
function parallax(e, target, layer) {
var layer_coeff = 10 / layer;
var x = ($(window).width() - target.offsetWidth) / 4 - (e.pageX - ($(window).width() / 4)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 4 - (e.pageY - ($(window).height() / 4)) / layer_coeff;
$(target).offset({ top: y ,left : x });
};
https://jsfiddle.net/jc0807/c5yke2on/
Thanks
Ok, I think I understand what you're looking for:
fiddle
$(document).ready(function () {
var plane = document.getElementById('plane');
var rocket = document.getElementById('rocket');
plane.homePos = { x: plane.offsetLeft, y: plane.offsetTop };
rocket.homePos = { x: rocket.offsetLeft, y: rocket.offsetTop };
$('#hero-panel').mousemove(function (e) {
parallax(e, document.getElementById('plane'), 10);
parallax(e, document.getElementById('rocket'), 20);
});
});
function parallax(e, target, layer) {
var x = target.homePos.x - (e.pageX - target.homePos.x) / layer;
var y = target.homePos.y - (e.pageY - target.homePos.y) / layer;
$(target).offset({ top: y ,left : x });
};
What we're doing here is recording the starting position of the plane and the rocket as a new property 'homePos' on the plane and rocket objects. This makes it easy to apply the parallax effect as an offset from the original positions based on the mouse distance from the object homePos.
If you modify the layer value passed to parallax, the amount of movement will change (we're dividing the mouse offset from the middle of the object's starting position by it, to calculate the new object offset amount).
I guess my question is somehow related to the one above and I didn't want to create a duplicate.
I am using the code bellow to "navigate" into an image on mousemove. The problem is that I cannot manage to make the image fill all the viewable screen area. I've added a red background to the container to show what I mean. The desired result would be to no red background visible.
HTML
<div class="m-scene" id="main">
<div class="scene_element">
<img class="body" src="http://www.planwallpaper.com/static/images/nature-background-images2.jpg" />
</div>
</div>
JS
$(document).ready(function() {
$('img.body').mousemove(function(e) {
parallax(e, this, 1);
});
});
function parallax(e, target, layer) {
var layer_coeff = 20 / layer;
var x = ($(window).width() - target.offsetWidth) / 2 - (e.pageX - ($(window).width() / 2)) / layer_coeff;
var y = ($(window).height() - target.offsetHeight) / 2 - (e.pageY - ($(window).height() / 2)) / layer_coeff;
$(target).offset({
top: y,
left: x
});
};
CSS
.m-scene {
background-color: red;
overflow: hidden;
width: 100%;
}
.scene_element {
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
.scene_element img {
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
}
My jsFiddle is: fiddle
Thank you!

DIVs to randomly fadeIn on page

Okay, I've seen a few things that sort of * answer my question, but none of them quite do what I want to do / I'd like to understand how to do this myself from start to finish as a learning exercise. I'm a novice at all this, so bear with me!
What I'm Trying to Do:
I have a black page and I'd like 20-30 small, white div boxes to fadeIn at random positions on the page (like stars is sort of the vibe I'm going for).
Ideally, they wouldn't overlap and they would be randomly sized between 5px and 10px, but I recognize that this might be getting a little too complicated.
Here's what I have so far
I've been working off this jsfiddle and well as this one. This is what I've come up with (that doesn't work, they all fade in equally spaced in a line and don't stay confined from to the site)
Here's my jsfiddle, code below
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
$(document).ready(function() {
var newPosition = randomPosition();
$('.star').css( {
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px'
}).each(function(index) { $(this).delay(1500*index).fadeIn('slow');
})
});
CSS
body {
background-color: black;
}
.star {
height: 10px;
width: 10px;
background-color: white;
display: none;
}
HTML (is there a way to do this with just a for loop or something similar?)
<div class="star"> </div>
<div class="star"> </div>
<div class="star"> </div>
<div class="star"></div>
The sizing and positioning isn't too hard. The thing is to do it all in the each loop - currently you get 1 position and use it for everything. Also you will want to make them position:absolute so they don't go off the page.
I've updated your fiddle to set the random position and a size between 5 and 10px:
The overlapping is a bit harder. You need to keep track of the sizes and positions you have generated and in the same .each function compare the current generated size+position to the previous ones to check for overlapping.
http://jsfiddle.net/5ocb5aww/3/
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
function randomSize() {
return Math.round(Math.random() * 5) + 5;
}
$(document).ready(function() {
// stores generated star positions
var stars = [];
$('.star').each(function(index) {
var newPosition, newSize;
// check for overlap
var isOverlap = true;
while(isOverlap)
{
newPosition = randomPosition();
newSize = randomSize();
// check previous stars to see if an edge of this one overlaps
isOverlap = $.grep(stars, function(s) {
return (
(newPosition[1] >= s.x1 && newPosition[1] <= s.x2)
|| (newPosition[1]+newSize >= s.x1 && newPosition[1]+newSize <= s.x2)
)
&& (
(newPosition[0] >= s.y1 && newPosition[0] <= s.y2)
|| (newPosition[0]+newSize >= s.y1 && newPosition[0]+newSize <= s.y2)
);
}).length > 0;
}
// store to check later stars against it
stars.push({
x1: newPosition[1],
x2: newPosition[1] + newSize,
y1: newPosition[0],
y2: newPosition[0] + newSize,
size: newSize});
$(this).css({
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px',
'width':newSize + 'px',
'height':newSize + 'px'
});
$(this).delay(800*index).fadeIn('slow');
})
});
Here is my approach to your exercise ... the overlapping position would require a little bit more effort ... I'll leave you that to sort for yourself (may require restructuring the code I'm handing here)
jsFiddle Demo
JS
function starDust(wdt, hgt, tSt, tAp){
var timer = tAp * 1000;
var defInt = tSt,
starInt = setInterval(function(){
var posX = Math.floor((Math.random() * wdt) + 1),
posY = Math.floor((Math.random() * hgt) + 1),
size = Math.floor((Math.random() * 10) + 1);
$('body').append('<div class="star"></div>');
$('.star:last').css({'width':size,'height':size,'left':posX,'top':posY}).hide().fadeIn('slow');
var totalStars = $('.star').length;
if(totalStars == defInt){
clearInterval(starInt);
}
}, timer);
}
$(function(){
// Function arguments: starDust(max X position in px, max Y position in px, total number of stars, time in seconds between stars show);
starDust(600,300,25,1);
});
CSS
body{
background-color:#000;
}
.star{
position: absolute;
background-color:#fff;
min-width:5px;
min-height:5px;
}

Implementing fluid JS tile interface

I'm building a photography website, and I want to create a nice "tiled" interface, which will look similar to the interface on new version MSN Money Now (note - the new version of the website can be viewed only on Windows 8 PCs) - http://t.money.msn.com/now/. I tried to implement this in Javascript.
Here is a sample page with prefilled data: http://photoachiever.azurewebsites.net/en
I created Tile groups - each 2 units high, 2 units wide, which can contain either one big square tile, two wide tiles or four small square tiles. Now, because I want the site to be responsive, I wanted to calculate on the fly in Javascript the optimal unit size, so that always 100 % of the space are filled and for wider screens are for example more columns visible and so on. It works the same way on MSN Money website, but there are two important differences:
1) When my images load the first time, I just see them in their highest resultion up until the point where all images are loaded and the JS is executed. The MSN Money web just displays a green area and the images later appear, already resized appropriately.
2) When I resize the window, it is far from fluid and the caluclations and mainly image resizing are very significantly visible. On MSN Money however the resizing is very smooth and even the images seem to just resize without a glitch. Also - they managed to make the fonts resize fluidly.
Could you please explain me, how the MSN Money website achieved these results?
I have seen a few similare questions here on Stack Overflow, but they never required the equal width and height of individual tiles, which I really need for my design.
Bonus question: Could you please add some explanations of how to achieve responsive animated reflow of divs? Example found on http://www.brainyquote.com/ - when you change Window size, it reflows all quotes in an animated manner.
Edit:
I'm attaching my current code, which is far from correct (preformance is very low and images appear too large first and their size drops a after they all download).
First part of the code (attaches all events to the tiles and adds animation on click):
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 - 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 a sample of how the HTML code looks now:
<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>
If I were you I would use Isotope for the basic layout and add the slide shows and click events along side it. You can insert most any content you like. jQuery Isotope.
Updated Working Model
Full page 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 Isotope
Note this animation is total kludge fine tune before using.
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. It will give you the tiles and animations as basic features.
Fluid Isotope
Images Loaded Plugin
Infinite Scroll
Added animation inside Isotope, Check Out the updated jsFiddles above
#MZetko I understand and respect your desire to implement it on your on.
#apaul34208 pointed in the right direction when he suggested Isotope. That's the term used for this kind of layout grid. You don't have to use jQuery, but it would be useful to look at how it does what it does to get it done... :)
I'm currently implementing a Wordpress site with the Studiofolio template, and even though I think it would have been fun to do it myself, I'm happy I spent those bucks on it. Now I can finish setting it up and go on to the next project. Cheers!
1) Loading images
You should hide images by default. You can do this by setting their dimensions to 1px
(using display:none may cause loading troubles). At the end of your css:
div.tile img { width:1px; height:1px }
This will be overwritten by your tile specific styles on a per element basis after your calculations are done.
To have a background while they are loading you have to use a background color other than white :-)
(e.g. look at your div.wide-tile rule). At the end of your css:
.col .tile { background-color: #2440b2; }
This will have a greater specificity than your white backgrounds so it will override them.
To avoid flicker I would hide all the tiles until their initial position is know (I didn't modify your js).
[working demo] (make sure to use an empty cache)
2) Reisizing with animation
Basically you have to give up using floats for this to work and use absolutely positioned elements instead (floats cannot be animated).
After this you can simply use CSS transitions on the tiles (top, left) so when you recalculate their positions inside a resize event handler they will slide to their new positions with a nice animation.
-webkit-transition: left .5s, top .5s;
-moz-transition: left .5s, top .5s;
transition: left .5s, top .5s;
[working demo]

Categories