JavaScript: Enlarge and decrease image on scroll - javascript

I am trying to resize an image on scroll in javascript with the following conditions:
The image is close to an upper and lower image that should not resize
The image has a minimum height of 0, and a max height of almost all screen height
The image should have height 0 until the lower image is fully seen on screen
The image should be resized, letting the lower image's position fixed at the bottom, until the upper image reaches the top.
The image will then start shrinking, and the upper image should be kept at top until the middle image reaches it's minimum size again.
My approach is as follows:
var upperImg = document.getElementById("scroll-sup");
var middleImg = document.getElementById("scroll-int");
var lowerImg = document.getElementById("scroll-inf");
const minHeight = 0;
const maxHeight = document.documentElement.clientHeight - 50;
document.addEventListener("wheel", function(e) {
var upperRect = upperImg.getBoundingClientRect();
var lowerRect = lowerImg.getBoundingClientRect();
var upperDistanceToTop = window.pageYOffset + upperRect.top - 100;
var delta = Math.floor(e.deltaY);
if (lowerRect.y > maxHeight + 500 || middleImg.height < minHeight) {
middleImg.height = minHeight;
} else if (middleImg.height > maxHeight) {
middleImg.height = maxHeight;
} else if (upperRect.top < 60 && middleImg.height > minHeight) {
middleImg.height -= delta;
window.scrollBy(0, -delta);
} else {
middleImg.height += delta;
}
e.preventDefault();
});
.flex-container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.scroll-img {
border-radius: 0px;
width: 100%;
}
<div style="height: 1000px">some HTML</div>
<div class="flex-container">
<img
src="https://raw.githubusercontent.com/sandraparraga/surveillancepower/main/assets/marco_teorico/scroll-sup.png"
id="scroll-sup"
class="scroll-img"
/>
<img
src="https://raw.githubusercontent.com/sandraparraga/surveillancepower/main/assets/marco_teorico/scroll-int.png"
id="scroll-int"
class="scroll-img"
style="position: sticky; object-fit: fill"
/>
<img
src="https://raw.githubusercontent.com/sandraparraga/surveillancepower/main/assets/marco_teorico/scroll-inf.png"
id="scroll-inf"
class="scroll-img"
/>
</div>
<div style="height: 1000px">some more HTML</div>
I fail to see how my code is wrong, but it is not decreasing the image and keeping the upper one in a fixed position.
Any help is appreciated, thanks in advance.

Related

Problem with infinite loop in slider using jQuery

I tried making jQuery slider, which will pause on current image for couple of seconds and then move images horizontally. Also, when slider presents the last image in collection, slider should slowly get back to first image, or when marginLeft attribute reaches value of -200vw(it decreases gradually for -100vw after every image). Most of the time slider works perfectly, but there are also moments when slider ignores my if and goes above -200vw(sometimes up to -3000vw), resulting in showing no images. The only thing that I noticed in this situation is that this problem can occur after around 5-10 minutes spent on other browser tabs.
P.S I apologize for a lack of precise details, because these are the only thing I have noticed with this problem
jQuery/Javascript
$(document).ready(function() {
let delay = 5300;
let currentSlide = 1;
let $carousel = $('#carousel')
let $slideContainer = $carousel.find('.slides');
let $width = 100;
let $slides = $slideContainer.find('.slide')
var interval = setInterval(function() {
$slideContainer.animate({
'marginLeft': '-=' + $width + 'vw'
}, 1000, function() {
currentSlide++;
var abc = $slideContainer.width();
if (currentSlide == $slides.length) {
setTimeout(function() {
$slideContainer.animate({
'marginLeft': '+=' + ($width + 100) + 'vw'
}, 1000, function() {
currentSlide = 1;
$slideContainer.css({
'marginLeft': 0 + 'vw'
})
})
}, (delay / 2.5))
}
})
}, delay);
});
HTML
<section id="carousel">
<div class="slides">
<div class="slide">
<img src="../javaskript/images/slider/gaming1.jpg" style="height:100vh" alt="Introduction image 2 - slider" />
</div>
<div class="slide">
<img src="../javaskript/images/slider/gaming2.jpg" style="height:100vh" alt="Introduction image 2 - slider">
</div>
<div class="slide">
<img src="../javaskript/images/slider/scorn.jpg" style="height:100vh" alt="Introduction image 2 - slider">
</div>
</div>
</section>
CSS
#carousel {
overflow : hidden;
white-space : nowrap;
width : 300vw;
height : 100vh;
}
.slides {
display : flex;
width : 300vw;
margin : 0;
padding : 0;
}
.slide {
width : 100vw;
overflow : hidden;
}

