how do you do non-linear animation with css3?
Basically, if I need to bring a box in from out of the view port, and its a straight down path, then its easy enough to do with the following code:
*{
transition: transform 0.5s ease-in;
}
-- And some JS to trigger the animation with a transform: translate3d(0,300px,0);
But what happens when the element is rotated? Say by 25 deg? Then for the animation to look somewhat natural, it'll need to progress in a 25 deg offset line, and cannot just be top-down, or left-right animations...
I hope I'm making sense here... Look at the demo here http://jsfiddle.net/YMHT4/8/
I'm trying to get the blue box to animate in on a slanted path...
I'm not sure if I'm understanding correctly, but if you're trying to do what I'm thinking, then the answer is quite simple:
function ani()
{
if (!state)
{
$('#otherbox').css('-webkit-transform', 'translate3d(100px,150px,0) rotate(25deg)');
state=true;
}
else
{
$('#otherbox').css('-webkit-transform', 'translate3d(0,0,0) rotate(25deg)');
state=false;
}
}
If you change both x and y in translate3d(x,y,z), then both dimensions will animate, and you will get a diagonal path animation.
http://jsfiddle.net/YMHT4/17
Related
I am confused with gsap's Flip.fit moving to coordinates.
I have a game board with 182 tiles and 182 playing tiles.
The goal
When the user clicks the bag, a random playing tile is selected and is "supposed" to move over the tile on the board.
If you change
Flip.fit(PTILE[tileArray], TILE[tileArray], {duration: 1 , scale: true});
when changing { duration: 0, ... } the move works as expected, however no animation. When duration is above zero, the final location is very random.
codepen
I'm not sure how the duration affects the final position, however, I found a way to get the positions right. That is reset the transform of your PTILE before telling GSAP to do the Flip animation.
// reset transform value
gsap.set(PTILE[tileArray], { transform: "" });
// animate with new transform value
Flip.fit(PTILE[tileArray], TILE[tileArray], {
duration: 1,
scale: true
});
My reason is that PTITLE and TITLE are placed in different <g> tags which means their transform systems are inconsistent. Plus, Flip.fit() will act like gsap.to() with new TITLE position is the to object, GSAP will try to calculate the from object from your original transforms which are already set in the SVG as transform:matrix(). This process, somehow, is messing up. So what I did is give GSAP an exact transform value for the from object, which is empty.
Ok, I found out that Inkscape stores the SVG with inline transforms that threw the animation off. I tried saving in plain or optimised, but still had no luck.
So there are two solutions.
Use SVGOMG an online SVG cleaner.
Use Affinity Designer application which can export and flatten transforms.
The key to rule out other factors is to use relative coordinates and flatten transforms.
I have included a screenshot of Affinity exporting options.
Affinity Export screenshot
I want to know whether a given point is inside or outside of a text shape. As you will notice in the sample I provided below, hitTest will return true as soon as the point is inside of the TextItem's bounds, and not only if the point is inside of the character itself. (You can experience this behavior best when you place your mouse pointer in the middle of the #)
Sample: Hit-testing against TextItem
I also tried drawing the character based on paths (as Raphaƫl is doing in their font samples) to use the paths itself for hit-testing but stumbled upon some quite strange behavior where (some) characters are not drawn correctly. (If you copy the path definition into a vector image software like Inkscape the text shapes are drawn correctly)
Sample: Drawing text as path
What is the most promising way to find out whether a given point is inside or outside of a text shape?
You can hit-test a text shape (or any other mathematically irregular shape) by texting whether the pixel under the mouse is transparent or not.
You can get the pixel-array for the entire canvas using:
var data=context.getImageData(0,0,canvas.width,canvas.height).data;
Then you can fetch the opacity (alpha) value for the pixel under the mouse like this:
var pixelIsTransparent = data[(mouseY*canvas.width+mouseX)*4+3]==0
If the pixel is not transparent then you're over the text shape.
If you have other non-text drawings on the canvas then those non-text drawings might give false-positives for your hit-tests. A workaround for that is to use a second in-memory canvas containing only your text-shape and then do hit testing against the pixels on that second canvas.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
ctx.strokeStyle='gray';
ctx.font='300px verdana';
var wasHit=false;
var isHit=false;
draw();
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
$("#canvas").mousemove(function(e){handleMouseMove(e);});
function draw(){
ctx.fillStyle=(isHit)?'green':'lightgray';
ctx.fillText("M",25,250);
ctx.strokeText("M",25,250);
}
function handleMouseMove(e){
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
isHit=(data[(mouseY*cw+mouseX)*4+3]>10)
if(!isHit==wasHit){
draw();
wasHit=isHit;
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p>Hit test: Move mouse over letter</p>
<canvas id="canvas" width=300 height=300></canvas>
After spending quite some time debugging paper.js code I finally found the solution for this problem.
Instead of using Path you are supposed to use CompoundPath:
A compound path contains two or more paths, holes are drawn where the paths overlap. All the paths in a compound path take on the style of the backmost path and can be accessed through its item.children list.
I also updated the example from above:
http://jsfiddle.net/64v7s6L9/1/
I am testing SVG performance using RaphaelJS library.
My code works, can be found here: JSFiddle
When you type in textbox "1" and press "add", a rectangle will be generated on screen and 4 animations will loop on it- moving right, down, left, up (also rotating, scaling and changing colour).
Performance seems to be ok. But add another element on stage and the performance gets knocked down to minimum in 3-4 seconds. Checked in Chrome timeline, the thing that is getting stacked up is "Animation Frame Fired - > Install Timer".
Perhaps I am doing loop incorrectly? Altough next animation starts when the previous ends, through callback function. Or is it Raphael itself? Should I try doing this with SVG and SMIL? (Raphael uses VML)
Any ideas?
------------------------------------UPDATE--------------------------------------
With RaphaelJS I did bad animation loop hooks, see answer below.
But another problem that does occur - add 1 element 10 times and you can see how animations get distorted, not finishing their full cycle, or add 10 elements 1 time and after few seconds you can see delayed animations on some of the elements.
I made SMIL version JSFiddle (no Raphael here), animations do not lag, delay, but they get syncronized. Can anyone explain why? And how to make those animations NOT sync, but unique?
I think the problem is you are recursively calling animations on a set.
So at the end of each animation, each element in the set calls an animation for the set again, so it spirals and grinds to a halt. You can get around this, by using 'this' instead of the set 'rectangles'.
//define 4 animations
var move_up = Raphael.animation({fill: "green", transform: "t0,0r360s1,1"}, 400, function(){ this.attr({"transform" : "t0,0"}); this.animate(move_right); });
var move_left = Raphael.animation({fill: "yellow", transform: "t0,100r270s0.5,0.5"}, 400, function(){ this.animate(move_up); });
var move_down = Raphael.animation({fill: "red", transform: "t100,100r180s1,1"}, 400, function(){ this.animate(move_left); });
var move_right = Raphael.animation({fill: "blue", transform: "t100,0r90s1.5,1.5"}, 400, function(){this.animate(move_down); });
jsfiddle
This may not be possible, but I have a DIV that I am transforming using CSS3.
#mydiv.fallback {
-webkit-transform: scale(.9);
-webkit-transform-origin: center;
}
Works like a charm. Except, I'm trying to create an effect similar to Mac OSX's Timemachine (faux 3D effect), where the DIV falls back in the background. For this to work aesteitcally, I need the transform-origin to NOT use it's element's center, but the user's web browser's center. It would work even if I was able to change it to use it's parent's elements coordinates for its transform.
Not sure if this is possible or not sadly. Quick image to help illustrate.
Just update 'transform-origin' whenever you want to pop out the modal window. For example:
function modal() {
var transOrigin = window.innerHeight/2 + document.body.scrollTop;
container.style.webkitTransformOrigin = "50% " + transOrigin + "px";
}
Here is a working example for webkit.
http://jsfiddle.net/GprNe/
I am trying to replicate an Apple style activity indicator (sundial loading icon) by using a PNG and CSS3 animation. I have the image rotating and doing it continuously, but there seems to be a delay after the animation has finished before it does the next rotation.
#-webkit-keyframes rotate {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
#loading img
{
-webkit-animation-name: rotate;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: infinite;
-webkit-transition-timing-function: linear;
}
I have tried changing the animation duration but it makes no difference, if you slow it right down say 5s its just more apparent that after the first rotation there is a pause before it rotates again. It's this pause I want to get rid of.
Any help is much appreciated, thanks.
Your issue here is that you've supplied a -webkit-TRANSITION-timing-function when you want a -webkit-ANIMATION-timing-function. Your values of 0 to 360 will work properly.
You also might notice a little lag because 0deg and 360deg are the same spot, so it is going from spot 1 in a circle back to spot 1. It is really insignificant, but to fix it, all you have to do is change 360deg to 359deg
my jsfiddle illustrates your animation:
#myImg {
-webkit-animation: rotation 2s infinite linear;
}
#-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(359deg);}
}
Also what might be more resemblant of the apple loading icon would be an animation that transitions the opacity/color of the stripes of gray instead of rotating the icon.
You could use animation like this:
-webkit-animation: spin 1s infinite linear;
#-webkit-keyframes spin {
0% {-webkit-transform: rotate(0deg)}
100% {-webkit-transform: rotate(360deg)}
}
If you're only looking for a webkit version this is nifty: http://s3.amazonaws.com/37assets/svn/463-single_spinner.html from http://37signals.com/svn/posts/2577-loading-spinner-animation-using-css-and-webkit
Your code seems correct. I would presume it is something to do with the fact you are using a .png and the way the browser redraws the object upon rotation is inefficient, causing the hang (what browser are you testing under?)
If possible replace the .png with something native.
see; http://kilianvalkhof.com/2010/css-xhtml/css3-loading-spinners-without-images/
Chrome gives me no pauses using this method.
I made a small library that lets you easily use a throbber without images.
It uses CSS3 but falls back onto JavaScript if the browser doesn't support it.
// First argument is a reference to a container element in which you
// wish to add a throbber to.
// Second argument is the duration in which you want the throbber to
// complete one full circle.
var throbber = throbbage(document.getElementById("container"), 1000);
// Start the throbber.
throbber.play();
// Pause the throbber.
throbber.pause();
Example.