Responsive Image Gallery with a content hover effect - javascript

After 3 days of trying to figure something out I have come to you, the good people of stackoverflow.
I have managed to create a quite nice looking responsive, masonry type gallery on my website, that I found on CodePen, here - http://codepen.io/justinklemm/pen/iCelj
Heres how it looks on my site - dangoodeofficial.co.uk/290-2
HTML
<div class="gallery">
<img src="http://31.media.tumblr.com/tumblr_loumsfBCuE1qeo682o1_500.jpg">
<img src="http://24.media.tumblr.com/tumblr_m1yru9PNMV1qeo682o1_500.jpg">
<img src="http://24.media.tumblr.com/tumblr_m24ic6kY6Q1qeo682o1_500.jpg">
<img src="http://31.media.tumblr.com/tumblr_lriz0lDN2y1qeo682o1_500.jpg">
<img src="http://24.media.tumblr.com/tumblr_lrmgjaL62O1qeo682o1_500.jpg">
</div>
CSS
body {
padding: 2px 0 0 2px;
}
.gallery img {
float: left;
padding: 0 2px 2px 0;
}
JAVASCRIPT
function scaleGallery()
{
// This is roughly the max pixels width/height of a square photo
var widthSetting = 400;
// Do not edit any of this unless you know what you're doing
var containerWidth = $(".gallery").width();
var ratioSumMax = containerWidth / widthSetting;
var imgs = $(".gallery img");
var numPhotos = imgs.length, ratioSum, ratio, photo, row, rowPadding, i = 0;
while (i < numPhotos) {
ratioSum = rowPadding = 0;
row = new Array();
while (i < numPhotos && ratioSum < ratioSumMax) {
photo = $(imgs[i]);
// reset width to original
photo.width("");
ratio = photo.width() / photo.height();
rowPadding += getHorizontalPadding(photo);
// if this is going to be first in the row, clear: left
if(ratioSum == 0) photo.css("clear", "left"); else photo.css("clear", "none");
ratioSum += ratio;
row.push(photo);
i++;
// if only 1 image left, squeeze it in
if(i == numPhotos - 1) ratioSumMax = 999;
}
unitWidth = (containerWidth - rowPadding) / ratioSum;
row.forEach(function (elem) {
elem.width(unitWidth * elem.width() / elem.height());
});
}
}
function getHorizontalPadding(elem)
{
var padding = 0;
var left = elem.css("padding-left");
var right = elem.css("padding-right");
padding += parseInt(left ? left.replace("px", "") : 0);
padding += parseInt(right ? right.replace("px", "") : 0);
return padding;
}
$(window).load(scaleGallery);
$(window).resize(scaleGallery);
What I am trying to achieve now is something very similar to this - http://thesaxman.com/#gallery
I am not great at HTML/CSS, so I am not sure where to start, as I have tried taking examples from many different hover effect tutorials but I can not get anything to work.
Any help is going to be most highly appreciated.
Thank you,
Dan

Related

How to Split *MULTIPLE* image into tiles? (Javascript)

