jQuery background-position animation to run more smoothly - javascript

I have a pattern background animation in jQuery made like this.
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
y--;
}, 150);
Live example http://codepen.io/anon/pen/emMxXa
But it is rather 'jittery'.
How can I make something like this to run more smoothly and slowly.

You want to use and look into CSS transitions and CSS animations for real smoothyness.
-webkit-animation: move 30s linear 0s infinite alternate;
-moz-animation: move 30s linear 0s infinite alternate;
#-webkit-keyframes move {
from { background-position: 0px 0px } to { background-position: 0px 400px }
}
#-moz-keyframes move {
from { background-position: 0px 0px } to { background-position: 0px 400px }
}
Demo: http://codepen.io/anon/pen/EaEMvy

Take a look at this jsFiddle.
You will see that by reducing the interval value from 150 to 30 it will be a lot smoother:
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
y--;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
}, 30);
You can lower it even more but the more you lower it, the faster it gets also.

have a look at https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
(function($) {
var x = 0;
var y = 0;
//cache a reference to the banner
var banner = $("#banner");
// set initial banner background position
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
function step() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
y--;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step);
})(jQuery);

Related

Mouse move with hover using JavaScript

Am trying to achieve this (built using webflow) animation and interaction when hovering on an element but am not able to do so. I've found this answer here but when I tried to refactor it with on hover function I still couldn't make it work.
Here's what I've tried.
// Maximum offset for image
var maxDeltaX = 50,
maxDeltaY = 50,
viewportWidth = 0,
viewportHeight = 0,
mouseX = 0,
mouseY = 0,
translateX = 0,
translateY = 0;
// Bind mousemove event to document
jQuery('.image-content-right').on('mousemove', function(e) {
// Get viewport dimensions
viewportWidth = document.documentElement.clientWidth,
viewportHeight = document.documentElement.clientHeight;
// Get relative mouse positions to viewport
// Original range: [0, 1]
// Should be in the range of -1 to 1, since we want to move left/right
// Transform by multipling by 2 and minus 1
// Output range: [-1, 1]
mouseX = e.pageX / viewportWidth * 2 - 1,
mouseY = e.pageY / viewportHeight * 2 - 1;
// Calculate how much to transform the image
translateX = mouseX * maxDeltaX,
translateY = mouseY * maxDeltaY;
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
}).hover(function() {
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
})
It's a little bit clunky and not as smooth as what I want to achieve and also I would want it to go back to its original position when not hovered.
I'm not too sure how you'd really do much more to make that function a little smoother really considering its really depending on how often jQuery itself would execute its events. For now maybe I'd consider splitting all the code within your jQuery event declaration into their own respective functions. It'll be a lot easier and cleaner for you to work on :)
function animateElementOnMouseMove() {
// your translate code
}
function animateElementOnMouseHover() {
// your initial hover animation code
}
$('.image-content-right').on('mousemove', animateElementOnMouseMove)
.on('hover', animateElementOnMouseHover);
for it to return back to the position you had it at before you could either save an original untranslated position of each of the elements OR, you could save each of the translations into a count variable, then "undo" the translations after the element has become unfocused.
like:
var elementTranslateCountX = 0;
var elementTranslateCountY = 0;
// ON TRANSLATE
elementTranslateCountX += translateX;
elementTranslateCountY += translateY;
By the looks and feel of the webflow thing (if I understand your goal correctly) you want to be able to move your object by the full maxDeltaX/Y within the hover area. If that's the case, your math needs some adjustments: you need to define an origin (the center of the moving object most likely) and normalize to [-1, 1] the hover area around the origin. Placing the object in the dead center of the hover box simplifies calculations. I'm posting the code in a snippet, but it should be run on a full page because the coordinates are not correctly calculated. Funnily enough, if I run it on codepen, it works as expected on Chrome, but not on Safari. To avoid this issue you should wrap everything in a parent div and calculate coordinates relative to it
const page = document.getElementById("page-id");
const area = document.getElementById("area-id");
const box = document.getElementById("box-id");
// we want to move the object by 50px at most
const maxDeltaX = 50;
const maxDeltaY = 50;
let pageBox = page.getBoundingClientRect();
let pageTopLeft = {
x: pageBox.x,
y: pageBox.y
};
let areaBox = area.getBoundingClientRect();
let areaRange = {
w: areaBox.width / 2.0,
h: areaBox.height / 2.0
};
let boxBox = box.getBoundingClientRect();
let transformOrigin = {
x: boxBox.x + (boxBox.width / 2.0),
y: boxBox.y + (boxBox.height / 2.0)
};
// multipliers allow the full delta displacement within the hover area range
let multX = maxDeltaX / areaRange.w;
let multY = maxDeltaY / areaRange.h;
area.addEventListener("mousemove", onMove);
area.addEventListener("mouseleave", onLeave);
window.addEventListener("resize", onResize);
// mouse coords are computed wrt the wrapper top left corner and their distance from the object center is normalized
function onMove(e) {
let dx = (((e.clientX - pageTopLeft.x) - transformOrigin.x));
let dy = (((e.clientY - pageTopLeft.y) - transformOrigin.y));
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0)";
/*
// or you can add some fancy rotation as well lol
let rotationDeg = Math.atan2(dy,dx) * (180/Math.PI);
let rotationString = "rotate(" + rotationDeg + "deg)";
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0) " + rotationString;
*/
}
function onLeave(e) {
box.style.transform = "translate3d(0, 0, 0)";
}
function onResize(e) {
// redefine all the "let" variables
}
* {
margin: 0;
padding: 0;
}
.page {
position: relative;
width: 100%;
height: 100vh;
background-color: #ddd;
display: grid;
place-items: center;
}
.hover-area {
position: relative;
width: 50%;
height: 100%;
background-color: #888;
}
.box {
position: absolute;
left: calc(50% - 25px);
top: calc(50% - 25px);
width: 50px;
height: 50px;
border-radius: 25px;
background-image: linear-gradient(45deg, #000, #aaa);
transform: translate3d(0, 0, 0);
transition: all 0.2s;
will-change: transform;
}
<div id="page-id" class="page">
<div id="area-id" class="hover-area">
<div id="box-id" class="box" />
</div>
</div>
Note that is runs smoother on Chrome than on Safari. I'm not sure divs and css is the best way to go here for performance.
If I misunderstood the final result, explain more and I'll try to help.

on scroll div gradually fadein fadeout

I am trying to creating fadein fadeout effect with scrolling but not figure out how . it is like http://www.popsci.com/ . There If scroll then background image (div id fixed-image ) get fading. Them code something like. But I cannot figure out yet how to easily apply in my code. Would guys please check it my code .
var opacity = 1;
var img = element.find('img');
// Look for featured stories.
if (element.length > 0) {
// Attach background image element.
$('#page-wrapper').prepend('<div id="fixed-image" style="background-image: url(\'' + img.attr('data-lgsrc') + '\');"></div>');
img.addClass('hidden');
var scrollPercent;
$(window).scroll(function() {
// When User scrolls down, determine percentage from top to bottom of window.
scrollPercent = (($(window).scrollTop() / $(window).height() * 1.9) - 0.9);
if (scrollPercent < 0) {
$('#fixed-image').css({
'-webkit-filter' : 'blur(0px)',
'-moz-filter' : 'blur(0px)',
'-o-filter' : 'blur(0px)',
'-ms-filter' : 'blur(0px)',
'filter' : 'blur(0px)'
});
}
var opacityCount = 1.5 - Math.min(1.5, (scrollPercent + 1));
$('#fixed-image').css('opacity', opacityCount);
if (scrollPercent <= 1) {
$('#fixed-image').css('opacity', opacityCount);
$('#fixed-image').css({
'-webkit-filter' : 'blur(' + scrollPercent * 10 + 'px)',
'-moz-filter' : 'blur(' + scrollPercent * 10 + 'px)',
'-o-filter' : 'blur(' + scrollPercent * 10 + 'px)',
'-ms-filter' : 'blur(' + scrollPercent * 10 + 'px)',
'filter' : 'blur(' + scrollPercent * 10 + 'px)'
});
}
else {
$('.content-wrapper-outer').css('background-color', 'rgba(255,255,255,' + opacity + ')');
my demo is here https://jsfiddle.net/cyber007/6e6dbr6j/1/
i want slider class will be fadeIn and fadeout gradually base on scrolling
UPDATE
$(window).scroll(function(){
$(".slider").css("opacity", 1 - $(window).scrollTop() / 250);
});
https://jsfiddle.net/cyber007/6e6dbr6j/2/ this one work fine. jus one minor think in console i saw that opacity value keep running after 0 even going - value. i don't think after value 0 no need down value more
Here's a function to clip opacity at 0, although according to MDN: "Any value outside the interval [0 to 1], though valid, is clamped to the nearest limit in the range," so it's not strictly necessary.
$(window).scroll(function(){
var opacity = 1 - $(window).scrollTop() / 250;
if (opacity < 0 ) opacity = 0;
$(".slider").css("opacity", opacity);
});
UPDATE
For arbitrary start and end of your transition, use the linear equation , like so:
$(window).scroll(function(){
var start = 200, end = 600;
var opacity = 1 - ($(window).scrollTop() - start) / (end - start);
if (opacity > 1 ) opacity = 1;
if (opacity < 0 ) opacity = 0;
$(".slider").css("opacity", opacity);
});
Here's a JSFiddle.

How to stop the background-position change when the background image reaches bottom?

Javascript:
(function($) {
var x = 0;
var y = 0;
//cache a reference to the banner
var banner = $("#banner");
// set initial banner background position
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
y--;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
}, 90);
})(jQuery);
CSS:
div#banner {
width: 960px;
height: 200px;
margin: auto;
background: url(../images/fotogrph-skyscraping.jpg) repeat 0 0;
}
HTML:
<div id="banner"></div>
I can not get the position change to stop when the background image reaches the bottom.
http://jsfiddle.net/naeemshaikh27/u0x15oj3/
calculate the real height of the image.
subtract the height of the div shown, this is the threshold.
clear the interval when this threshold is reached
Do you mean something like this:
(function($) {
var x = 0;
var y = 0;
//cache a reference to the banner
var banner = $("#banner");
// set initial banner background position
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
var timer = window.setInterval(function() {
if(y > -200){
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
y--;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
} else {
clearInterval(timer);
}
}, 90);
})(jQuery);

Image Rotation with Fancybox

I'm creating an interface that allows users to rotate images 90 degrees counter clockwise. I rotate the image on the page using jquery and -webkit-transform, but I also want to update the preview of image in the Fancybox slideshow.
I tried rotating the image by doing the following:
$(".fancybox").fancybox({
afterShow: function(){
fancyboxRotation();
}
});
function fancyboxRotation(){
$('.fancybox-wrap').css('webkitTransform', rotate(-90deg));
$('.fancybox-wrap').css('mozTransform', rotate(-90deg));
}
But that ended up rotating the controls as well (and also placed the close button on the top left instead of the top right):
If I just apply the rotation to the image, the white border around it has the wrong orientation:
Anyone have experience applying transformations to a fancybox image?
For fancybox 3 here is what I came up with. It uses font awesome icons, you can replace with glyphicons or whatever else you choose.
//adding custom item to fancybox menu to rotate image
$(document).on('onInit.fb', function (e, instance) {
if ($('.fancybox-toolbar').find('#rotate_button').length === 0) {
$('.fancybox-toolbar').prepend('<button id="rotate_button" class="fancybox-button" title="Rotate Image"><i class="fa fa-repeat"></i></button>');
}
var click = 1;
$('.fancybox-toolbar').on('click', '#rotate_button', function () {
var n = 90 * ++click;
$('.fancybox-image-wrap img').css('webkitTransform', 'rotate(-' + n + 'deg)');
$('.fancybox-image-wrap img').css('mozTransform', 'rotate(-' + n + 'deg)');
});
});
You can rotate the outer most div in the fancy box content, In my case it's fancybox-skin(fancybox v2 )
afterShow: function(){
var click = 1;
$('.fancybox-wrap').append('<div id="rotate_button"></div>')
.on('click', '#rotate_button', function(){
var n = 90 * ++click;
$('.fancybox-skin').css('webkitTransform', 'rotate(-' + n + 'deg)');
$('.fancybox-skin').css('mozTransform', 'rotate(-' + n + 'deg)');
});
};
With help from #Ashik I finally got this working and did not have to give up showing the title since I instead rotate .fancybox-inner and overwrite some CSS so I can keep the white border. I also initialize the fancybox from the $(document).ready() function so I had to bind the button a little different.
Finally, this is kind of a long answer so let me know if I left something out since it is entirely possible! Also, we do not need to support IE (thank the lord), so it may or may not work correctly there.
I went ahead and removed the regular arrow and close buttons so they would stay put at the top. This requires that you add the fancy box button helper CSS and JS files:
<link href="/Content/css/jquery.fancybox.css" rel="stylesheet"/>
<link href="/Content/css/jquery.fancybox-buttons.css" rel="stylesheet"/>
<script src="/Content/Scripts/fancybox/jquery.fancybox.js"></script>
<script src="/Content/Scripts/fancybox/jquery.fancybox.pack.js"></script>
<script src="/Content/Scripts/fancybox/jquery.fancybox-buttons.js"></script>
Then initializing fancy box is being done from $(document).ready(), as I said, like below (notice I remove the arrows and close buttons and add them back in using the button helper's tpl property). In that tpl property I also create a custom rotation button with an onclick and a custom data-rotation property which will hold the current rotation:
$(document).ready(function() {
$(".fancybox").fancybox({
loop : true,
helpers: {
buttons: {
position: 'top',
tpl : '<div id="fancybox-buttons"><ul><li><a class="btnPrev" title="Previous" href="javascript:;"></a></li><li><a id="fancybox-rotate-button" title="Rotate" data-rotation="0" onclick="FancyBoxRotateButton()"></a></li><li><a class="btnNext" title="Next" href="javascript:;"></a></li><li><a class="btnClose" title="Close" href="javascript:jQuery.fancybox.close();"></a></li></ul></div>'
}
},
closeBtn: false, // you will use the tpl buttons now
arrows : false // you will use the tpl buttons now
});
Here is the custom rotation button's onclick function:
window.FancyBoxRotateButton = function() {
var fancyboxInner = $('.fancybox-inner');
var fancyBoxRotateButton = $('#fancybox-rotate-button');
var currentRotation = parseInt(fancyBoxRotateButton.data("rotation"));
var rotation = 'rotate(-' + (90 * ++currentRotation) + 'deg)';
fancyboxInner.css({
'-moz-transform' : rotation,
'-webkit-transform': rotation,
'-o-transform' : rotation,
'transform' : rotation
});
fancyBoxRotateButton.data("rotation", currentRotation.toString());
}
Last but not least we need to fix the white border and then I also change the size of the custom button ul and set my custom rotation button's picture. There is probably better ways to do this (if you know of one let me know!) but I simply removed .fancybox-skin's background and box shadow and added it to .fancybox-inner:
#fancybox-buttons ul{
width: 130px;
}
#fancybox-buttons #fancybox-rotate-button {
background-image: url('/Content/images/fancybox_rotate.png')
}
.fancybox-skin {
background: none !important;
}
.fancybox-opened .fancybox-skin {
-webkit-box-shadow: none !important;
-moz-box-shadow : none !important;
box-shadow : none !important;
}
.fancybox-inner {
border-radius : 4px;
border : 2px solid white;
padding : 10px;
background : white none repeat scroll 0 0;
-webkit-box-shadow: 0 10px 25px #000000;
-webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
-moz-box-shadow : 0 10px 25px #000000;
-moz-box-shadow : 0 10px 25px rgba(0, 0, 0, 0.5);
box-shadow : 0 10px 25px #000000;
box-shadow : 0 10px 25px rgba(0, 0, 0, 0.5);
}
Hope it helps someone!
You can rotate your image by applying the css style -webkit-transform: rotate(-90deg); only to the '.fancybox-inner' class element instead of the '.fancybox-wrap' so that it will rotate only the image and not the whole container that includes the controls and descriptions.
My solution was to remove the arrows and close button from the fancybox and fade in the fancybox slideshow after the rotation was applied to the entire fancybox-wrap. To do this, I set the display of the fancybox-wrap to none in the "beforeShow", and then on "AfterShow", I fade in the image. Here's my code:
$(".fancybox").fancybox({
helpers: {
overlay: {
locked: false
}
},
arrows: false,
closeBtn: false,
beforeShow: function(){
if($('.fancybox-image').length>0){
$('.fancybox-wrap').css('display', 'none');
var imageID = getFancyboxImageID();
var rotation = getFancyboxRotation(imageID);
if(rotation!=0){
fancyboxRotate(rotation);
}
}
},
afterShow: function(){
if($('.fancybox-image').length>0){
$('.fancybox-wrap').fadeIn();
}
}
});
I wanted to keep the close button and the caption so I did a little magic trick with CSS3 transform:
afterShow: function() {
var click = 0, deg;
$('.fancybox-inner')
.append('<img id="rotate_button" src="https://cdn0.iconfinder.com/data/icons/super-mono-sticker/icons/button-rotate-cw_sticker.png" title="Rotate 90° CW">')
.on('click', '#rotate_button', function() {
click = (++click % 4 === 0) ? 0 : click;
deg = 90 * click;
$('.fancybox-wrap').css('transform', 'rotate(' + deg + 'deg)');
$('#rotate_button').css('transform', 'rotate(-' + deg + 'deg)');
sessionStorage.setItem('prev_rotated_image', $('.fancybox-image').prop('src'));
sessionStorage.setItem($('.fancybox-image').prop('src'), deg);
// move the close button and rotate the label
switch (deg) {
case 90:
$('.fancybox-close').css('transform', 'translate(-' + $('.fancybox-wrap').width() + 'px, 0px)');
$('.fancybox-title').find('span.child').css('transform', 'translate(' + ($('.fancybox-wrap').width() / 2 + $('.fancybox-title').height() / 2 + 8) + 'px, -' + ($('.fancybox-wrap').height() / 2) + 'px) rotate(-' + deg + 'deg)');
break;
case 180:
$('.fancybox-close').css('transform', 'translate(-' + $('.fancybox-wrap').width() + 'px, ' + $('.fancybox-wrap').height() + 'px)');
$('.fancybox-title').find('span.child').css('transform', 'translate(0px, -'+ ($('.fancybox-wrap').height() + $('.fancybox-title').height() + 16) +'px) rotate(-' + deg + 'deg)');
break;
case 270:
$('.fancybox-close').css('transform', 'translate(0px, ' + $('.fancybox-wrap').height() + 'px)');
$('.fancybox-title').find('span.child').css('transform', 'translate(-' + ($('.fancybox-wrap').width() / 2 + $('.fancybox-title').height() / 2 + 8) + 'px, -' + ($('.fancybox-wrap').height() / 2) + 'px) rotate(-' + deg + 'deg)');
break;
case 0:
case 360:
default:
$('.fancybox-close').css('transform', 'translate(0px, 0px)');
$('.fancybox-title').find('span.child').css('transform', 'translate(0px, 0px) rotate(0deg)');
}
});
}
Thanks, #Paul for a great snippet, I have added some class changes (current slide) and CSS property that worked for my version of fancybox3. Might help someone.
Note: you can replace the "Rotate" text with an icon.
//adding custom item to fancybox menu to rotate image
$(document).on('onInit.fb', function (e, instance) {
if ($('.fancybox-toolbar').find('#rotate_button').length === 0) {
$('.fancybox-toolbar').prepend('<button id="rotate_button" class="fancybox-button" title="Rotate Image">Rotate</button>');
}
var click = 1;
$('.fancybox-toolbar').on('click', '#rotate_button', function () {
var n = 90 * ++click;
$('.fancybox-slide--current img').css('webkitTransform', 'rotate(-' + n + 'deg)');
$('.fancybox-slide--current img').css('mozTransform', 'rotate(-' + n + 'deg)');
$('.fancybox-slide--current img').css('transform', 'rotate(-' + n + 'deg)');
});
});
See this issue on Github: https://github.com/fancyapps/fancybox/issues/1100
Code of user seltix5 is working in fancybox v3. I tested it myself.
You just need to append his/her code to your fancybox.js file. It simply extends fancybox object with rotate functionality after event "onInit.fb". It also adds rotate buttons to top menu and smooth rotate animation.

Rotating DIV spins slowly on second click

I think I overlooked something. This is a very simple spin-the-bottle game.
Javascript/jQuery
$('.bottle').on('click', function(e) {
this.removeAttribute('style');
var deg = 3000 + Math.round(Math.random() * 500);
var css = '-webkit-transform: rotate(' + deg + 'deg);';
this.setAttribute(
'style', css
);
});
CSS:
.bottle {
width: 200px;
height: 200px;
background-image: url(img/bottle.png);
-webkit-transition: -webkit-transform 6s ease-out;
}
HTML:
<div class="bottle"></div>
This works perfectly on the first click of the bottle. But starting from the second click, the spin is very very slow?
Try this : http://jsfiddle.net/sMcAN/
var i = 1;
$('.bottle').on('click', function(e) {
this.removeAttribute('style');
var deg = 3000 + Math.round(Math.random() * 500);
deg = ((-1) ^ i) * deg;
var css = '-webkit-transform: rotate(' + deg + 'deg);';
this.setAttribute('style', css);
i++;
});​
Another update : http://jsfiddle.net/sMcAN/2/
This is because at first, you are going from 0 to a value over 3000. But then, the value is always within 3000 - so the difference is not big enough and it still takes the 6 seconds you have defined.
One solution would be to make sure that you offset the value and make it different by few thousand each time.
var i = 0, offset = [2000, 4000, 6000, 3000, 5000, 1000];
$('.bottle').on('click', function(e) {
this.removeAttribute('style');
var deg = offset[i] + Math.round(Math.random() * 500);
i++;
if (i > 5) {
i = 0;
}
var css = '-webkit-transform: rotate(' + deg + 'deg);';
this.setAttribute(
'style', css
);
});​
math.round(math.random() * 1000);
Try that

Categories