I am creating event's page and at the bottom I placed many logos. It sliding from right to left. I don't use jQuery, only pure Javascript and I just wondering about the best performance. My code works, but maybe there is better way to do that ? I think this 'animation' sometimes slow down.
var banners = [],
links = [];
links[0] = 'http://...',
banners[0] = 'img/logo1.png',
...
var banLenght = banners.length,
banContent = "<div id='bannersBack'><div id='banners' style='display:inline-block;'>";
for (var ii =0; ii < banLenght; ii++){
banContent += "<a target='_blank' href='"+links[ii]+"'><img src='"+banners[ii]+"'></a>";
}
banContent += "</div></div>";
document.getElementById('sliding-logos').innerHTML = banContent;
var actual = document.getElementById('banners');
var move = function(){
position = actual.offsetLeft;
position -= 1;
actual.style.left = position +"px";
// 3000 is sum of banner's width
if (position > -3000) {
setTimeout( move, 20);
}else {
actual.style.left = "0px";
move();
}
};
move();
single image sprite rather than multiple image
CSS Transitions rather than JS. CSS is part of the browser engine and doesn't have to modify the properties of the DOM so it should be faster
Here's an example (however it is does NOT work in all browsers yet)
http://css-tricks.com/infinite-all-css-scrolling-slideshow/
Best performance is achieved by using CSS transforms+translate. Modern browsers will be able to use the GPU to do the transformation.
.animation {
transition: .25s ease-in-out;
transition-property: transform, width;
}
.move {
width: 200px; // set width to 200px
translateX(-200px); // move 200px to the left (always relative)
}
Typically, if you move large images or large DOM Nodes, you will see some stuttering. With CSS transform you get no stuttering.
If you can't use CSS transform (because you need it to work in IE8 or lower) I'd use jQuery's .animate.
Related
The closest solution I found is Show div on scrollDown after 800px.
I'm learning HTML, CSS, and JS, and I decided to try to make a digital flipbook: a simple animation that would play (ie, load frame after frame) on the user's scroll.
I figured I would add all the images to the HTML and then use CSS to "stack them" in the same position, then use JS or jQuery to fade one into the next at different points in the scroll (ie, increasing pixel distances from the top of the page).
Unfortunately, I can't produce the behavior I'm looking for.
HTML (just all the frames of the animation):
<img class="frame" id="frame0" src="images/hand.jpg">
<img class="frame" id="frame1" src="images/frame_0_delay-0.13s.gif">
CSS:
body {
height: 10000px;
}
.frame {
display: block;
position: fixed;
top: 0px;
z-index: 1;
transition: all 1s;
}
#hand0 {
padding: 55px 155px 55px 155px;
background-color: white;
}
.frameHide {
opacity: 0;
left: -100%;
}
.frameShow {
opacity: 1;
left: 0;
}
JS:
frame0 = document.getElementById("frame0");
var myScrollFunc = function() {
var y = window.scrollY;
if (y >= 800) {
frame0.className = "frameShow"
} else {
frame0.className = "frameHide"
}
};
window.addEventListener("scroll", myScrollFunc);
};
One of your bigger problems is that setting frame0.className = "frameShow" removes your initial class frame, which will remove a bunch of properties. To fix this, at least in a simple way, we can do frame0.className = "frame frameShow", etc. Another issue is that frame0 is rendered behind frame1, which could be fixed a variety of ways. ie. Putting frame0's <img> after frame1, or setting frame0's CSS to have a z-index:2;, and then setting frame0's class to class="frame frameHide" so it doesn't show up to begin with. I also removed the margin and padding from the body using CSS, as it disturbs the location of the images. I have made your code work the way I understand you wanted it to, here is a JSFiddle.
It depends on your case, for example, in this jsFiddle 1 I'm showing the next (or previous) frame depending on the value of the vertical scroll full window.
So for my case the code is:
var jQ = $.noConflict(),
frames = jQ('.frame'),
win = jQ(window),
// this is very important to be calculated correctly in order to get it work right
// the idea here is to calculate the available amount of scrolling space until the
// scrollbar hits the bottom of the window, and then divide it by number of frames
steps = Math.floor((jQ(document).height() - win.height()) / frames.length),
// start the index by 1 since the first frame is already shown
index = 1;
win.on('scroll', function() {
// on scroll, if the scroll value equal or more than a certain number, fade the
// corresponding frame in, then increase index by one.
if (win.scrollTop() >= index * steps) {
jQ(frames[index]).animate({'opacity': 1}, 50);
index++;
} else {
// else if it's less, hide the relative frame then decrease the index by one
// thus it will work whether the user scrolls up or down
jQ(frames[index]).animate({'opacity': 0}, 50);
index--;
}
});
Update:
Considering another scenario, where we have the frames inside a scroll-able div, then we wrap the .frame images within another div .inner.
jsFiddle 2
var jQ = $.noConflict(),
cont = jQ('#frames-container'),
inner = jQ('#inner-div'),
frames = jQ('.frame'),
frameHeight = jQ('#frame1').height(),
frameWidth = jQ('#frame1').width() + 20, // we add 20px because of the horizontal scroll
index = 0;
// set the height of the outer container div to be same as 1 frame height
// and the inner div height to be the sum of all frames height, also we
// add some pixels just for safety, 20px here
cont.css({'height': frameHeight, 'width': frameWidth});
inner.css({'height': frameHeight * frames.length + 20});
cont.on('scroll', function() {
var space = index * frameHeight;
if (cont.scrollTop() >= space) {
jQ(frames[index]).animate({'opacity': 1}, 0);
index++;
} else {
jQ(frames[index]).animate({'opacity': 0}, 0);
index--;
}
});
** Please Note that in both cases all frames must have same height.
I'd like an element to do a CSS3 animation once the page is scrolled down enough for it to be visible, and I'm wondering if there's any way to accomplish this. Anything involving JavaScript or CSS would work. I've done many Google searches and Stackoverflow searches and can't find exactly what I need.
Depending on the complexity of your layout, it could be as simple as finding the scroll position, the height of the window, and where the element is on the page.
function scrollEvent() {
var el = document.getElementsByTagName('a')[0];
var body = document.getElementsByTagName('body')[0];
var posY = (window.innerHeight + body.scrollTop) - el.offsetTop;
var onScreen = (posY > 0 && posY < window.innerHeight) ? true : false;
}
window.onscroll = scrollEvent;
Use the same technique if you're worried about horizontal positioning, as well.
It depends on what you want to do specifically. I would look at these resources:
http://daneden.github.io/animate.css/
http://www.w3schools.com/css/css3_animations.asp
http://css-tricks.com/almanac/properties/a/animation/
Put your CSS3 animation style in a class, but don't assign it to your element until it has been scrolled completely into view.
Assuming your element has an id of sprite, this should get you going:
<style>
.animate {
//CSS3 animation style
}
</style>
window.onscroll= function() {
var sprite = document.getElementById('sprite');
if(sprite.getBoundingClientRect().top>=0 && sprite.getBoundingClientRect().bottom<=window.innerHeight) {
sprite.className= 'animate';
}
}
I want to make an image begin scaled all the way down in x in, and then animate all the way up in x when classes are added (via javascript). The pattern that I am using works well for things like rotate, but I am thinking this is only because rotate goes a full 360 degrees. I am not sure why this does not work:
CSS:
.scaleXStart {
visibility: hidden;
z-index: 0;
transform:scaleX(.5);
}
.scaleXEnd {
z-index: 3;
transform: scaleX(2);
transition: transform 1s;
}
Javascript:
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
a.className = 'scaleXEnd';
I would think this would work because it is adding and then remove a class, so the scaleXproperty would be set to 0 and then 1 but this is not working. Thanks for any ideas on why
The problem is, it never gets a chance to get the start class and it goes straight to the end class. Putting the end class change into a timeout (even zero milliseconds!) will trick it into doing the both class changes:
function anim(){
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
setTimeout(function(){a.className = 'scaleXEnd';}, 0)
}
function anim2(){
a = document.querySelector('#myDiv');
a.className = 'scaleXStart';
a.className = 'scaleXEnd';
}
See what I mean here: http://jsfiddle.net/shomz/nzJ8j/
I am looking for a script but I'm not sure what to look for.
I have a webpage that has the body tag with a background image.
body {
background: url(eye.gif)repeat;
background-size:91px 91px;
}
What I am hoping to achieve is when the page loads it shows the background image as 991px then slowly decrease by 10px over a set time until the original size of 91px.
I'm not sure if there is away to do this, or even another way that when the page is loaded it is zoomed in and then zooms out automatically over time.
Basically when the page is loaded you will see the image twice and then over time you will see more and more.
Can anyone point me in the right direction.
if you use background-size your using css3 and so you can use keyframes
no javascript needed.
#-webkit-keyframes bganimation{
0%{background-size:991px 991px;}
100%{background-size:91px 91px;}
}
body{
background: url(eye.gif)repeat;
background-size:91px 91px;
-webkit-animation:bganimation 20s linear; // 20s = 20 seconds
}
for more support you need to add the other specific prefixes (-moz,-ms..)
Here is a sample using JQuery:
http://jsfiddle.net/frUvf/16/
$(document).ready(function(){
$('body').animate({'background-size':'10000px'}, 50000);
})
Using vanilla JS:
var lowerBound = 250,
step = 10,
duration = 1000,
image = document.getElementById('image');
(function resizer () {
if (image.clientWidth > lowerBound) {
image.style.width = image.clientWidth - step + 'px';
var timer = setTimeout(resizer, duration);
} else {
clearTimeout(timer);
}
}());
Just change the lowerBound/step/duration variables to whatever you need them to be.
Fiddle
with jquery:
var body = $('body');
var zoom = 2;
var interval_zoom = 0.5;
var time_interval = 90000;
setInterval(function(){
body.css("zoom", zoom);
zoom = zoom - interval_zoom;
if(zoom<=1)
clearTimeout(this);
}, time_interval )
Zoom and interval must be calculated
You could use Javascript for the animation or could take a look at CSS3 Transformations: http://web.archive.org/web/20180414114433/http://www.pepe-juergens.de/2013/02/css3-transform/
I am creating a header that acts like the Chrome for Android Address bar. The effect is that the header is a pseudo sticky header that scrolls out of view as you scroll down and then you you begin to scroll back up the header scrolls back into view.
Right now it works fine on the desktop (around 60fps) but on Chrome for Android (on Nexus 7 2013) it is full of jank.
Demo: jsFiddle
Both the header and content area are moved with transform translateY which are more performant than pos:top
I am also using requestAnimationFrame to debounce scrolling and only change properties when it is most convenient for the browser.
The header is position: fixed; top: 0; and then scrolled in and out of view with transform: translateY(...);. Also instead of using margin-top to get the content out from underneath the header, I am using transform: translateY(...);
The basic structure of my js looks like:
var latestScrollTop = 0;
var lastReactedScrollTop = 0;
var ticking = false;
function DoScroll()
{
var builtUpScrollTop = latestScrollTop - lastReactedScrollTop;
// Fold the top bar while we are scrolling (lock it to scrolling)
$('header.main-header').css('transform', 'translateY(' ... 'px)');
HeaderHeightChange();
lastReactedScrollTop = latestScrollTop;
ticking = false;
}
function HeaderHeightChange()
{
// We need to update the margin-top for the content so we don't overlap it
$('main.content-area').css('transform', 'translateY(' ... 'px)');
}
function requestTick() {
if(!ticking) {
requestAnimationFrame(function(){
DoScroll();
});
}
ticking = true;
}
$(window).on('scroll', function(e) {
latestScrollTop = $(window).scrollTop();
requestTick();
});
The effect is not complete as it needs to resolve the fold after you finish scrolling (and is coded) but I do not want to complicate the issue when just the scroll movement lock to header is causing jank. I see paint rectangles when scrolling up and down even though I am changing transform which I assume the gpu is handling and shouldn't be painting.
Edit: It seems when debugging with ADB that there is a a bunch of clear grey outlined time in each frame.
Turns out that even though I was using transform: translateY() that you still need to add translateZ(0) to see the benefit of layers and having it gpu accelerated.
But I did also update my code to use a object literal code style and got rid of the forced synchronous layout warning in the timeline by reading then writing. This is coupled along with requestAnimationFrame.
Demo: jsFiddle
var myUtils = {
clamp: function(min, max, value) {
return Math.min(Math.max(value, min), max);
},
getTranslateYFromTransform: function(rawTransform) {
return parseFloat(rawTransform.match(/^matrix\((([+-]?[0-9]*\.?[0-9]*),\s*?){5}([+-]?[0-9]*\.?[0-9]*)\)$/)[3])
}
};
var scrollHeader = {
latestScrollTop: 0,
lastReactedScrollTop: 0,
headerHeight: 0,
headerTransformTranslateY: 0,
ticking: false,
requestTick: function() {
if(!scrollHeader.ticking) {
requestAnimationFrame(function(){
scrollHeader.doHeaderFold();
});
}
scrollHeader.ticking = true;
},
doHeaderFold: function() {
var header = $('header.main-header');
var builtUpScrollTop = scrollHeader.latestScrollTop - scrollHeader.lastReactedScrollTop;
scrollHeader.headerHeight = header.outerHeight();
scrollHeader.headerTransformTranslateY = myUtils.clamp(-parseInt(scrollHeader.headerHeight), 0, (myUtils.getTranslateYFromTransform(header.css('transform')) - builtUpScrollTop));
// Fold the top bar while we are scrolling (lock it to scrolling)
header.css('transform', 'translateY(' + scrollHeader.headerTransformTranslateY + 'px) translateZ(0)');
scrollHeader.headerHeightChange();
scrollHeader.lastReactedScrollTop = scrollHeader.latestScrollTop;
scrollHeader.ticking = false;
},
headerHeightChange: function() {
// We need to update the margin-top for the content so we don't overlap it
$('main.content-area').css('transform', 'translateY(' + (scrollHeader.headerHeight + scrollHeader.headerTransformTranslateY) + 'px) translateZ(0)');
}
};
$(window).on('scroll', function(e) {
//console.log(e);
scrollHeader.latestScrollTop = $(window).scrollTop();
scrollHeader.requestTick();
});
This makes the timeline debugging on ADB (Nexus 7 2013) look like(very smooth):
Also to get rid of a small jump when first scrolling add transform: translateZ(0) to your element before animating it.