Trying to keep the random placement of my folders within the window. At the moment it seems to overflow on the x and y axis

I am trying to keep the random placement of my folders within the window. At the moment it seems to overflow on the x-axis and y-axis. Are you able to help? I'm sure this is a simple fix.
The goal is to have the set of folders randomly placed on the screen, but never be placed outside of the screen height and width.
<style>
/* Background Color */
body {
background-color: lightcoral;
}
/* Div position and placement */
div {
position: absolute;
top: 100px;
left: 100px;
}
</style>
<body>
<div class="container">
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
<div><img src="/images/folder.png" width="100px" /></div>
</div>
</body>
<script>
// collect all the div folders
var folderDivs = document.getElementsByTagName("div");
// get window width and height
var winWidth = window.innerWidth;
var winHeight = window.innerHeight;
// i stands for "index". you could also call this banana or haircut. it's a variable
for (var i = 0; i < folderDivs.length; i++) {
// shortcut! the current div in the list
var thisDiv = folderDivs[i];
// get random numbers for each element
randomTop = getRandomNumber(0, winHeight);
randomLeft = getRandomNumber(0, winWidth);
// update top and left position
thisDiv.style.top = randomTop + "px";
thisDiv.style.left = randomLeft + "px";
}
// function that return a random number between a min and max
function getRandomNumber(min, max) {
return Math.random() * (max - min) + min;
}
</script>
Do not use getElementsByTagName("div") because this applies also to
div class="container"
Instead use
var folderDivs = document.querySelectorAll(".container div");
Subtract image size from allowed place
In your style part you have set top and left position to each div. This rule applies to container div too.
Notice: Your images are in div with class "container" but this div is positioned 100px left and 100px top.
So all your images have 100px left and top offset.
200 = 100 (for image size) + 100 (for container offset)
var imgSizeWithOffset = 200;
// get random numbers for each element
randomTop = getRandomNumber(0, winHeight-imgSizeWithOffset);
randomLeft = getRandomNumber(0, winWidth-imgSizeWithOffset);
If you decide you don't need this container offset you can set your style like this and use 100
<style>
/* Background Color */
body {
background-color: lightcoral;
}
/* Div position and placement */
.container div {
position: absolute;
}
</style>

How to condense JavaScript Using Loops