I want to split multiple images into tiles(it must be div or similar). (With Javascript or jQuery.)
and Images should be one below the other.
Its my div;
<div class="reading-content">
<div class="page-break no-gaps">
<img id="image-0" src="image.jpg" class="some-class">
</div>
<div class="page-break no-gaps">
<img id="image-1" src="image1.jpg" class="some-class">
</div>
</div>
example jsfiddle; http://jsfiddle.net/0Lxr2tad/
var t = Date.now();
var img = new Image();
var length = 30;
var ylength = 20;
img.onload = function() {
var width = this.width,
height = this.height,
_length = -length,
i, j;
// create a <div/> with all basic characteristics, to be cloned over and over in the loops below.
var $basicDiv = jQuery('<div/>', {
class: 'splitImg',
css: {
'width': Math.floor(width/length),
'height': Math.floor(height/ylength),
'background-image': 'url(' + img.src + ')'
}
});
// Finding a node in the DOM is slow. Do it once and assign the returned jQuery collection.
// Also, #wrapper's width can be set here.
var $wrapper = $('#wrapper').width(width + length * 2);
for (i = 0; i > _length; i--) {
for (j = 0; j > _length; j--) {
$basicDiv.clone().css({'background-position': `${width/length * j}px ${height/ylength * i}px`}).appendTo($wrapper);
}
}
console.log(Date.now() - t);
}
img.src = 'http://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg'; // how to do multiple this??
Anyone can help?
I don't use jquery much so I can suggest javascript option like this:
const targetElement = document.querySelector(...) // I choose the div that we are going to append newdivs
I don't know if it exists but I assume there is an array of content you use for src urls or any other data.
let arrayOfInfo = [{},{}] // this is a hypothetical array of objects that we will use
arrayOfInfo.forEach(obj => {
let newDiv = document.createElement("div");
newDiv.classList.add("page-break","no-gaps")
newDiv.innerHTML=`<img id="image-${obj.index}" src=${obj.url} class="some-class">`
targetElement.append(newDiv);
})
so with that approach you automate everything. If you need to reuse, you can make it into a function and run it wherever you need. It is also handy if you need to change some content later on, so you only change the array values and rest goes auto again.
I hope this was what you need.
I tried to do it again after a long time and I was able to do it yesterday, anyone can use it. ( You can remove .splitImg { some style } class for remove spaces. )
JSFiddle Example
let i = 0;
let seenUrls = new Set()
setInterval(() => {
let url = document.querySelectorAll('#someid p img')[i].src;
let image = document.querySelectorAll('#someid p')[i]
let gor = document.querySelectorAll('#someid p')
if (!seenUrls.has(url)) {
seenUrls.add(url)
var t = Date.now();
var img = new Image();
var length = 5;
var ylength = 5;
img.onload = function() {
var width = this.width,
height = this.height,
_length = -length,
i, j;
// create a <div/> with all basic characteristics, to be cloned over and over in the loops below.
var $basicDiv = jQuery('<div/>', {
class: 'splitImg',
css: {
'width': Math.floor(width/length),
'height': Math.floor(height/ylength),
'background-image': 'url(' + img.src + ')'
}
});
// Finding a node in the DOM is slow. Do it once and assign the returned jQuery collection.
// Also, #wrapper's width can be set here.
var $wrapper = image;
image.style.width = width + length * 2 + "px";
for (i = 0; i > _length; i--) {
for (j = 0; j > _length; j--) {
$basicDiv.clone().css({'background-position': `${width/length * j}px ${height/ylength * i}px`}).appendTo($wrapper);
}
}
console.log(Date.now() - t);
}
img.src = url
i = (i + 1) % gor.length;
}
}, 50)
#someid {
max-width: 100%;
}
#someid p {
display:flex;
flex-wrap:wrap;
margin: 0 auto;
}
#someid p img {
display: none!important;
}
.splitImg {
padding: 1px;
background-clip: content-box;
background-repeat: no-repeat;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div id="someid">
<p>
<img src="http://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg" />
</p>
<p>
<img src="https://www.jqueryscript.net/images/Responsive-Touch-Friendly-jQuery-Gallery-Lightbox-Plugin-lightGallery.jpg" />
</p>
</div>

Resize image slider to the current image's aspect ratio in JavaScript

I made this fully working image slider, and the only thing I want it to do now is resize its height to the current image's aspect ratio. I wrote a loop that calculates aspect ratio for every image in the slider, the only problem is that, when executed, the loop returns all the values at once, when I only need it to give me one after another value for adjustedHeight each click.
I tried putting i++ inside the loop, pushing all values into an array, which requires another loop to iterate between the array's values, but all of that just feels more complicated than it needs.
<div class="slider-images">
<img src="https://via.placeholder.com/1280x720.png">
<img src="https://via.placeholder.com/1280x720.png">
<img src="https://via.placeholder.com/1280x960.png">
<img src="https://via.placeholder.com/1280x720.png">
<img src="https://via.placeholder.com/1280x720.png">
<img src="https://via.placeholder.com/1280x720.png">
<img src="https://via.placeholder.com/1280x720.png">
</div>
JavaScript
const images = document.querySelector('.slider-images');
const image = document.querySelectorAll('.slider-images img');
var i;
function slideRight() {
//...
for (i = 0; i < image.length; i++) { // Calculates aspect ratio
const allImagesWidth = image[i].naturalWidth,
allImagesHeight = image[i].naturalHeight;
const aspectRatio = allImagesWidth / allImagesHeight;
adjustedHeight = Math.round(slideWidth / aspectRatio); // Final slider height required for a current shown image
}
images.style.height = adjustedHeight + 'px'; // Dynamically should add respective height calculated in the loop above
//...
}
document.querySelector('.slide-right').addEventListener('click', slideRight, false);
CSS
.slider-images {
display: flex;
transition: .5s all cubic-bezier(0.4, 0.0, 0.2, 1);
max-width: 200%;
max-height: 512px;
}
.slider-images img {
width: 50%;
z-index: -1;
}
Finally solved, works and resizes vertically as I wanted. Just needed to move the loop above the slideRight function, set new empty array variable imageArray, push everything from the loop to that array, and then set the style in the function to read from that array and change width based on already existed variable position = 0 used for the slider to work.
Also made adjustments in styles, removed max-height from .slider-images, and set .slider-images img to max-height: unset;, in case you have your img set to max-height: 100%; by default.
var position = 0;
var index = 0;
var imageArray = [];
//...
for (index; index < image.length; index++) {
const allImagesWidth = image[index].naturalWidth,
allImagesHeight = image[index].naturalHeight;
const aspectRatio = allImagesWidth / allImagesHeight;
var adjustedHeight = slideWidth / aspectRatio;
imageArray.push(adjustedHeight++);
}
images.style.height = imageArray[position] + 'px';
function slideRight() {
position--;
images.style.height = imageArray[position] + 'px';
if (position == -1) {
position = imageCount - 1;
}
images.style.transform = 'translateX(' + -(slideWidth * position) + 'px)';
}
// ...
document.querySelector('.slide-right').addEventListener('click', slideRight, false);

Responsive, cover-fit image tagging

I am working on a project where I have a slideshow with images as follows:
img {
width:100vw;
height:100vh;
object-fit:cover;
}
This makes the images fullscreen and behave like background-size:cover, so they fill out the whole viewport on any screen size without distortion.
I would like to tag certain points with text tooltips on these images. For this purpose I have found Tim Severien's Taggd, which works great on responsive images, but in my case the object-fit:cover; property makes the tagged positions inaccurate.
I have tried everything from CSS hacks to improving Tim's code, but I am out of ideas. If you have any solution or workaround in mind please share.
Thank you!
well i actually wanted to do the same thing.
here is what i've done.
maybe it will help someone in the future.
it would be great if this feature could be integrated in taggd.
function buildTags()
{
// be aware that image.clientWidth and image.clientHeight are available when image is loaded
var croppedWidth = false;
var expectedWidth = 0;
var croppedWidthHalf = 0;
var imageWidth = 0;
var croppedHeight = false;
var expectedHeight = 0;
var croppedHeightHalf = 0;
var imageHeight = 0;
var naturalRatio = image.naturalWidth/image.naturalHeight;
var coverRatio = image.clientWidth/image.clientHeight;
if(Math.abs(naturalRatio - coverRatio) < 0.01)
{
// the image is not cropped, nothing to do
}
else
{
if(naturalRatio > coverRatio)
{
// width is cropped
croppedWidth = true;
expectedWidth = image.clientHeight * naturalRatio;
croppedWidthHalf = (expectedWidth - image.clientWidth)/2;
imageWidth = image.clientWidth;
}
else
{
// height is cropped
croppedHeight = true;
expectedHeight = image.clientWidth / naturalRatio;
croppedHeightHalf = (expectedHeight - image.clientHeight)/2;
imageHeight = image.clientHeight;
}
}
function calcy(y)
{
if(croppedHeight)
{
var positiony = y * expectedHeight;
if(positiony > croppedHeightHalf)
return (positiony - croppedHeightHalf)/imageHeight;
else // tag is outside the picture because cropped
return 0; // TODO : handle that case nicely
}
else
return y;
}
function calcx(x)
{
if(croppedWidth)
{
var positionx = x * expectedWidth;
if(positionx > croppedWidthHalf)
return (positionx - croppedWidthHalf)/imageWidth;
else // tag is outside the picture because cropped
return 0; // TODO : handle that case nicely
}
else
return x;
}
var tags = [
Taggd.Tag.createFromObject({
position: { x: calcx(0.74), y: calcy(0.56) },
text: 'some tag',
}),
Taggd.Tag.createFromObject({
position: { x: calcx(0.9), y: calcy(0.29) },
text: 'some other tag',
}),
....
];
var taggd = new Taggd(image, options, tags);
}
$(window).bind("load", function() {buildTags();});
Is not possible. Think if the user has a tablet with 1024x768 resolution, when the user change view from horizontal to vertical the image can fill the space but you will loose part of the image, loose img quality, etc.
The best way for cross devices is to use big pictures and add in css
img {
height: auto;
width: 100%;
display: block;
}
And fill image background with a color;

random position of images

i found a script that would position divs / images randomly. However, it isn't completely working the way i want/need it to.
The images are loaded each within a div (which isn't ideal i guess). I have around 30 images.
But they don't load nicely and var posy = (Math.random() * ($(document).height() - 0)).toFixed(); doesn't work nicely either. The images mostly load on top (i think that the images in the blog don't count so it gets the height without images?)
So what I want: Load the in more nicely Randomize them so they get to the bottom of the page, too
var circlePosition = document.getElementsByClassName('circle');
console.log(circlePosition);
function position() {
for (var i = 0; i < circlePosition.length; i++ ) {
//give circle a random position
var posx = (Math.random() * ($(document).width() - 0)).toFixed();
var posy = (Math.random() * ($(document).height() - 0)).toFixed();
//apply position to circle
$(circlePosition[i]).css({
'position':'absolute',
'left':posx+'px',
'top':posy+'px',
})
}
} //end function position
var circleTotal = circlePosition.length;
$('.circle').click(function() {
$(this).fadeOut();
circleTotal = circleTotal - 1;
console.log(circleTotal);
if(circleTotal == 0) {
position()
$('.circle').fadeIn();
}
});
position();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="//placehold.it/1x5000"> <!-- Placeholder image to show issue -->
<div class="circle">
<img src="http://static.tumblr.com/tensqk8/k8anq0438/01.png">
</div>
<div class="circle">
<img src="http://static.tumblr.com/tensqk8/k8anq0438/01.png">
</div>
A clean and readable and solution without a jQuery dependence might be something like this. It avoids unnecessarily wrapping your images in divs by positioning the images themselves. It includes a hidden element as a sort of "poor man's" shadow DOM.
http://jsfiddle.net/sean9999/yv9otwr7/9/
;(function(window,document,undefined){
"use strict";
var init = function(){
var canvas = document.querySelector('#x');
var icon_template = document.querySelector('#template');
var icon_width = 40;
var icon_height = 30;
var the_images = [
'http://static.tumblr.com/tensqk8/k8anq0438/01.png',
'http://static.tumblr.com/tensqk8/rYanq05el/04.png',
'http://static.tumblr.com/tensqk8/SYknq05py/05.png',
'http://static.tumblr.com/tensqk8/s7inq057d/03.png'
];
var pickRandomImage = function(){
var i = Math.floor( Math.random() * the_images.length );
return the_images[i];
};
var total_number_of_images = 10;
var max_height = canvas.offsetHeight - icon_height;
var max_width = canvas.offsetWidth - icon_width;
var randomCoordinate = function(){
var r = [];
var x = Math.floor( Math.random() * max_width );
var y = Math.floor( Math.random() * max_height );
r = [x,y];
return r;
};
var createImage = function(){
var node = icon_template.cloneNode(true);
var xy = randomCoordinate();
node.removeAttribute('id');
node.removeAttribute('hidden');
node.style.top = xy[1] + 'px';
node.style.left = xy[0] + 'px';
node.setAttribute('src',pickRandomImage());
canvas.appendChild(node);
};
for (var i=0;i<total_number_of_images;i++){
createImage();
};
};
window.addEventListener('load',init);
})(window,document);
body {
background-color: #fed;
}
#x {
border: 3px solid gray;
background-color: white;
height: 400px;
position: relative;
}
#x .icon {
position: absolute;
z-index: 2;
}
<h1>Randomly distributed images</h1>
<div id="x"></div>
<img src="#" class="icon" hidden="hidden" id="template" />
try moving your position(); call inside the $(window).load function.
I think maybe the images are being positioned before all the images have loaded, so the page is shorter then.

Draggable Columns With Pure JavaScript

I'm trying to build a draggable column based layout in JavaScript and having a bit of hard time with it.
The layout comprises of 3 columns (divs), with two dragable divs splitting each. The idea is that they are positioned absolutely and as you drag the draggers, the columns' respective widths, and left values are updated.
The three columns should always span the full width of the browser (the right most column is 100% width), but the other two should remain static by default when the browser is resized (which is why i'm using px, not %).
My code isn't working as of yet, I'm relatively new to JavaScript (which is why I don't want to use jQuery).
Having said that, there must be a more efficient (and cleaner) way of achieving this with less code that works (without reaching for the $ key).
If anyone with some awesome JS skills can help me out on this I'd be super-appreciative.
Here's the fiddle I'm working on http://jsfiddle.net/ZFwz5/3/
And here's the code:
HTML
<!-- colums -->
<div class="col colA"></div>
<div class="col colB"></div>
<div class="col colC"></div>
<!-- draggers -->
<div class="drag dragA" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left:100px;"><div></div></div>
<div class="drag dragB" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left: 300px;"><div></div></div>
CSS:
body {
overflow:hidden;
}
.col {
position: absolute;
height:100%;
left: 0;
top: 0;
overflow: hidden;
}
.colA {background:red;width:100px;}
.colB {background:green; width:200px; left:100px;}
.colC {background:blue; width:100%; left:300px;}
.drag > div {
background: 0 0;
position: absolute;
width: 10px;
height: 100%;
cursor: col-resize;
left: -5px;
}
and my terrible JavaScript:
//variabe columns
var colA = document.querySelector('.colA');
var colB = document.querySelector('.colB');
var colC = document.querySelector('.colC');
//variable draggers
var draggers = document.querySelectorAll('.drag');
var dragA = document.querySelector(".dragA");
var dragB = document.querySelector(".dragB");
var dragging = false;
function drag() {
var dragLoop;
var t = this;
var max;
var min;
if (dragging = true) {
if (this == dragA) {
min = 0;
max = dragB.style.left;
} else {
min = dragA.style.left;
max = window.innerWidth;
}
dragLoop = setInterval(function () {
var mouseX = event.clientX;
var mouseY = event.clientY;
if (mouseX >= max) {
mouseX = max;
}
if (mouseY <= min) {
mouseY = min;
}
t.style.left = mouseX;
updateLayout();
}, 200);
}
}
function updateLayout() {
var posA = dragA.style.left;
var posB = dragB.style.left;
colB.style.paddingRight = 0;
colA.style.width = posA;
colB.style.left = posA;
colB.style.width = posB - posA;
colC.style.left = posB;
colC.style.width = window.innerWidth - posB;
}
for (var i = 0; i < draggers.length; i++) {
draggers[i].addEventListener('mousedown', function () {
dragging = true;
});
draggers[i].addEventListener('mouseup', function () {
clearInterval(dragLoop);
dragging = false;
});
draggers[i].addEventListener('mouseMove', function () {
updateLayout();
drag();
});
}
I see a couple of things wrong here. First of all, the mousemove event only fires on an element when the mouse is over that element. You might have better luck registering a mousemove listener on the parent of your div.drag elements, then calculating the mouse's position inside that parent whenever a mouse event happens, then using that position to resize your columns and your draggers.
Second, I'm not quite sure what you're trying to do by registering a function with setInterval. You're doing pretty well with registering event listeners; why not continue to use them to change the state of your DOM? Why switch to a polling-based mechanism? (and the function you pass to setInterval won't work anyway - it refers to a variable named event, which in that context is undefined.)
This is just a little example... I hope it can help you :)
window.onload = function() {
var myDiv = document.getElementById('myDiv');
function show_coords(){
var monitor = document.getElementById('monitor');
var x = event.clientX - myDiv.clientWidth / 2;
var y = event.clientY - myDiv.clientWidth / 2;
monitor.innerText = "X: " + x + "\n" + "Y: " + y;
myDiv.style.left = x + "px";
myDiv.style.top = y + "px";
}
document.onmousemove = function(){
if(myDiv.innerText == "YES"){show_coords();}
}
myDiv.onmousedown = function(){
myDiv.innerText = "YES";
}
myDiv.onmouseup = function(){
myDiv.innerText = "NO";
}
}

Categories