I am trying to create an animation which takes a image that is anywhere on a page and moves it to the middle while resizing it to full width of the browser window. My solution works, but has some stutters/jumps in it, which I can't really explain. Is there anyone who has tried creating a similar animation already? EDIT: I noticed that the stutter problem only seems to appear in macOS Safari. In other browsers this animation appears to run perfectly smooth.
Here is my js code:
function getWindowWidth() {
return document.documentElement.clientWidth
}
function getWindowHeight() {
return document.documentElement.clientHeight;
}
//at the moment this is hacky and only supports one image to be enlarged
let en_img_left = null;
let en_img_top = null;
function enlargeImage(img) {
let boundingClientRect = img.getBoundingClientRect();
img.style.position = "fixed";
en_img_top = boundingClientRect.y + "px";
img.style.top = en_img_top;
en_img_left = boundingClientRect.x + "px";
img.style.left = en_img_left;
img.style.width = boundingClientRect.width + "px";
img.style.zIndex = "1000";
setTimeout(function() {
img.style.transition = "1s ease-in-out";
setTimeout(function() {
let scaleFactor = getWindowWidth() / boundingClientRect.width;
img.style.transform = "scale(" + scaleFactor + ")";
img.style.left = getWindowWidth() / 2 - (boundingClientRect.width / 2) + "px";
img.style.top = getWindowHeight() / 2 - boundingClientRect.height / 2 + "px";
}, 1);
}, 1);
return img;
}
function delargeImage(img) { //sorry for the function name
img.style.transition = "1s ease-in-out";
setTimeout(function() {
img.style.transform = "scale(1)";
img.style.left = en_img_left;
img.style.top = en_img_top;
}, 1);
return img;
}
example HTML+CSS code, but it can be any image with an ID on a website:
HTML:
<div class="container">
<img id="example" style="width: 100%" src="https://images.pexels.com/photos/1361815/pexels-photo-1361815.jpeg?cs=srgb&dl=blur-bokeh-close-up-1361815.jpg&fm=jpg">
</div>
CSS:
.container {
width: 200px;
}
I also made a jsfiddle displaying the stutter problem quite nicely:
https://jsfiddle.net/robske_110/vhz5Ln4o/11/
You are not using CSS animations or transitions!
The animation itself is executed through JavaScript in your example. Instead of computing every single step of an animation in JS and setting a new CSS property on each iteration, you should setup a CSS animation with the desired start- and end-states or define the properties, that should be transitioned. This way the animation should look smooth while transitioning.
Your example using a CSS transition (without any JS code):
.container {
width: 200px;
transition: width ease-in 1s;
}
.container:hover {
width: 80vw;
}
.container img {
width: 100%;
}
<div class="container">
<img id="example" src="https://images.pexels.com/photos/1361815/pexels-photo-1361815.jpeg?cs=srgb&dl=blur-bokeh-close-up-1361815.jpg&fm=jpg">
</div>
Related
I have some sort of poll where you vote either YES and NO and based on the votes it creates a poll chart (by creating two divs inside another div that has a set width and setting the width of the first two divs the percentage of YES and NO votes out of the total votes). You can see the project for a better understanding by clicking HERE.
I want it to appear animated as if it were in CSS with transition: width 100ms linear; just like here:
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<style>
.poll{
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover{
width: 500px;
}
</style>
</head>
<body>
<div class="poll"></div>
</body>
</html>
However, whenever I add something similar to the class of my divs I see no change. The divs in question are created in this function:
function renderPoll(){
container.innerHTML=''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES votes with the equation percentage = (100*NumberOfYES)/NumberOfVotes
poll1.style.width = calcPerc() + '%';
poll2.style.width = 100-calcPerc() + '%';
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100-innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
}
I am not nearly experienced enough to figure this out so any input is appreciated!
Bulding on your code and #Noel MarĂ³ti answer, indeed all you have to do is set interval for animating the polls after you add them to the container.
function renderPoll() {
container.innerHTML = ''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100 - innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
var target_length = 300;
animation(poll1, 0, (calcPerc()) * target_length / 100);
animation(poll2, 0, (100 - calcPerc()) * target_length / 100);
}
function calcPerc() {
return 75;
}
function animation(elem, from, to) {
let id = null;
let width = from || 0;
var speed = 2.5;
requestAnimationFrame(frame);
function frame() {
if (width < to) {
width += speed;
elem.style.width = width + "px";
requestAnimationFrame(frame);
}
}
}
renderPoll();
.poll-attr {
border: 1px solid blue;
height: 50px;
background: lightyellow;
}
.poll {
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover {
width: 500px;
}
<div class="poll"></div>
<div id="container"></div>
You can do it easily like this:
function animation () {
let id = null;
const elem = document.querySelector(".poll");
let width = 300; // default width
clearInterval(id);
id = setInterval(frame, 5); // changing the number will effect the speed of the animation
function frame() {
if (width == 500) { // if the width is 500px, then finish animation
clearInterval(id); // finish animation
} else {
width++;
elem.style.width = width + "px";
}
}
}
I'm trying to change the opacity of a DIV based on how much is visible (height-wise) in the window. For example if 50% of the DIV is visible in the window then the opacity should be .5
Here is what I've got, I know it's amateur and not optimal code. It's my math that is the problem. When the DIV is roughly 50% on the screen, with my calculations it comes out to around 80%
$(window).scroll(function () {
var block = $('.block')
var blockHeight = block.outerHeight();
var bottom_of_block = block.offset().top + blockHeight;
var blockOpacity = 0;
if (bottom_of_block < ($(window).height() + $(window).scrollTop())) {
// Sets opacity to 1 if div is completely on screen
blockOpacity = 1;
} else {
// This is the math that I cant figure out completely
blockOpacity = ($(window).height() + $(window).scrollTop()) / (bottom_of_block);
}
block.css('opacity', blockOpacity);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="height: 500px"></div>
<div class="block" style="background: blue;height: 220px;width: 220px;"></div>
I think you need to calculate blockOpacity like this:
blockOpacity = ($(window).height() + $(window).scrollTop() - block.offset().top) / blockHeight;
I want to achieve when an image is clicked then it will enlarge for a set amount of time. So far I'm up to here in my JS but it doesn't work
var image = document.getElementById('pic');
function enlarge() {
image.style.height="600px";
}
image.onclick =enlarge;
After I tried to implement.
var image = document.getElementById('pic');
function enlarge() {
image.style.height="600px";
}
image.onclick = setInterval(enlarge; 1000);
How should I implement this? JSFIDDLE
Using setInterval
We want 60fps, so each frame would be 1000 / 60 which equals about 16.667ms long. We need to enlarge the height by 500px. 500 / 60 equals to 8.334. So we need an interval of 16.667 ms, and every iteration, enlarge the image by 8.334px, and stop when the height reaches 600px:
var images = document.querySelectorAll('.pic');
images.forEach(function(image) {
image.addEventListener('click', enlarge);
});
function enlarge(e) {
var image = e.target;
var interval;
var height = 100;
interval = setInterval(function() {
height += 8.334;
if(height >= 600) {
height = 600;
clearInterval(interval);
}
image.style.height = height + 'px';
}, 16.667);
}
.pic {
width: 100px;
height: 100px;
vertical-align: top;
}
<img src='https://placehold.it/100x100' class='pic'>
<img src='https://placehold.it/100x100' class='pic'>
Using requestAnimationFrame
A better way of doing it, will use requestAnimationFrame() that produces smoother animations. According to MDN:
The window.requestAnimationFrame() method tells the browser that you
wish to perform an animation and requests that the browser call a
specified function to update an animation before the next repaint.
The math stays the same, but requestAnimationFrame will handle the calling the next frame after 16.667ms.
var images = document.querySelectorAll('.pic');
images.forEach(function(image) {
image.addEventListener('click', enlarge);
});
function enlarge(e) {
var image = e.target;
var interval;
var height = 100;
function enlargeInner() {
height += 8.334;
if(height >= 600) {
height = 600;
}
image.style.height = height + 'px';
height < 600 && requestAnimationFrame(enlargeInner);
}
enlargeInner();
}
.pic {
width: 100px;
height: 100px;
vertical-align: top;
}
<img src='https://placehold.it/100x100' class='pic'>
<img src='https://placehold.it/100x100' class='pic'>
You just assign same height in an interval. You need to increment it, like:
image.style.height = (+image.style.height + 600) + "px";
But I guess it is not your goal, as it will make your image grow 600px every second. I think what you are looking for is just making it bigger to actual point of size? If so, try using CSS transition combined with javascript, like:
CSS:
img {
width: 300px;
height: 300px;
-webkit-transition: width 1s linear, height 1s linear;
transition: width 1s linear, height 1s linear;
}
.enlarged {
width: 600px;
height: 600px;
}
JS:
document.getElementById('pic').addEventListener("click", function(e){
this.classList.toggle("enlarged");
}
setInterval() only won't work.
Basically what your code is doing, that it waits 1000 milliseconds, and runs the enlarge function.
(by the way, you have a typo, at the last line, there should be a comma between enlarge and 1000)
The way I would do it is to add a css class with an animation, and then add that class to the image on click.
let myImg = document.getElementById("img1");
myImg.addEventListener("click", () => myImg.classList.toggle("enlarge"));
/*
The code above is using ES6 (the newest version of JavaScript) and and a thing called an arrow function. If you don't get it, here is the "normal way" to do it. It will do exactly the same as the above code.
var myImg = document.getElementById("img1");
myImg.addEventListener("click", function(){
myImg.classList.toggle("enlarge")
});
*/
#img1 {
transition: 1s
}
.enlarge {
width: 300px;
height: 300px;
}
<img id="img1" src="https://foswiki.org/pub/Support/Glossary/600px-Example.svg.png" width="100px" height="100px">
I forked your fiddle.
You need to change the way you're approaching the click event like so:
function enlarge() {
setInterval(function() {
// do stuff
}, 1000)
}
image.onclick = enlarge;
I'm experiencing an issue with full-width divs/imgs.
I'll preface this by saying I know it'd be easy to create one with width:100%, but I'm writing within someone else's code and have to place my content within 8-9 nested divs whose overflow I cannot change from hidden. To get a div/img to appear above/outside these 8-9 divs, I've tried a few strategies, none of which is working, so I'm trying what I consider the bluntest strategy--making the img position:fixed and using offsetTop to calculate the exact position I want the img placed--but when I do this I run into an issue where the img isn't visible and the Chrome developer console thinks it's forever hiding below the window. Code I'm using live + screenshot follows. Fiddle that replicates the issue (make sure to resize the page) here.
CSS
div.full_width {
width:100%;
height:100px;
background-color:rgba(0,0,0,0.1); /* default light gray */
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
HTML
<div class="full_width" id="twilight">
<img src="http://res.cloudinary.com/territory/image/upload/v1461033616/issue-i-utopia/adam-al-sirgany/kuhne_twlight.jpg"/>
</div>
JS
function fullWidth() {
var newWidth = document.documentElement.clientWidth + "px";
var newHeight = document.documentElement.clientHeight + "px";
var allImageDivs = document.getElementsByClassName("full_width");
if (allImageDivs.length > 0) {
for (i = 0; i < allImageDivs.length; i++) {
var image = allImageDivs[i].getElementsByTagName("img")[0];
var newTop = allImageDivs[i].offsetTop + "px";
allImageDivs[i].style.height = newHeight;
allImageDivs[i].style.backgroundColor = "rgba(0,0,0,0)"
image.style.position = "fixed";
image.style.left = 0;
image.style.right = newWidth;
image.style.top = newTop;
image.style.width = newWidth;
image.style.height = newHeight;
image.style.zIndex = 1000000;
}
}
};
window.addEventListener("DOMContentLoaded", fullWidth);
window.addEventListener("resize", fullWidth);
image at bottom
I'm sure there are better solutions to this problem, which I'm very open to learning, but I'm also curious as to why the img is behaving this way.
Thanks fam.
Got a strange issue, my tag has a greater width than my monitor, which it shouldn't. I have some JavaScript which gets the scroll offset and adjusts my background, to give it a parallax effect, but as you can see, once the background gets given an 100% width, it snaps and stretches out. You can see this by zooming out of the page, the background is larger.
Here is the website
Any idea what is going wrong with it? Here is my JavaScript, and view the CSS by inspecting the element. It has also gone a bit slow as well to be honest, was working nice and smooth.
var ismobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
if (!ismobile){
window.onresize = function(event) {
//Detect window size and make new padding
if (window.innerWidth > 835) {
var newPadding = parseInt(window.innerHeight)/2.8;
newPadding = newPadding.toFixed(0);
var limitPadding = 221;
//Apply new padding value to header
if (newPadding > limitPadding) {
doc("header").style.padding = newPadding + "px 0px";
}
}
}
window.onscroll = function() {
var speed = 0.7;
var newPos = "100% " + (window.pageYOffset * speed) + "px";
document.body.style.backgroundPosition = newPos;
}
}
Add the overflow property to your body tag...
body {overflow-X: hidden;}