I'm trying to achieve a pulsating glow effect in raphael.js. Here is my code http://jsfiddle.net/eaPSC/ I'm very sorry about the massive brain. ;)
I tried animating both the width of the glow effect and the opacity and neither seem to be influenced by animation at all. (The glow is static. I examined it by hiding the brain element, zooming in and checking out just the glow element, and there is simply no action.)
I tried animating a separate (non-glow) element using the same procedure and multiple attributes do get animated fine.
thanks!
You cannot animate the width or color properties of a glow. The glow is created by adding a stroke to a set of paths with zero fill. If you want to change the color or the width of the glow you have to animate the stroke or stroke-width properties.
http://jsfiddle.net/eaPSC/2/
Wrong: (quoted from your source):
anim = Raphael.animation({
width: 15,
opeacity: 1
}, 500);
Slightly More Correct:
anim = Raphael.animation({
"stroke-width": 15,
opacity: 1
}, 500);
But you will notice that this kills off the gradiented glow effect. If you actually look at the source code for glow() you can see that the final for loop creates a layered set of paths to create the gradient effect.
elproto.glow = function (glow) {
if (this.type == "text") {
return null;
}
glow = glow || {};
var s = {
width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
fill: glow.fill || false,
opacity: glow.opacity || .5,
offsetx: glow.offsetx || 0,
offsety: glow.offsety || 0,
color: glow.color || "#000"
},
c = s.width / 2,
r = this.paper,
out = r.set(),
path = this.realPath || getPath[this.type](this);
path = this.matrix ? mapPath(path, this.matrix) : path;
for (var i = 1; i < c + 1; i++) {
out.push(r.path(path).attr({
stroke: s.color,
fill: s.fill ? s.color : "none",
"stroke-linejoin": "round",
"stroke-linecap": "round",
"stroke-width": +(s.width / c * i).toFixed(3),
opacity: +(s.opacity / c).toFixed(3)
}));
}
return out.insertBefore(this).translate(s.offsetx, s.offsety);
};
So if you just fix the stroke-width for all of these paths, it kills the glow effect as you will see in the example. There isn't really an easy answer to this. You could possibly animate it yourself using setInterval to remove the old glow and add a new one with a new width, but it doesn't sound like a very efficient method.
i've been able to correct this issue without the timing issue as shown in your jsfiddle demo by adding the following to resume.
elproto.resume = function (anim) {
for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
var e = animationElements[i];
if (eve("raphael.anim.resume." + this.id, this, e.anim) !== false) {
delete e.paused;
this.status(e.anim, e.status,**e.totalOrigin**);
}
}
return this;
};
Related
If the left half of the screen is a specific rgb value, and the right is another, how would one smoothly transition depending on mouse position between those two on mousemove?
So that when the mouse in at the far left, it's one colour, and far right it's the other. In the middle it would be the half way point.
Something like this: http://jsfiddle.net/j08691/BrZjJ/ - But based on set colours, not arbitrary ones.
var $el = $('div');
var p_x, p_y,
window_width = window.innerWidth,
window_height = window.innerHeight;
var m = {
moving_left: false,
moving_up: false,
last_x: 0,
last_y: 0,
x: 0,
y: 0
};
var colors = {
orange: { r: 238, g: 119, b: 0 },
blue: { r: 40, g: 203, b: 215 }
};
$el.on('mousemove', function(e){
m.x = e.pageX;
m.y = e.pageY;
m.moving_left = (m.x < m.last_x) ? true : false;
m.moving_up = (m.y < m.last_y) ? true : false;
m.last_x = m.x;
m.last_y = m.y;
});
function animateBackground() {
p_width = m.x / window_width;
p_height = m.y / window_height;
p_x = p_width.toFixed(2);
p_y = p_height.toFixed(2);
switch(true) {
// top left
case p_x <= 0.5 && p_y <= 0.5:
fr = colors.orange.r;
fg = colors.orange.g;
fb = colors.orange.b;
break;
// bottom right
default:
fr = colors.blue.r;
fg = colors.blue.g;
fb = colors.blue.b;
break;
}
$el.css({
backgroundColor: 'rgb('+fr+', '+fg+', '+fb+')'
});
requestAnimationFrame(animateBackground);
}
requestAnimationFrame(animateBackground);
https://jsfiddle.net/o32juay5/
I'm using something similar to the above, (except using 4 colours on four corners, but for simplicity...). I just can't work out in my head how to transition between two colours...
Any ideas?
Thanks.
You need to calculate the colors mathematically according to the mouse position.
For each of the R, G, B values: Take values for the two colors, determine the range between them, select a position inside that range according to the mouse position, and combine them into a background-color to be used in CSS.
https://jsfiddle.net/kbpyxxcn/6/
If you want to use this method with four colors, your calculations will become more complicated, unless you also define a color for the center.
Add the CSS property transition and it works:
https://jsfiddle.net/o32juay5/2/
div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-ms-transition: background-color 1s;
-webkit-transition: background-color 1s;
transition: background-color 1s;
}
If you make it with javascript the performance will be less than this css property.
So basically what I need to do is to load an SVG with SNAP.svg and add an effect (zoom in) I understand that in order to achieve this I need to respect this :
Load the SVG file
Scale de SVG in (0 ?)
Append this SVG
Add a transform effect (with the suitable scale)
The problem is that I need to display this in a 650 width and 280 height size.
The SVG I'm loading, witch I'll name it 'map' is in 1920 width and 1080 height.
This is my code so far :
<svg id="svg" width="650px" height="280px"></svg>
<script type="text/javascript">
var s = Snap("#svg");
var map = Snap.load("./src/map.svg", function (f) {
g = f.select("g");
var t = Snap.matrix().scale(0.35);
s.append(g);
g.group(g.selectAll("path")).transform(t);
});
</script>
It seems the scale instruction is working find but not the animation.
Also, how I can center this loaded SVG not matter what scale it takes ?
Thank you !
UPDATE :
I managed to add some effects but I don't think the way I'm doing it it's the correct one :
var carte = Snap.load("./src/carte.svg", function (f) {
g = f.select("g");
//var t = Snap.matrix().scale(0.35);
s.append(g);
//Set the map in first position
var firstScene = new Snap.Matrix();
firstScene.translate(300, 160);
firstScene.scale(0.05);
//Zoom effect
var secondScene = new Snap.Matrix();
secondScene.scale(2.0);
secondScene.translate(-850, -360);
//Move the matrix till desired point (not finish)
var threeScene = new Snap.Matrix();
threeScene.translate(-850, -360);
g.animate({ transform: firstScene }, 0, function() {g.animate ({ transform: secondScene}, 1500, mina.linear )});
});
It seems impossible to add a timer or more than two effects ?
Just as an alternative if there are quite a few sequenced animations I may be tempted to write a function to handle an array of animations. It could look something like this...
function nextFrame ( el, frameArray, whichFrame ) {
if( whichFrame >= frameArray.length ) { return }
el.animate( frameArray[ whichFrame ].animation,
frameArray[ whichFrame ].dur,
frameArray[ whichFrame ].easing,
nextFrame.bind( null, el, frameArray, whichFrame + 1 ) );
}
var block = s.rect(100, 100, 100, 100, 20, 20);
.attr({ fill: "rgb(236, 240, 241)", stroke: "#1f2c39",
strokeWidth: 3, transform: 's1' });
var frames = [
{ animation: { transform: 's0.4,0.4,200,200' }, dur: 1000, easing: mina.bounce },
{ animation: { transform: 't-100,-80' }, dur: 1000, easing: mina.bounce },
{ animation: { transform: 's1.2,1.2,300,300t200,-100' },dur: 1000, easing: mina.bounce }
];
nextFrame( block, frames, 0 );
jsfiddle
I think if you want to sequence animations, the most elegant way would be to use promises. For that all you would need is to wrap animate function in Q library or jQuery.Deferred. Here is the example I put together on jsFiddle https://jsfiddle.net/stsvilik/Lnhc77b2/
function animate(obj, conf, duration, asing) {
var def = Q.defer();
obj.animate(conf, dur, easing, function(){
def.resolve();
});
return def.promise;
}
This seems to work fine, but like lan said above, maybe his method it's better for doing animations
var carte = Snap.load("./src/carte.svg", function (f) {
g = f.select("g");
//var t = Snap.matrix().scale(0.35);
s.append(g);
//Load the map
var firstScene = new Snap.Matrix();
firstScene.translate(295, 160);
firstScene.scale(0.04);
//Set scene 1
var secondScene = new Snap.Matrix();
secondScene.scale(0.4);
secondScene.translate(-300, -10);
//Set scene 2
var threeScene = new Snap.Matrix();
//threeScene.scale(0.5);
threeScene.translate(-825, -380);
//Set scene 3
var fourScene = new Snap.Matrix();
fourScene.scale(21.0);
fourScene.translate(-1164, -526);
var anim1 = function() {
g.animate({ transform: firstScene}, 0, anim2);
}
var anim2 = function() {
g.animate({ transform: secondScene}, 1500, mina.easing, anim3);
}
var anim3 = function() {
g.animate({ transform: threeScene}, 1000, mina.linear, anim4);
}
var anim4 = function() {
g.animate({ transform: fourScene}, 2000, mina.easing);
}
anim1();
});
Is the fact of adding several matrix a performance killer ? Or this is the way it should be done ?
I am trying to animate shape continue from from X to Y distance, again return shape from Y to X using kinetic js animate function. eg. move shape from 0px to 200px and again return shape 200px to 0px.
thanks.
You are probably looking for a Tween here rather then an Animation. Check out the kinetic documentation for more information on Tweens, however what you are looking to do will probably look something like this:
var tween = new Kinetic.Tween({
node: myShape,
x: moveToX,
y: moveToY,
duration: 1
});
Now that you have a tween, you can play it, rewind it, pause it, reverse it (again, check out the documentation for all up to date information on Tweens).
So you can now do:
tween.play()
or
tween.reverse()
to accomplish what you are looking for.
Reference: http://www.html5canvastutorials.com/kineticjs/html5-canvas-stop-and-resume-transitions-with-kineticjs/
Update (as per comment below): If you want a looping affect, in an X direction, Y direction, or both. You can do something like:
var yPos = myShape.getAttr('y'),
xPos = myShape.getAttr('x'),
maxX = 1000,
maxY = 1000,
yIncreasing = true,
xIncreasing = true; //lets assume myShape resides somewhere below the max
var anim = new Kinetic.Animation(function(frame) {
/* move the shape back and fourth in a y direction */
if (yPos < maxY && yIncreasing) {
hexagon.setY(yPos++);
} else {
if (yPos < 1) {
yIncreasing = true;
hexagon.setY(yPos++);
} else {
hexagon.setY(yPos--);
yIncreasing = false;
}
}
/* move the shape back and fourth in a x direction */
if (xPos < maxX && xIncreasing) {
hexagon.setX(xPos++);
} else {
if (xPos < 1) {
xIncreasing = true;
hexagon.setX(xPos++);
} else {
hexagon.setX(xPos--);
xIncreasing = false;
}
}
}, layer);
Note: I haven't ran this code, it should work. Using both will cause the shape to move diagonally, but hopefully this snipplet shows a solution to your problem.
I'm using the latest version of Snap.svg to draw and animate a path within a SVG:
var s = Snap('#svg');
var getPath = s.path('M15 15L115 115');
var pathLength = getPath.getTotalLength();
getPath.attr({
stroke: '#000',
strokeWidth: 5,
strokeDasharray: pathLength + ' ' + pathLength,
strokeDashoffset: pathLength,
strokeLinecap: 'round'
}).animate({
strokeDashoffset: 0
}, 1500);
While this is working fine (as you can see here), I want to make it a dotted line, animated one dot after another.
I've built a quick prototype with circles (which you can see here), to illustrate the look and feel, but technically I want it to base on a custom path.
Basically I'm looking for a way to animate a dotted (complex) path; so a path with attributes would be as fine as circles on a path.
since stroke-dasharray can be an array of values you can leave the stroke-dashoffset at 0 and update the stroke-dasharray until you get to the complete line.
something like this although this example is not really smooth and optimized.
var s = Snap('#svg');
var getPath = s.path('M15 15L115 115');
var pathLength = getPath.getTotalLength();
var perc = 0;
var dotLength = 5;
var gapLength = 4;
getPath.attr({
stroke: '#000',
strokeWidth: 1,
strokeDasharray: 0,
strokeDashoffset: 0,
strokeLinecap: 'round'
});
function updateLine(){
perc +=1;
if(perc>100){
perc = 100;
}
var visibleLength = pathLength*perc/100;
var drawnLength = 0;
var cssValue = '';
while(drawnLength < visibleLength){
drawnLength += dotLength;
if(drawnLength < visibleLength){
cssValue += dotLength+ ' ';
drawnLength += gapLength;
if(drawnLength < visibleLength){
cssValue += gapLength+ ' ';
}
}else{
cssValue += (visibleLength + dotLength - drawnLength)+ ' ';
}
}
cssValue += pathLength;
if(perc<100){
setTimeout(updateLine, 100);
}
getPath.attr({
strokeDasharray: cssValue
});
}
updateLine();
http://jsfiddle.net/EEe69/7/
If you want the gaps to be "skipped" on the animation, you should substract them from the pathLength
Why not use d3?
There's an example doing something similar with a dotted line, based on mouse movement. I started a timed function to do what you're looking to do, but I think you can figure it out :)
Look at this part, and see if you can adjust it to do a specific path instead of d3.mouse:
d3.timer(function(step) {
var svgagain = d3.select("body").select("svg")
.on("mousemove", function() { var pt = d3.mouse(this); tick(pt); });
});
If I have a CSS keyframe animation like this
#keyframes flash-red {
50% {
background: #f00;
}
}
#goflash.anm-flash {
animation-name: flash-red;
animation-duration: .5s;
background: rgba(255, 0, 0, 0);
}
Then I can always trigger the animation like this:
var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
}, 50);
Is there any way to override the animation-duration/animation-timing-function to be dependent on JavaScript? I'd like to be able to say something like gf.animate("flash-red", "50%") to make the background of gf red, or gf.animate("flash-red", "75%") to make the background more like rgba(255, 0, 0, .5).
Ideally, the same technique would work for transitions. gf.transitionTo("new-class", "50%") would show the element as half way transitioned.
Obviously the flash-red is just an example—I'd like to be able to do this with any animation.
With the built-in animation:
Unfortunately, no
The internals of the transition isn't exposed for JavaScript so you cannot tap into it to set or get the data. And this is for a purpose - if the data were exposed it would mean reduced efficiency as the JavaScript event queue had to be updated. As JS is single-threaded and the animation goes on a separate thread you'll would soon loose the benefit of it running in compiled code internally on a separate thread.
You can however make your own transitions. This involve calculation transitions yourselves.
This is not as complicated as it sounds like as you simply use an interpolation formula for what you want to animate:
current = source + (destination - source) * fraction;
For example, for color you can use it with the color component. Lets assume we have color objects with properties r, g, b:
var color1 = {r: 100, g: 200, b: 55}; //some random color
var color2 = {r: 0, g: 100, b: 100};
var fraction = 0.5; //0-1
Here the current RGB would be:
r = color1.r + (color2.r - color1.r) * fraction;
g = color1.g + (color2.g - color1.g) * fraction;
b = color1.b + (color2.b - color1.b) * fraction;
For positions:
var pos1x = 100;
var pos1y = 100;
var pos2x = 500;
var pos2y = 250;
var fraction = 1; //0-1
posX = pos1x + (pos2x - pos1x) * fraction;
posY = pos1y + (pos2y - pos1y) * fraction;
And so forth.
By making wrapper functions you can easily calculate these and even put them in a loop to animate them.
Example function for setting transition between color 1 and color 2.
Style can be ie. backgroundColor, color etc.:
function setColor(element, style, color1, color2, fraction) {
var r = color1.r + (color2.r - color1.r) * fraction;
var g = color1.g + (color2.g - color1.g) * fraction;
var b = color1.b + (color2.b - color1.b) * fraction;
element.style[style] = 'rgb(' + (r|0) + ',' + (g|0) + ',' + (b|0) + ')';
}
(the r|0 is simply cutting off the decimal part).
And for position, for example:
var pos1 = {x: 0, y: 0};
var pos2 = {x: 200, y: 0};
function setPosition(element, pos1, pos2, fraction) {
var x = pos1.x + (pos2.x - pos1.x) * fraction;
var y = pos1.y + (pos2.y - pos1.y) * fraction;
element.style.left = x + 'px';
element.style.top = y + 'px';
}
A simple demo (use Chrome or Aurora 23 to see sliders, slider comes in next version of FF 23).
Fiddle
Manually set transition at any point between source and destiny, or animate them.
say you have only one animation over your element gf, you can simply control it with animation-delay and animation-play-state:
gf.__proto__.animate = function(percent) {
this.style["animation-play-state"] = "paused";
this.style["animation-delay"] = (
(parseFloat(this.style["animation-duration"] || 1) * -percent) + "s"
);
};
and you can get the computed style as following:
window.getComputedStyle(gf).background
to step through at any speed:
(function animation(time) {
gf.animate( ((time || 0) % desireSpeed ) / desireSpeed );
requestAnimationFrame(animation);
})();
note: this will override animation-delay from css so you'll probably want to keep it in a vairable and add it as an offset in gf.__proto__.animate().
You can't do that as you want it.
Your only posibility is to change play-state after a given delay.
In your case, since the animation lasts 0.5 seconds, to get the animation at 50% you should set a timeout of 0.25 seconds and then set animation-play-state : paused.
Of course that won't be exactly at 50%, don't trust the precision of this method.
editing
Added demo for webkit:
fiddle
The HTML is trivial
<div id="goflash">TEST</div>
<input type="button" value="animate" onclick="animate()">
And the CSS easy
#goflash {
width: 200px;
height: 200px;
left: 35px;
top: 35px;
position: absolute;
background-color: red;
}
.anm-flash {
-webkit-animation-name: flash;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: 1;
}
#-webkit-keyframes flash {
from { -webkit-transform: rotate(0deg);
background-color: red; }
50% { -webkit-transform: rotate(120deg);
background-color: yellow;}
to { -webkit-transform: rotate(360deg);
background-color: red;
}
}
And the javascript is an extension from what you supplied:
function animate () {
var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
gf.style.webkitAnimationPlayState = "running";
}, 50);
setTimeout(function() {
gf.style.webkitAnimationPlayState = "paused";
}, 2550);
}
You reset the class, after a small pause start the animation, and a calculated delay after the start, you stop it.
Since the animation time was 5s,and the initial delay 50 ms, the second delay has to be (5000/2) + 50.
Since you have set now the play state to paused, to de able to re-run the animation you have to set the state to running again.
Perhaps using CSS DOM to parse animation's intent (if that's even possible?) and then reconstructing everything in JavaScript.
But that's no mean feat!
I wonder if a CSS preprocessor would help constructing code like this. This is very much all in theory.
Yes,
You can just overide the duration or timing of an animation. Hope I understood what you want to do:
http://jsfiddle.net/SEHyW/
var gf = document.querySelector("#goflash"),
animationDuration = '1s'
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
gf.style["-webkit-animation-duration"] = animationDuration;
}, 1000);