I have the following code working properly to responsively lazy load background images for a number of divs on a page:
// get frames
// REFACTOR LIST:
var frame1 = document.getElementById('frame1');
var frame2 = document.getElementById('frame2');
var frame3 = document.getElementById('frame3');
var frame4 = document.getElementById('frame4');
var frame5 = document.getElementById('frame5');
// create Lazy loader
var myLazyLoad = new LazyLoad({
elements_selector: ".lazy"
});
// load images responsively
function loadImgs() {
console.log('Loading images...');
if(window.matchMedia("only screen and (max-width:700px)").matches) {
// viewport is less than or equal to 700 pixels wide
// REFACTOR LIST:
var src1 = frame1.getAttribute('data-src-small');
var src2 = frame2.getAttribute('data-src-small');
var src3 = frame3.getAttribute('data-src-small');
var src4 = frame4.getAttribute('data-src-small');
var src5 = frame5.getAttribute('data-src-small');
} else {
// viewport is greater than 700 pixels wide
// REFACTOR LIST:
var src1 = frame1.getAttribute('data-src-large');
var src2 = frame2.getAttribute('data-src-large');
var src3 = frame3.getAttribute('data-src-large');
var src4 = frame4.getAttribute('data-src-large');
var src5 = frame5.getAttribute('data-src-large');
}
// set data-src for lazy loader
// REFACTOR LIST:
frame1.setAttribute('data-src', src1);
frame2.setAttribute('data-src', src2);
frame3.setAttribute('data-src', src3);
frame4.setAttribute('data-src', src4);
frame5.setAttribute('data-src', src5);
// tell lazy loader that the data should be re-processed
// REFACTOR LIST:
frame1.removeAttribute('data-was-processed');
frame2.removeAttribute('data-was-processed');
frame3.removeAttribute('data-was-processed');
frame4.removeAttribute('data-was-processed');
frame5.removeAttribute('data-was-processed');
// tell lazy loader to update
myLazyLoad.update();
}
// load images initially
loadImgs();
// reload images when window is resized across the 700px breakpoint
var lastWindowSize = window.innerWidth;
window.onresize = function(event) {
var currentWindowSize = window.innerWidth;
if((lastWindowSize <= 700 && currentWindowSize > 700) || (lastWindowSize > 700 && currentWindowSize <= 700)) {
loadImgs();
}
lastWindowSize = currentWindowSize;
};
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
&:focus {
outline: none;
}
}
* {
font-family: monaco, courier;
}
body {
margin: 0;
padding: 0;
}
.wrapper {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center ;
background: #ddd;
}
p {
position: absolute;
top: 0;
left: 0;
margin: 0;
padding: 8px;
color: darkslategray;
background: gold;
}
.frame {
width: 80vw;
height: 200px;
margin: 0 0 1rem 0;
padding: 0;
position: relative;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: 2px solid gold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/8.7.1/lazyload.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- https://stackoverflow.com/questions/50431531/lazylaoding-css-background-not-html-img-tags -->
<main class="wrapper">
<a href="#">
<div id="frame1" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=1"
data-src-large="https://source.unsplash.com/random/1200x600?sig=1">
<p>1</p>
</div>
</a>
<a href="#">
<div id="frame2" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=2"
data-src-large="https://source.unsplash.com/random/1200x600?sig=2">
<p>2</p>
</div>
</a>
<a href="#">
<div id="frame3" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=3"
data-src-large="https://source.unsplash.com/random/1200x600?sig=3">
<p>3</p>
</div>
</a>
<a href="#">
<div id="frame4" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=4"
data-src-large="https://source.unsplash.com/random/1200x600?sig=4">
<p>4</p>
</div>
</a>
<a href="#">
<div id="frame5" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=5"
data-src-large="https://source.unsplash.com/random/1200x600?sig=5">
<p>5</p>
</div>
</a>
</main>
CodePen here
But I'd like to refactor the code to dry it up. I'm thinking for loops can be used to replace each of the 5 lists under a REFACTOR LIST: comment. My aim is to enable the code for any unknown number of divs with a class of frame.
To start, as an example, I've attempted to refactor the variable declarations at the beginning with the following loop:
var FramesQuantity = document.getElementsByClassName("frame").length
var frameVariables = [];
function createframeVariables() {
for (var i = 0; i <= FramesQuantity; ++i) {
var frameIndex = 'frame' + i;
console.log("frameIndex: " + frameIndex);
frameVariables[i] = document.getElementById(frameIndex);
}
return frameVariables;
}
createframeVariables();
console.log("frameVariables[0]: " + frameVariables[0]);
but that second console log returns null and I'm not sure if this is the right direction anyway.
Any ideas?
As was suggested, I was able to refactor the code, DRYing it up, by using .forEach with .querySelectorAll which obviated the need to set variables:
// for loop demo
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
console.log( "index: " + index);
console.log( "frame.dataset.srcSmall: " + frame.dataset.srcSmall);
console.log( "frame.dataset.srcLarge: " + frame.dataset.srcLarge);
})
// create Lazy loader
var myLazyLoad = new LazyLoad({
elements_selector: ".lazy"
});
// load images responsively
function loadImgs(context) {
console.log('Loading images ' + context);
if(window.matchMedia("only screen and (max-width:700px)").matches) {
// viewport is less than or equal to 700 pixels wide
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
var srcSmall = frame.dataset.srcSmall;
// set data-src for lazy loader
frame.setAttribute('data-src', srcSmall);
// tell lazy loader that the data should be re-processed
frame.removeAttribute('data-was-processed');
})
} else {
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
// viewport is greater than 700 pixels wide
var srcLarge = frame.dataset.srcLarge;
// set data-src for lazy loader
frame.setAttribute('data-src', srcLarge);
// tell lazy loader that the data should be re-processed
frame.removeAttribute('data-was-processed');
})
}
// tell lazy loader to update
myLazyLoad.update();
}
// load images initially
loadImgs("initially");
// reload images when window is resized across the 700px breakpoint
var lastWindowSize = window.innerWidth;
window.onresize = function(event) {
var currentWindowSize = window.innerWidth;
if((lastWindowSize <= 700 && currentWindowSize > 700) || (lastWindowSize > 700 && currentWindowSize <= 700)) {
loadImgs("on resize across breakpoint");
}
lastWindowSize = currentWindowSize;
};
Forked updated version of the original CodePen here.

