Can I get some help with animating elements on a canvas? I would like to get the elements already drawn on the canvas and "move" them off the canvas and display the new elements. My functions are in javascript and work nicely. I would just like to add animation. TIA.
just have an event handler control the left of the style for that canvas's id and then specify a loop to have it change or move to the left/right based on time.
(function() {
var speed = 10,
movePic = function(moveBy) {
var el = document.getElementById("animationStyle"),
left = el.offsetLeft
if ((moveBy > 0 && left > 399) || (moveBy < 0 && left < 51)) {
clearTimeout(timer);
timer = setInterval(function () {
movePic(moveBy * -1);
}, speed);
}
el.style.left = left + moveBy + "px";
};
var timer = setInterval(function () {
movePic(3)
}, speed);
}());
This would move it back and forth, this is an example that should help you get started.
Related
I used this jquery to move a div by every click.
$(document).ready(function(){
$('#hero').click(function(){
$(this).animate({
left: '+=50px'
},300);
})
})
I'd like to avoid jquery whenever it is possible to get deeper into pure JS.
Is there anyway to achieve the same effect without using jquery?
I know that this will be more complex, but just trying to learn.
you can use the same technique in javascript:
// get the object refrence
var hero_obj = document.getElementById('hero');
// attach the onclick event
hero_obj.onclick = function(){
this.style.left = ( parseInt(this.style.left, 10) + 50 ) + 'px'
};
However, the effect won't be as smooth as jquery
I've stumbled upon this gem on vanilla-js.com a few weeks ago:
var s = document.getElementById('thing').style;
s.opacity = 1;
(function(){(s.opacity-=.1)<0?s.display="none":setTimeout(arguments.callee,40)})();
I really like the simplicity and the size of the code. Elegant and efficient!
I've created a function that affects the left property of an element of your choice based on the code above:
/* element: DOM element such as document.getElementById('hero')
distance: distance in pixels to move to the left such as 50 or 100 */
function moveBy(element, distance){
var target = isNaN(parseInt(s.left)) ? distance : parseInt(s.left) + distance;
(function(){
s.left = isNaN(parseInt(s.left)) ? '1px' : (parseInt(s.left) + 1).toString() + 'px';
if(parseInt(s.left) <= distance) setTimeout(arguments.callee, 40);
})();
}
You can play around and see what fits to your liking in terms of speed and smoothness. Try it here on a jsfiddle.
/* So you go: */
moveBy(document.getElementById('hero'), 50);
/* Or you can bind it to an event */
document.getElementById('hero').addEventListener('click', function(event){
moveBy(this, 50);
});
What a solution like this would need if you're willing to make it better is to replace the left property by translate. As Paul Irish states on his blog, translate provides way better performance than moving elements around with TRBL (top-left-bottom-right). Some sort of easing functions could be added as well to smooth things out.
Here's a code with animation. This snippet is only for modern browsers, but it is easy to modify to work with older browsers (IEs) too. (Actually only attachment of the event needs to be fixed.)
window.onload = function () {
var timer, k, intervals, kX, kY,
counter = 0,
hero = document.getElementById('hero'),
posX = hero.offsetLeft,
posY = hero.offsetTop,
anim = function (elem, params) {
posX += kX;
posY += kY;
elem.style.left = posX + 'px';
elem.style.top = posY + 'px';
if (counter > intervals) {
clearInterval(timer);
counter = 0;
} else {
counter++;
}
return;
},
move = function (elem, params) {
if (timer) {
clearInterval(timer);
counter = 0;
}
k = Math.atan2(params.left, params.top);
kX = Math.sin(k);
kY = Math.cos(k);
intervals = Math.floor(Math.sqrt(Math.pow(params.left, 2) + Math.pow(params.top, 2)));
timer = setInterval(function () {
anim(elem, params);
return;
}, params.speed);
return;
};
document.getElementById('hero').addEventListener('click', function (e) {
move(e.currentTarget, {left: 50, top: 0, speed: 0});
return;
}, false);
return;
}
As you can see, with this code you can also move elements vertical and adjust speed. To switch direction, just add - to corresponding property. The code is using pixels only as units, but that's easy to modify if needed.
It's also easy to convert this functional code to an object. Also jQuery-like duration can be added by passing property params.duration instead of params.speed and doing some advanced calculations with that and kX, kY.
Working demo at jsFiddle
I needed to create an animation solution with easing a while back without using a framework.
The tricky part for me was coping with interrupting/restarting animations part way through when they are tied to user interactions. I found that you can run into trouble pretty quickly if your animations double-fire.
Here is on github: https://github.com/robCrawford/js-anim
There are a few supporting functions but here's the main animation:
function animate(el, prop, to, pxPerSecond, easing, callback){
/**
* Animate style property
* i.e. animate(div1, "width", 1100, 1000, "out", function(){console.log('div1 anim end')});
*
* #param el DOM element
* #param prop Property to animate
* #param to Destination property value
* #param pxPerSecond Speed of animation in pixels per second
* #param easing (optional) Easing type: "in" or "out"
* #param callback (optional) Function to call when animation is complete
*/
var frameDur = 10,
initPropVal = parseInt(getCurrCss(el, prop)),
distance = Math.abs(to-initPropVal),
easeVal = (easing==="in")?1.5:(easing==="out")?0.5:1, // >1 ease-in, <1 ease-out
elAnimData = getData(el, 'animData');
//Quit if already at 'to' val (still fire callback)
if(initPropVal===to){
if(callback)callback.call();
return;
}
//Init animData for el if first anim
if(!elAnimData){
elAnimData = {};
setData(el, {'animData':elAnimData});
}
//Get data for prop being animated or create entry
var animDataOb = elAnimData[prop];
if(!animDataOb)animDataOb = elAnimData[prop] = {};
//Don't re-initialise an existing animation i.e. same prop/to
if(animDataOb.to === to)return;
animDataOb.to = to; //Store 'to' val
//Clear any exisiting interval
if(animDataOb.intId){
clearInterval(animDataOb.intId);
animDataOb.intId = null;
}
//Create new anim
animDataOb.intId = (function(animDataOb){
var totalSteps = Math.round((distance/pxPerSecond)/(frameDur*.001)),
thisStep = 0;
return setInterval(function(){
var newVal = easeInOut(initPropVal, to, totalSteps, thisStep++, easeVal);
if(!isNaN(newVal))el.style[prop] = newVal + "px"; //Allow 0
if(thisStep > totalSteps)endAnim(animDataOb, callback);
}, frameDur);
})(animDataOb);
}
function endAnim(animDataOb, callback){
//End anim
clearInterval(animDataOb.intId);
animDataOb.intId = animDataOb.to = null;
if(callback)callback.call();
}
I have to repair bottom slider on http://rhemapress.pl/www_wopr/ . If you see when you click right arrow twice, then animation back to start and animate again. Here when i click one on right arrow time this should be blocked and not possible to click second time.
Numer of moves right is created dynamicly by checkWidth();
function checkWidth() {
var elements = $('.items').children().length;
var width = Math.ceil(elements / 5) * 820;
return width;
}
This return realWidth witch is something like limit of offset. Variable offset is setted to 0 at start. So, if i click right, then in method moveRight() is checked if element can be moved and it's move. At end offset is increment by 820px (one page of slider), so if we've got 2 pages, then next move can't be called. But it is and this is problem! :/
My code
<script type="text/javascript">
$(document).ready(function() {
$('a.prev').bind('click',moveLeft);
$('a.next').bind('click',moveRight);
var realWidth = checkWidth();
realWidth -= 820;
var offset = 0;
function moveLeft(e) {
var position = $('.items').position();
var elements = $('.items').children().length;
if ((elements > 5) && ((offset - 820) >= 0) ) {
$('.items').animate({
'left': (position.left + 820)
}, 300, function() {
offset -= 820;
});
}
}
function moveRight(e) {
var position = $('.items').position();
var elements = $('.items').children().length;
if ((elements > 5) && ((offset + 820) <= realWidth)) {
$('.items').animate({
'left': (position.left - 820)
}, 300, function() {
offset += 820;
});
}
}
function checkWidth() {
var elements = $('.items').children().length;
var width = Math.ceil(elements / 5) * 820;
return width;
}
});
</script>
How can i do this correctly?
It seems like you want to prevent the click event from firing before the current animation is complete. You can do this by preventing the rest of the function from executing if the element is currently being animated:
function moveLeft(e) {
if ($(this).is(":animated")) {
return false;
}
I have created a parallax scroll, which seem to be working fine in firefox however in the chrome browser there's a slight jump on the body text when scrolling. click here scroll to the about section. I am not sure if t this is a css or JS issue.. below is a snippet i have incorporated into my parallax function
Does anyone know how i an fix this issue?
$(document).ready(function(){
// Cache the Window object
$window = $(window);
// Cache the Y offset and the speed of each sprite
$('[data-type]').each(function() {
$(this).data('offsetY', parseInt($(this).attr('data-offsetY')));
$(this).data('Xposition', $(this).attr('data-Xposition'));
$(this).data('speed', $(this).attr('data-speed'));
});
// For each element that has a data-type attribute
$('[data-type="background"]').each(function(){
// Store some variables based on where we are
var $self = $(this),
offsetCoords = $self.offset(),
topOffset = offsetCoords.top;
// When the window is scrolled...
$(window).scroll(function() {
// If this section is in view
if ( ($window.scrollTop() + $window.height()) > (topOffset) &&
( (topOffset + $self.height()) > $window.scrollTop() ) ) {
// Scroll the background at var speed
// the yPos is a negative value because we're scrolling it UP!
var yPos = -($window.scrollTop() / $self.data('speed'));
// If this element has a Y offset then add it on
if ($self.data('offsetY')) {
yPos += $self.data('offsetY');
}
// Put together our final background position
var coords = '50% '+ yPos + 'px';
// Move the background
$self.css({ backgroundPosition: coords });
$('[data-type="scroll-text"]', $self).each(function() {
var $text= $(this);
var pos = ($window.scrollTop()/10) * $text.data('speed');
var curP = $text.css('margin-top');
var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if(is_chrome) {
$text.animate({
paddingTop: pos,
}, 200, 'linear', function() {
// Animation complete.
});
} else {
$text.css('padding-top', pos);
}
});
}; // in view
}); // window scroll
}); // each data-type
}); // document ready
Some suggestions:
1.) Use position: fixed to avoid any jitter, as you'll be taking the element out of the document flow. You can then position it using z-index.
2.) Cache as much as you can to ease processing time.
3.) Math.round may not be necessary, but try adding this CSS to your moving areas: -webkit-transform: translate3d(0,0,0); This will force hardware acceleration in Chrome, which may ease some of the jittering. (It looked smoother on my screen when I added this with Inspector, but it didn't get rid of the jumpiness with the scroll wheel.) Note: Don't do this on your entire document (e.g. body tag), as it might cause some issues with your current layout. (Your navigation bar didn't stick to the top of the window, for instance.)
4.) If you have any animations running as part of your parallax logic (tweening the margin into place or something along those lines), remove it - that would probably cause the jump you see.
Hope this helps. Best of luck.
I see the same jittering in FireFox and Chrome (Mac). Looking at your containers, one thing that's glaring at me is the pixel position that's being calculated/used.
Chrome: <div id="about-title" style="margin-top: 1562.3999999999999px;">
FireFox: <div id="about-title" style="margin-top: 1562.4px;">
Browsers aren't going to allow content to sit at 1/2 pixel, let alone 0.3999999 of a pixel. I think it's moving it, and trying to calculate whether to round up or round down. It jitters because it's calculating with every click of your mouse wheel.
Thus, I'd try adding Math.round() to your positions so that the containers are never being left in limbo.
Take a look at the code here: http://webdesigntutsplus.s3.amazonaws.com/tuts/338_parallax/src/index.html
Firebug some of the elements, and you'll see that their only fraction of a pixel is '0.5'. Most of them (the bulk) go to round number values.
You are going to have to change the way that the scrolling works (i.e. change how the spacing is computed), but this can be fixed by adding the position:fixed CSS element to the page elements that are scrolling. The problem is coming from the time that it takes for the JavaScript to process and then render.
For example, on your page you would set each of the <div> tags containing text to have a fixed position and then use the JavaScript/JQuery function to update the top: CSS element. This should make the page scroll smoothly.
Have you tried adding the preventdefault inside the scroll function?
$(window).scroll(function(e) {
e.preventDefault();
// rest of your code
}
In a previous question I created a fairly good parallax scrolling implementation. Jquery Parallax Scrolling effect - Multi directional You might find it useful.
Here's the JSFiddle http://jsfiddle.net/9R4hZ/40/ use the up/down arrows or scroll wheel.
Using padding and margin for the positioning are probably why you're experiencing rendering issues. While my code uses scroll or keyboard input for the effect you can loop the relavent portion and check the $moving variable until you reach the desired element on screen.
function parallaxScroll(scroll) {
// current moving object
var ml = $moving.position().left;
var mt = $moving.position().top;
var mw = $moving.width();
var mh = $moving.height();
// calc velocity
var fromTop = false;
var fromBottom = false;
var fromLeft = false;
var fromRight = false;
var vLeft = 0;
var vTop = 0;
if($moving.hasClass('from-top')) {
vTop = scroll;
fromTop = true;
} else if($moving.hasClass('from-bottom')) {
vTop = -scroll;
fromBottom = true;
} else if($moving.hasClass('from-left')) {
vLeft = scroll;
fromLeft = true;
} else if($moving.hasClass('from-right')) {
vLeft = -scroll;
fromRight = true;
}
// calc new position
var newLeft = ml + vLeft;
var newTop = mt + vTop;
// check bounds
var finished = false;
if(fromTop && (newTop > t || newTop + mh < t)) {
finished = true;
newTop = (scroll > 0 ? t : t - mh);
} else if(fromBottom && (newTop < t || newTop > h)) {
finished = true;
newTop = (scroll > 0 ? t : t + h);
} else if(fromLeft && (newLeft > l || newLeft + mw < l)) {
finished = true;
newLeft = (scroll > 0 ? l : l - mw);
} else if(fromRight && (newLeft < l || newLeft > w)) {
finished = true;
newLeft = (scroll > 0 ? l : l + w);
}
// set new position
$moving.css('left', newLeft);
$moving.css('top', newTop);
// if finished change moving object
if(finished) {
// get the next moving
if(scroll > 0) {
$moving = $moving.next('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:last');
} else {
$moving = $moving.prev('.parallax');
if($moving.length == 0)
$moving = $view.find('.parallax:first');
}
}
// for debug
$('#direction').text(scroll + " " + l + "/" + t + " " + ml + "/" + mt + " " + finished + " " + $moving.text());
}
May not be related to your specifics, but I had a jumpy parallax scrolling problem, I was able to solve it adding the following CSS for the fixed portions of the page:
#supports (background-attachment: fixed)
{
.fixed-background
{
background-attachment: fixed;
}
}
Not sure of all the specifics, but found at Alternate Fixed & Scroll Backgrounds
How I can move DOM elements slowly?
This does not work
for ( var a = 0 ; a < 100 ; a++){
$('*').each(function(){
if ( ! /HTML/.test($(this).context.nodeName))
{
var top = parseInt($(this).css('top')) + 1;
$(this).css('top',top + "px");
}
});
}
Elements are positioned when the loop finish
How can I do this slowly?
Sorry for my English
Or in pure javascript, you should use a timer
var $elem = $(this), // jquery object
elem = $elem[0], // dom element
currentPos = $elem.offset().top, // current position
targetPos = currentPosition + 100, // target position
timer = setInterval (function () { // timer to move element slowly
currentPos++;
$elem.css('top',currentPosition + "px");
if (currentPos == targetPos)
clearInterval(timer);
}, 100);
try jquery's $.animate()
it requires you to set a target position to move to, rather than continuous movement
or using setInterval:
intervalInMilliseconds=17;//60 frames per second
var interval = setInterval(function()
{
for ( var a = 0 ; a < 100 ; a++){
$('*').each(function(){
if ( ! /HTML/.test($(this).context.nodeName))
{
var top = parseInt($(this).css('top')) + 1;
$(this).css('top',top + "px");
}
});
}
},intervalInMilliseconds);
stop when you're done by doing this:
clearInterval(interval)
If you are targetting new enough browser versions, you could use CSS animation instead.
I'm wondering if there is a simple way to make use of JavaScript (probably jQuery too?) in order to make the contents of a fixed-height div element scroll infinitely up and down (top, bottom, top, bottom, etc) when the page loads and without any user input or manipulation?
Thanks ahead of time, any input is greatly appreciated as I am hardly mediocre with JavaScript.
With pure js you can do something like this:
var scroller = document.getElementById('scroller');
var delta = 15;
var lastSc;
//console.log(scroller.scrollTop, scrollHeight);
setInterval(function(){
var sc = scroller.scrollTop + delta;
scroller.scrollTop = sc;
if (scroller.scrollTop === lastSc){
delta = delta*(-1);
}
lastSc = scroller.scrollTop;
}, 10);
Here is demo
Edit: updated demo
Here is something I've just written, using jQuery:
var speed = 100; //smaller means faster
var offset = 5; //bigger means more text will be "scrolled" every time
function ScrollMyDiv() {
var myDiv = $("#MyDiv");
var direction = myDiv.attr("scroll_dir") || "";
var lastScrollTop = parseInt(myDiv.attr("last_scroll_top") || "0", 10);
if (direction.length === 0) {
myDiv.attr("scroll_dir", "down");
direction = "down";
}
var top = myDiv.scrollTop();
myDiv.attr("last_scroll_top", top + "")
if (direction === "down") {
if (top > 0 && lastScrollTop === top)
myDiv.attr("scroll_dir", "up");
top += offset;
} else {
if (top <= 0)
myDiv.attr("scroll_dir", "down");
top -= offset;
}
myDiv.scrollTop(top);
window.setTimeout(ScrollMyDiv, speed);
}
$(document).ready(function() {
ScrollMyDiv();
});
Live test case: http://jsfiddle.net/HmfNJ/1/
Basically, it will start by scrolling down (adding to the scrollTop) then when it identify it reached the bottom by seeing the scrollTop remains the same, it will change direction and start scroll up.
Thanks for the replies but I found my answer elsewhere. Here's what I ended up using: http://jsbin.com/onohan/3/edit#preview
It had a couple of small problems but I at least knew enough about basic JavaScript to fix them. Hopefully this will benefit someone in the future. :)
To get a smooth transition for scroll to bottom this is VanillaJS code that works well with me
var delta = 0.6, interval;
interval = setInterval(function(){
window.scrollBy(0, delta);
}, 20);
To clear the Interval you can run
clearInterval(interval);