Related
I'm newbie of Javascript and Jquery.
I am learning img animation and I have a question.
If I want to move the image from bottom left to the top right in window. Is there any better way than my code?
My code doesn't' work then I expected.
here is my code
$(document).ready(function () {
function startMoving() {
var img = $("#imageId");
var imgWidth = img.width();
var imgHeight = img.height();
var screenWidth = $(window).innerWeight();
var screenHeight = $(window).innerHeight();
var c = Math.sqrt((screenWidth*screenWidth+screenHeight*screenWidth));
var movement = c/10 // This is for the step of movement
var zScale = (screenWidth+screenHeight)/2;
var imgZScale = (imgHeight+imgWidth)/2;
console.log(zScale);
console.log(imgHeight);
img.animate({
"left": "+="+movement,
"top": "-="+movement
},"slow");
}
setInterval(function(){
startMoving();
},1000)
});
If the image at the corner how can I restart image movement again from bottom left?
Thank you in advance!
You can simplify things using CSS transitions instead of JQuery animate(). This feature is easy to manage and with less code.
I've just created an initial CSS state and a final stage (.end), using jQuery to toggle the class to switch from initial/final position.
$(document).ready(function () {
setInterval(function(){
$("#imageId").toggleClass("end");
},2000);
});
img{
position: absolute;
width: 5vw;
height: 5vw;
top: 95vh;
left: 0;
transition: linear 1s;
}
img.end{
top: 0;
left: 95vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img id="imageId" class="start" width="48" height="48">
$(document).ready(function() {
//ENTRANCE
$("#first").css("top", -1000);
setTimeout(function() {
$("#first").animate({
top: 10
}, 400);
}, 200);
$("#second").css("top", -1000);
setTimeout(function() {
$("#second").animate({
top: 10 + 45 + 15
}, 400);
}, 400);
$("#third").css("top", -1000);
setTimeout(function() {
$("#third").animate({
top: 10 + 45 + 45 + 30
}, 400);
}, 600);
$("#four").css("top", -1000);
setTimeout(function() {
$("#four").animate({
top: 10 + 45 + 45 + 45 + 45
}, 400);
}, 800);
//EXIT
$('#first').on('click', function() {
$('#first').toggle();
$('#second').animate({top: 5}, 400);
});
$('#second').on('click', function() {
$('#second').toggle();
$('#third').animate({top: 5}, 400);
});
$('#third').on('click', function() {
$('#third').toggle();
$('#four').animate({top: 5}, 400);
});
$('#four').on('click', function() {
window.location.reload();
});
});
`
I have been trying for a while to make elements interact with each other using jquery, Here is a
Fiddle of my code.
I have although been having a few hiccups.
In a real world environment, elements may not be called in ascending or logical order.
Items do not animate properly when closed, there are gaps and in some cases, some items do not move depending on which is clicked.
There may be more than 4 items.
Here is my question: How can i make the elements animate and cover properly regardless of which item is clicked and what order the items are sorted.
please see this fiddle
var elements = $('.menu');// Here you can write any selector to get list of elements
elements.on('click', function() {
$(this).toggle();
var nextEleemnts = $(this).nextAll('.menu'); // Same selector will follow here
for (var i = 0; i < nextEleemnts.length; i++) {
var topPos = $(nextEleemnts[i]).position().top - 60; //little bit of counting
$(nextEleemnts[i]).animate({
top: topPos
}, 400);
}
});
There is also a good solution and straight forward solution provided to you by a guy in comment, For this you need to do a bit of change in CSS aswell, so if you don't want to do it, then you can take my approach aswell
Here I am talking an alternate approach, here what I am doing whenever you click on any element I am finding it's next siblings and position them up by 60 pixels
If I were you, I would consider using jqueryUI
But maybe you have some restrictions of some kind.
I came up with a solution, in which I use jquery gt selector to select elements after the one clicked.
Please note that html is almost empty, which allows to add as many elements as you like.
(By the way I wouldn't make elements position absolute as well, but that's another story.
$(document).ready(function() {
"use strict";
var childCount = 12;
// some templating library would make a better job
for (var i = 0; i < childCount; ++i) {
var child = $("<div>" + i + "th div </div>");
child.css("background-color", "#" + ((1 << 24) * Math.random() | 0).toString(16));
child.css("top", i * 50);
$("#parent").append(child); // add any transition here
}
var reset = $("<div id='reset'>Reset</div>")
.css("background-color", "black")
.css("color", "white")
.css("top", childCount * 50);
$("#parent").append(reset);
$("#parent > div").on("click", function() {
var clicked = $(this);
var index = $("#parent > div").index(clicked);
$("#parent > div:gt(" + (index - 1) + ")").add(reset).animate({
top: "-=50"
}, 100, function() {
clicked.remove();
});
childCount -= 1;
});
});
#parent > div {
width: 100px;
height: 40px;
margin-bottom: 10px;
position: absolute;
text-align: center;
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
<!-- some data-bind attribute would be better than an id -->
</div>
I have an element div in a shape of a ball. What I am trying to do is, when I refresh the page I want to the ball to fall to the bottom of the webpage and then bounce back up to the top of the page.
This is my jQuery function where the ball falls to the bottom of the web page
$(document).ready(function(){
$("div").animate({ top: '+=585'}, 400);
});
Am I using a correct approach? Should I use slideDwon and slideUp instead?
Try utilizing jQuery UI .effect()
$(function() {
var div = $("div");
// `elem`: element to apply bounce effect,
// `n`: number of bounce effects to apply to `elem`
var bounce = function bounce(elem, n) {
var fx = function fx(el) {
return (el || $(this))
.effect({
effect: "bounce",
easing: "swing",
duration: 400,
distance: window.innerHeight
- (el.height() + el.offset().top * 1.5),
direction: "down",
times: 1
}).promise()
};
return fx(elem).then.apply(elem, $.map(Array(n - 1), function() {
return fx(elem)
}));
};
bounce(div, 1).then(function(el) {
// do stuff when bounce effect complete
console.log("complete", el)
});
});
div {
position: relative;
width: 100px;
height: 100px;
background: rgb(212, 98, 44);
border: 2px solid navy;
border-radius: 50%;
}
<link href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css"
rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div></div>
Take advantage of jQuery's animation chainability. Also, you probably shouldn't assume a static value of 585 will be suitable for every screen size. I suggest using calculated values for generating the offsets, check this fiddle:
$(document).ready(function () {
var viewportH = $(window).height();
var elem = $('div');
var elemH = elem.height();
elem.animate({
top: '+=' + (viewportH - elemH) // bottom of screen
}, 400).animate({
top: '-=' + (viewportH - elemH) // original position
});
});
Using this HTML :
<div id="myDiv" class="myRelativeDiv">test</div>
1st step is to set the position of your div as "relative" :
.relative {
position:relative;
}
2nd step is animate with Jquery (You can chain many animate):
$(function() {
$("#myDiv").animate({ top: '+=585'}, 400).animate({ top: '0'}, 400);
});
JsFiddle
$(document).ready(function(){
$("div").animate({ top: '+=585'}, 400);
setTimeout(
function()
{
$("div").animate({ top: '-=585'}, 400);
}, 400);
});
Is it possible to use smooth scroll to anchor links but without jQuery? I am creating a new site and I don't want to use jQuery.
Extending this answer: https://stackoverflow.com/a/8918062/3851798
After defining your function of scrollTo, you can pass the element you want to scrollTo in the function.
function scrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
If you have a div with an id="footer"
<div id="footer" class="categories">…</div>
In the script that you run to scroll you can run this,
elmnt = document.getElementById("footer");
scrollTo(document.body, elmnt.offsetTop, 600);
And there you have it. Smooth scrolling without jQuery. You can actually play around with that code on your browser's console and fine tune it to your liking.
Using the function from here: JavaScript animation and modifying it to modify a property (not only a style's property), you can try something like this:
DEMO: http://jsfiddle.net/7TAa2/1/
Just saying...
function animate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function() {
var step = Math.min(1, (new Date().getTime() - start) / time);
if (prop) {
elem[style] = (from + step * (to - from)) + unit;
} else {
elem.style[style] = (from + step * (to - from)) + unit;
}
if (step === 1) {
clearInterval(timer);
}
}, 25);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
window.onload = function() {
var target = document.getElementById("div5");
animate(document.scrollingElement || document.documentElement, "scrollTop", "", 0, target.offsetTop, 2000, true);
};
div {
height: 50px;
}
<div id="div1">asdf1</div>
<div id="div2">asdf2</div>
<div id="div3">asdf3</div>
<div id="div4">asdf4</div>
<div id="div5">asdf5</div>
<div id="div6">asdf6</div>
<div id="div7">asdf7</div>
<div id="div8">asdf8</div>
<div id="div9">asdf9</div>
<div id="div10">asdf10</div>
<div id="div10">asdf11</div>
<div id="div10">asdf12</div>
<div id="div10">asdf13</div>
<div id="div10">asdf14</div>
<div id="div10">asdf15</div>
<div id="div10">asdf16</div>
<div id="div10">asdf17</div>
<div id="div10">asdf18</div>
<div id="div10">asdf19</div>
<div id="div10">asdf20</div>
Actually, there is more lightweight and simple way to do that:
https://codepen.io/ugg0t/pen/mqBBBY
function scrollTo(element) {
window.scroll({
behavior: 'smooth',
left: 0,
top: element.offsetTop
});
}
document.getElementById("button").addEventListener('click', () => {
scrollTo(document.getElementById("8"));
});
div {
width: 100%;
height: 200px;
background-color: black;
}
div:nth-child(odd) {
background-color: white;
}
button {
position: absolute;
left: 10px;
top: 10px;
}
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
<div id="7"></div>
<div id="8"></div>
<div id="9"></div>
<div id="10"></div>
<button id="button">Button</button>
This is a pretty old question, but it's important to say that nowadays smooth scrolling is supported in CSS, so there's no need for any scripts:
html {
scroll-behavior: smooth;
}
As noted by #Andiih, as of late 2022 there is full browser support for this.
Use this:
let element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"});
DEMO: https://jsfiddle.net/anderpo/x8ucc5ak/1/
Vanilla js variant using requestAnimationFrame with easings and all browsers supported:
const requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
function scrollTo(to) {
const start = window.scrollY || window.pageYOffset
const time = Date.now()
const duration = Math.abs(start - to) / 3;
(function step() {
var dx = Math.min(1, (Date.now() - time) / duration)
var pos = start + (to - start) * easeOutQuart(dx)
window.scrollTo(0, pos)
if (dx < 1) {
requestAnimationFrame(step)
}
})()
}
Any easing supported!
CSS3 transitions with a :target selector can give a nice result without any JS hacking. I was just contemplating whether to imlement this but without Jquery it does get a bit messy. See this question for details.
Try this code here:
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
Smooth Scroll behavior with polyfill...
Example:
document.querySelectorAll('a[href^="#"]').addEventListener("click", function(event) {
event.preventDefault();
document.querySelector(this.getAttribute("href")).scrollIntoView({ behavior: "smooth" });
});
Repository: https://github.com/iamdustan/smoothscroll
My favorite scroll-to library currently is Zenscroll because of the wide range of features and small size (currently only 3.17kb).
In the future it may make more sense to use the native scrollIntoView functionality, but since it'd have to be polyfilled in most production sites today due to the lack of IE support, I recommend using Zenscroll instead in all cases.
March 2022
I know this is an old question but wanted to put forward an answer that has simpler ways of doing it in modern days. As of today, almost all the major browsers are compatible with scroll-behavior including Safari with its latest release. Still, you might want to employ fallback methods or just use the javascript approach described in method 2 for compatibility in older browsers.
Method 1: HTML and CSS
You can just do this with
Click
.
.
.
<h2 id="target">Target</h2>
and CSS
html {
scroll-behavior: smooth
}
Method 2: JavaScript
Or if you have a unique case that needs javascript, go on elaborate with this method.
const scrollTrigger = document.getElementById('scroll-trigger');
const target = document.getElementById('target');
scrollTrigger.addEventListener('click', function (e) {
window.scroll({
top: target.offsetTop,
left:0,
behavior: 'smooth' });
}, false)
It's upgraded version from #Ian
// Animated scroll with pure JS
// duration constant in ms
const animationDuration = 600;
// scrollable layout
const layout = document.querySelector('main');
const fps = 12; // in ms per scroll step, less value - smoother animation
function scrollAnimate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function () {
var step = Math.min(1, (new Date().getTime() - start) / time);
var value = (from + step * (to - from)) + unit;
if (prop) {
elem[style] = value;
} else {
elem.style[style] = value;
}
if (step === 1) {
clearInterval(timer);
}
}, fps);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
function scrollTo(hash) {
const target = document.getElementById(hash);
const from = window.location.hash.substring(1) || 'start';
const offsetFrom = document.getElementById(from).offsetTop;
const offsetTo = target.offsetTop;
scrollAnimate(layout,
"scrollTop", "", offsetFrom, offsetTo, animationDuration, true);
setTimeout(function () {
window.location.hash = hash;
}, animationDuration+25)
};
// add scroll when click on menu items
var menu_items = document.querySelectorAll('a.mdl-navigation__link');
menu_items.forEach(function (elem) {
elem.addEventListener("click",
function (e) {
e.preventDefault();
scrollTo(elem.getAttribute('href').substring(1));
});
});
// scroll when open link with anchor
window.onload = function () {
if (window.location.hash) {
var target = document.getElementById(window.location.hash.substring(1));
scrollAnimate(layout, "scrollTop", "", 0, target.offsetTop, animationDuration, true);
}
}
For anyone in 2019,
first, you add an event listener
document.getElementById('id').addEventListener('click', () => scrollTo())
then you target the element and go smoothly to it
function scrollTo() {
let target = document.getElementById('target');
target.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
})
}
Based on MDN docs for scroll options we can use the following code:
element.scrollTo({
top: 100,
left: 100,
behavior: 'smooth'
});
In fact, the behavior key can accept smooth and auto variables. first for smooth motion and second for a single jump.
Here is a simple solution in pure JavaScript. It takes advantage of CSS property scroll-behavior: smooth
function scroll_to(id) {
document.documentElement.style.scrollBehavior = 'smooth'
element = document.createElement('a');
element.setAttribute('href', id)
element.click();
}
Usage:
Say we have 10 divs:
<div id='df7ds89' class='my_div'>ONE</div>
<div id='sdofo8f' class='my_div'>TWO</div>
<div id='34kj434' class='my_div'>THREE</div>
<div id='gbgfh98' class='my_div'>FOUR</div>
<div id='df89sdd' class='my_div'>FIVE</div>
<div id='34l3j3r' class='my_div'>SIX</div>
<div id='56j5453' class='my_div'>SEVEN</div>
<div id='75j6h4r' class='my_div'>EIGHT</div>
<div id='657kh54' class='my_div'>NINE</div>
<div id='43kjhjh' class='my_div'>TEN</div>
We can scroll to the ID of choice:
scroll_to('#657kh54')
You simply call this function on your click event (e.g. click button then scroll to div #9).
Result:
Of course it looks much smoother in real life.
FIDDLE
Unfortunately, IE and Safari don't support scrollBehavior = 'smooth' as of 2019
MDN Web Docs
For a more comprehensive list of methods for smooth scrolling, see my answer here.
To scroll to a certain position in an exact amount of time, window.requestAnimationFrame can be put to use, calculating the appropriate current position each time. setTimeout can be used to a similar effect when requestAnimationFrame is not supported.
/*
#param pos: the y-position to scroll to (in pixels)
#param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
Demo:
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
document.getElementById("toElement").addEventListener('click', function(e) {
var elem = document.querySelector("div");
scrollToSmoothly(elem.offsetTop);
});
document.getElementById("toTop").addEventListener('click', function(e){
scrollToSmoothly(0, 700);
});
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="toTop">Scroll back to top</button>
</div>
For more complex cases, the SmoothScroll.js library can be used, which handles smooth scrolling both vertically and horizontally, scrolling inside other container elements, different easing behaviors, scrolling relatively from the current position, and more.
document.getElementById("toElement").addEventListener('click', function(e) {
smoothScroll({toElement: document.querySelector('div'), duration: 500});
});
document.getElementById("toTop").addEventListener('click', function(e){
smoothScroll({yPos: 0, duration: 700});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll#1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="toTop">Scroll back to top</button>
</div>
Alternatively, you can pass an options object to window.scroll which scrolls to a specific x and y position and window.scrollBy which scrolls a certain amount from the current position:
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
top: elem.offsetTop,
left: 0,
behavior: 'smooth'
});
}
</script>
Modern browsers support the scroll-behavior CSS property, which can be used to make scrolling in the document smooth (without the need for JavaScript). Anchor tags can be used for this by giving the anchor tag a href of # plus the id of the element to scroll to). You can also set the scroll-behavior property for a specific container like a div to make its contents scroll smoothly.
html, body{
scroll-behavior: smooth;
}
Scroll To Element
<div id="elem" style="margin: 500px 0px;">Div</div>
Without jQuery
const links = document.querySelectorAll('header nav ul a')
for (const link of links) {
link.onclick = function clickHandler(e) {
e.preventDefault()
const href = this.getAttribute('href')
document.querySelector(href).scrollIntoView({ behavior: 'smooth' })
}
}
body {
background-color: black;
height:7000px
}
header {
margin-top: 1.3rem;
margin-bottom: 25rem;
display: flex;
justify-content: center;
align-items: center;
}
nav ul {
display: flex;
}
nav ul li {
all: unset;
margin: 2rem;
cursor: pointer;
}
nav ul li a {
all: unset;
font: bold 1.8rem robto;
color: white;
letter-spacing: 1px;
cursor: pointer;
padding-top: 3rem;
padding-bottom: 2rem;
}
#team,
#contact,
#about {
background-color: #e2df0d;
width: 100%;
height: 35rem;
display: flex;
justify-content: center;
align-items: center;
color: black;
font: bold 4rem roboto;
letter-spacing: 6.2px;
margin-top: 70rem;
}
<header>
<!-- NavBar -->
<nav>
<ul>
<li>Team</li>
<li>Contact</li>
<li>About</li>
</ul>
</nav>
</header>
<!-- ----------- Team ----------------------- -->
<div id="team">
<h2>Team</h2>
</div>
<!-- ----------- Contact ----------------------- -->
<div id="contact">
<h2>Contact</h2>
</div>
<!-- ----------- About ----------------------- -->
<div id="about">
<h2>About</h2>
</div>
Or with just CSS, but it's not supported in all browsers yet
html {scroll-behavior: smooth}
If you want to set all of your deep links # to scroll smoothly you can do this:
const allLinks = document.querySelectorAll('a[href^="#"]')
allLinks.forEach(link => {
const
targetSelector = link.getAttribute('href'),
target = document.querySelector(targetSelector)
if (target) {
link.addEventListener('click', function(e) {
e.preventDefault()
const top = target.offsetTop // consider decreasing your main nav's height from this number
window.scroll({
behavior: 'smooth',
left: 0,
top: top
});
})
}
})
An example code to consider also your main nav's height (this code goes where top const is declared):
const
mainHeader = document.querySelector('header#masthead'), //change to your correct main nav selector
mainHeaderHeight = mainHeader.offsetHeight,
// now calculate top like this:
top = target.offsetTop - mainHeaderHeight
Here is the most elegant and concise solution.
Links:
CSS:
html {
scroll-behavior: smooth;
}
Remember to add a unique id="elementIDtoScrollTo" to each HTML element.
My first question here. :)
I'm looking for a transitions between two images where the image first shrinks in a circle shape and then the circle grows again containing the other image. It's hard to explain, and I may be using the wrong words, because I can't find anything about it on the Interwebz.
I'm talking about an effect like the Loony Toons ending.
http://www.youtube.com/watch?v=ZuYIq-J5l9I
That shrinking-to-black, can it be done in JavaScript/JQuery?
TL:DR
- Cross-browser: [**See a working demo
here**](http://jsfiddle.net/lthibodeaux/8DSjz/).
Well, mostly working... and
cross-browser. Could do worse. ;]
- Purely CSS3 Solution: [**See a working demo
here**](http://jsfiddle.net/lthibodeaux/8DSjz/16/)
How do I even begin to describe this one? It would be a lot easier if the CSS 2 clip standard supported anything besides a "rect" value, namely a "circle" or "ellipse" but... since that doesn't exist, I've done my best to piece something together that will do what you're asking. The caveats are many. One is that this is only going to work on something with a solid color background in the event you wanted the picture to clip to the background. Another is that while I've tried to account for the CSS update timing across browsers, the rendering still isn't "perfect." My initial approach was to simply animate the clip on the image that was getting replaced, but that didn't work due to the way updates were made to the clipping via the easing function in the plugin I located. The final approach is below.
The Approach
The concept is to set the image as a background-image property of a container like a <div> with a background-position of center center, and the position of the container to relative, or anything non-static. The next is to generate the clipping elements as children of the container. The first is a position: absolute clipping circle image of the color of your background, either transparent PNG or GIF (I prefer the former), and the next four are divs, also with absolute positions that have left, right, top, and bottom attributes set to 0 for each of the respective sides they will clip. The idea is to animate the top, left, width, and height of the clipping circle image and synch up the width and height of the clipping divs using the step callback option of the .animate() call by matching them to the current left and top values. Between animations, you change the background-image of the container to the new image and then start the animation back in the opposite direction.
This required a little finessing in IE7, 8, and Webkit browsers as the animation clipped much more cleanly in Firefox and IE9. This would be the adjust variable you'll see in the working demo.
The sample code is below:
The Markup
<div class="imageContainer image1">
<img class="clip" src="clipCircle.png" />
<div class="top fill"></div>
<div class="left fill"></div>
<div class="right fill"></div>
<div class="bottom fill"></div>
</div>
The CSS
div.imageContainer
{
background-position: center;
width: 300px;
height: 300px;
position: relative;
}
img.clip
{
width: 100%;
height: 100%;
position: absolute;
}
div.fill
{
position: absolute;
background-color: White;
}
div.left, div.right
{
height: 100%;
top: 0;
width: 0;
}
div.left
{
left: 0;
}
div.right
{
right: 0;
}
div.top, div.bottom
{
width: 100%;
left: 0;
height: 0;
}
div.top
{
top: 0;
}
div.bottom
{
bottom: 0;
}
The Script
var speed = 1000;
$clip = $("img.clip");
$clip.animate({
top: $clip.parent().height() / 2,
left: $clip.parent().width() / 2,
width: 0,
height: 0
}, {
duration: speed,
step: function(now, fx) {
switch (fx.prop) {
case "top":
$("div.top").css("height", now);
$("div.bottom").css("height", now + adjust);
break;
case "left":
$("div.left").css("width", now);
$("div.right").css("width", now + adjust);
}
},
complete: function() {
$(this).parent().addClass("image2");
$(this).animate({
top: 0,
left: 0,
width: $clip.parent().width(),
height: $clip.parent().height()
}, {
duration: speed,
step: function(now, fx) {
switch (fx.prop) {
case "top":
$("div.top").css("height", now);
$("div.bottom").css("height", now + adjust);
break;
case "left":
$("div.left").css("width", now);
$("div.right").css("width", now + adjust);
}
},
complete: function() {
$("div.imageContainer > *").removeAttr("style");
}
});
}
});
EDIT:
The CSS3 Solution
When cross-browser compatibility is less of a concern, CSS3 is an option (although I'd probably suggest seeing what can be done with the new HTML5 Canvas for this kind of animation). There are a couple things to note:
The image must be inside a container in order to allow us to clip toward its center rather than its top left corner.
The border-radius attribute will not clip the child images inside a container. For this reason, the image must become the background-image attribute of the container.
jQuery does not currently animate border-radius correctly. You can either replace the current jQuery animate functionality for that attribute or build a custom border-radius animation object to make jQuery more well-behaved. I have opted for the latter. Each corner's border-radius must be animated separately.
The animation in or out consists of two separate segments, and as a result the "linear" easing function is probably best used for cleanest results.
The method is commented inline below:
The Markup
<div class="imageContainer image1">
</div>
The CSS
div.imageContainer
{
background-position: 0px 0px;
background-repeat: no-repeat;
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 0;
}
div.image1
{
background-image: url(/images/myFirstImage.png);
}
div.image2
{
background-image: url(/images/mySecondImage.png);
}
The Script
// Total animation speed in or out will be speed * 1.5
var speed = 600;
// Store a reference to the object to be clipped
var $clip = $("div")
// A function to build a mapping object for border radius parameters
var buildRadiusObj = function(value) {
// Dimension an option object
var opts = {};
// Use specialized Mozilla CSS attributes when needed
var attributes = $.browser.mozilla ?
["-moz-border-radius-topleft",
"-moz-border-radius-bottomleft",
"-moz-border-radius-topright",
"-moz-border-radius-bottomright"] :
["border-top-left-radius",
"border-bottom-left-radius",
"border-top-right-radius",
"border-bottom-right-radius"];
// Build the option object
$.each(attributes, function(i, key) {
opts[key] = value;
});
// Return the result
return opts;
}
$clip.animate(buildRadiusObj($clip.width() * 0.5), { // Animate the border radius until circular
duration: speed * 0.5,
easing: "linear"
}).animate({ // Resize and reposition the container
width: 0,
left: $clip.width() / 2,
height: 0,
top: $clip.height() / 2
}, {
duration: speed,
easing: "linear",
step: function(now, fx) { // Synch up the background-position
if (fx.prop == "top") {
$(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left"));
}
},
complete: function() { // Swap the image
$(this).addClass("image2");
}
}).animate({ // Restore position and size
width: $clip.width(),
left: 0,
height: $clip.height(),
top: 0
}, {
duration: speed,
easing: "linear",
step: function(now, fx) { // Synch the background-position
if (fx.prop == "top") {
$(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left"));
}
},
complete: function() { // Remove inline styles but reapply border-radius
$(this).removeAttr("style").css(buildRadiusObj($clip.width() * 0.5));
}
}).animate(buildRadiusObj(0), { // Restore border-radius to block
duration: speed * 0.5,
easing: "linear",
complete: function() {
$(this).removeAttr("style"); // Remove inline styles
}
});
Again, the demo is located here.
I came this across, I hope it is interesting: http://www.netzgesta.de/transm/. The transition circles_out with one circle could do the job I think.
Here you go. http://jquery.malsup.com/cycle/ Check out the zoom. Something can be worked out with the circle part.
I tried some more and came up with the idea of using a <canvas> element.
Please see the result at: http://jsfiddle.net/3MG8e/2/.
var cv = $('canvas')[0];
var ctx = cv.getContext('2d');
ctx.fillStyle = 'black';
var int = null;
var t = -1;
var amount = 50;
var time = 1000;
var size = 0;
var im = new Image();
im.src = "http://burzak.com/proj/fxcanvas/docs/images/mario2.png";
im.onload = function() {
size = im.width;
int = setInterval(update, time / amount);
}
function update() {
if(++t >= amount) {
clearInterval(int);
}
ctx.fillRect(0, 0, cv.width, cv.height);
ctx.beginPath();
ctx.arc(size/2, size/2,
size/2 - t * (size/2) / amount,
0, Math.PI*2,
false);
ctx.clip();
ctx.drawImage(im, 0, 0, size, size);
}