Why can't move the image with ++imgbox.scrollLeft?

I want to move a serie of images from right to left using javascript.
window.onload = function(){
function move(){
var speed = 500;
var imgbox = document.getElementById("imgbox");
imgbox.innerHTML += imgbox.innerHTML;
var span = imgbox.getElementsByTagName("span");
var timer = window.setInterval(marquee,speed);
function marquee(){
console.log(imgbox.scrollLeft);
console.log(span[0].offsetWidth);
if( imgbox.scrollLeft > span[0].offsetWidth){
imgbox.scrollLeft = 0;
}else{
++imgbox.scrollLeft;
console.log("in else block");
console.log(imgbox.scrollLeft);
}
}
}
move();
}
div{
width: 933px;
height: 129px;
border: 1px solid red;
overflow:hidden;
}
<div id="imgbox">
<span>
<img src="https://i.stack.imgur.com/b7J9w.jpg" alt="">
<img src="https://i.stack.imgur.com/yh7YJ.jpg" alt="">
<img src="https://i.stack.imgur.com/5uIog.jpg" alt="">
<img src="https://i.stack.imgur.com/r5GCW.jpg" alt="">
</span>
</div>
When to open it with firefox, js run in else block ,why imgbox.scrollLeft can't increase?imgbox.scrollLeft keep value as 0 ,not increased as 1,2,3.......
How to fix my js or css ?
Adding white-space: nowrap; to the style helps. With white-space option allowing text wrapping, there is no horizontal overflow, the element needs no horizintal scrolling, and .scrollLeft is automatically reset to 0. See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft, section Setting the value.
window.onload = function(){
function move(){
var speed = 50; // I made it faster.
var imgbox = document.getElementById("imgbox");
imgbox.innerHTML += imgbox.innerHTML;
var span = imgbox.getElementsByTagName("span");
var timer = window.setInterval(marquee,speed);
function marquee(){
console.log(imgbox.scrollLeft);
console.log(span[0].offsetWidth);
// see UPD -v
if( imgbox.scrollLeft > span[0].offsetWidth || imgbox.scrollLeft >= imgbox.scrollWidth - imgbox.clientWidth ){
imgbox.scrollLeft = 0;
}else{
++imgbox.scrollLeft;
console.log("in else block");
console.log(imgbox.scrollLeft);
}
}
}
move();
}
div{
width: 933px;
height: 129px;
border: 1px solid red;
overflow:hidden;
white-space: nowrap; /* <-- */
}
<div id="imgbox">
<span>
<img src="https://i.stack.imgur.com/b7J9w.jpg" alt="">
<img src="https://i.stack.imgur.com/yh7YJ.jpg" alt="">
<img src="https://i.stack.imgur.com/5uIog.jpg" alt="">
<img src="https://i.stack.imgur.com/r5GCW.jpg" alt="">
</span>
</div>
UPD per #Jaromanda_X. There is a another problem with the author's code: the scrolling stops when imgbox.scrollLeft reaches 910, for
If specified as a value greater than the maximum that the content can be scrolled, scrollLeft is set to the maximum.
The maximum value of .scrollLeft is defined as .scrollWidth - .clientWidth (see https://stackoverflow.com/a/5704386/6632736). So, the condition for resetting .scrollLeft ought to be imgbox.scrollLeft > span[0].offsetWidth || imgbox.scrollLeft >= imgbox.scrollWidth - imgbox.clientWidth. Perhaps, it can be simplified.

fit images with different aspect ratios into multiple rows evenly

Good morning.
First, thanks in advance! I've been a stack overflow spectator for quite a while, and you guys are great.
I am looking to create a photo layout for my webpage www.eden-koru.com, where photos are presented in rows. Due to cropping, and different cameras, each photo may have different aspect ratios and therefor there are many uneven gaps when just placed in a row.
A perfect example of what I want to do is www.flickr.com/childe-roland. Those are my photos, all laid out perfectly despite aspect ratio.
On a different, but similar question I found an 80% solution with this JSFiddle http://jsfiddle.net/martinschaer/aJtdb/:
var container_width = $('#container2').width();
var container_width_temp = 0.0; // must be float!
var container_height = 100.0; // random initial container heigth for calculations
$('#container2 img').each(function(){
var newwidth = (this.width / this.height) * container_height;
this.width = newwidth;
$(this).data('width', newwidth);
container_width_temp += newwidth;
});
$('#container2 img').each(function(){
this.width = $(this).data('width') * (container_width / container_width_temp);
});
Now, that only works for one row. I have no experience with JQuery, but I was able to see the math and create a "row_counter" variable that counted the number of image wrapper divs... That got me to 90%. I just multiplied the final width by that row count, then subtracted a few pixels to make up for margins.
it looks like this:
$('.imageWrapper').each(function(){
rows +=1;
});
My div layout looks like this:
<div class="mainWrapper">
<div class="imageWrapper">
<img width="326" src="images/_DSC4434.jpg"></img>
<img width="276" src="images/_DSC4537.jpg"></img>
<img width="254" src="images/_DSC4483.jpg"></img>
</div>
<div class="imageWrapper">
<img width="276" src="images/_DSC0253.jpg"></img>
<img width="306" src="images/The_Alaska_RangeIR.jpg"></img>
<img width="275" src="images/DSC_9111.jpg"></img>
</div>
<div class="imageWrapper">
<img width="276" src="images/_DSC4689.jpg"></img>
<img width="276" src="images/_DSC4718.jpg"></img>
<img width="276" src="images/_DSC4738.jpg"></img>
</div>
</div>
and my CSS like this:
.mainWrapper {
background-color: black;
margin: 0 auto 50px auto;
width: 70%;
height: auto;
border: 2px solid white;
border-radius: 10px;
clear: both;
padding: 7px 7px 7px 7px;
text-align: center;
overflow: hidden;
}
.mainWrapper .imageWrapper {
overflow: hidden;
width: 100%x;
margin: 0px auto 0px auto;
}
.mainWrapper .imageWrapper img {
display: inline-block;
border: 1px solid #fff;
}
Now, it looks better than it did, but there is still a lot of unevenness that I can't account for with styling. Additionally I can no longer use width: 100% to make my images shrink as the viewport changes.
I hope that I have given enough detail. Please keep in mind that I know nothing about JQuery and haven't touched JavaScript in 5 years. I was an IT major who joined the navy after graduation and never coded again until last week.
Cheers!
Wes
This is something quite complex. I managed to make a jQuery plugin that almost achieves what you want, I'm having some issues with making it dynamic when a user resizes their browser window, but ignoring this, it should do what you're asking for.
jQuery Plugin
(function ( $ ) {
$.fn.gallery = function( options ) {
var settings = $.extend({
imgs: [],
row_height: 300,
margin: 10
}, options);
var container = $(this);
//create a div for each image
for(var i=0;i<settings.imgs.length;i++){
$(this).append("<div class='imgwrapper'></div>");
}
//setup the css for the imgwrappers
$("head").append("<style type='text/css'>.imgwrapper{ float: left; margin-left: "+settings.margin+"px; margin-top: "+settings.margin+"px; height: 261px; background-repeat: no-repeat; background-position: center; background-size: cover;}</style>")
//define some global vars
var imgs_aspect = [];
var imgs_rows = [0];
var tot = 0;
var loaded = 0;
function setup(){
var imgs = settings.imgs;
var row_width = 0;
$(".imgwrapper").each(function(index){
var imgwrapper = $(this);
var img = new Image();
img.src = imgs[index];
img.onload = function(){
//determine the aspect ratio of the image
var img_aspect = img.height/img.width;
imgs_aspect.push(img_aspect);
//work out a rough width for the image
var w = settings.row_height*img_aspect;
row_width += w;
//check if there is still space on this row for another image
if(row_width >= container.width()){
imgs_rows.push(1);
row_width = 0;
}
else{
imgs_rows[imgs_rows.length-1]++;
}
//set some of the css vars
imgwrapper.css("width",w+"px");
imgwrapper.css("height",settings.row_height+"px");
imgwrapper.css("background-image","url("+imgs[index]+")");
loaded++;
checkIfLoaded();
}
});
}
function checkIfLoaded(){
//make sure all images are loaded
if(loaded == $(".imgwrapper").length){
setHeight();
}
}
function setHeight(){
for(var r=0;r<imgs_rows.length;r++){
if(r==0){
var y = 0;
}
else{
var y = 0;
for(var j=0;j<r;j++){
y += imgs_rows[j]
}
}
if(imgs_rows[r] == 0){
}
else{
tot = 0;
for(var i=y;i<(y+imgs_rows[r]);i++){
tot += imgs_aspect[i];
}
//work out optimum height of image to fit perfectly on the row
var h = ((container.width()-(settings.margin*(imgs_rows[r]+1)))/tot);
$(".imgwrapper").each(function(index){
if(index >= y && index < (y+imgs_rows[r])){
//work out width using height
var w = h*imgs_aspect[index];
$(this).css("width",w+"px");
}
});
}
}
}
setup();
};
}( jQuery ));
How To Use
var images = ["http://lorempixel.com/300/300",
"http://lorempixel.com/250/250",
"http://lorempixel.com/200/200",
"http://lorempixel.com/210/220",
"http://lorempixel.com/210/230",
"http://lorempixel.com/260/230",
"http://lorempixel.com/410/830",
"http://lorempixel.com/300/200",
"http://lorempixel.com/250/250",
"http://lorempixel.com/200/200",
"http://lorempixel.com/210/220",
"http://lorempixel.com/210/230",
"http://lorempixel.com/260/230",
"http://lorempixel.com/410/830"];
$(".container").gallery({imgs:images, margin: 0, row_height: 300});
images is an array which should contain the images url that you wish to use. The container can have any width desired (define in the css). The margin value allows you to have a similar white border around the images. Since I saw in your code that you had a row height, this is also implemented, by just changing the row_height value.
Demo: http://codepen.io/motorlatitude/pen/iHgCx
This is far from perfect, but it might give you an idea of what you need to do.
Hope it helps!

Categories