I'm using the Raphaël Javascript lib (awesome stuff for SVG rendering, by the way) and am currently trying to update the source of an image as the mouse goes over it.
The thing is I can't find anything about it (it's probably not even possible, considering I've read a huge part of the Raphaël's source without finding anything related to that).
Does someone knows a way to do this ?
Maybe it can be done without directly using the Raphaël's API, but as the generated DOM elements doesn't have IDs I don't know how to manually change their properties.
I'm actually doing CoffeeScript, but it's really easy to understand. CoffeeScript is Javascript after all.
This is what I'm doing right know, and I would like the MouseOver and MouseOut methods to change the source of the "bg" attribute.
class Avatar
constructor: (father, pic, posx, posy) ->
#bg = father.container.image "pics/avatar-bg.png", posx, posy, 112, 112
#avatar = father.container.image pic, posx + 10, posy + 10, 92, 92
mouseOver = => #MouseOver()
mouseOut = => #MouseOut()
#bg.mouseover mouseOver
#bg.mouseout mouseOut
MouseOver: ->
#bg.src = "pics/avatar-bg-hovered.png"
alert "Hover"
MouseOut: ->
#bg.src = "pics/avatar-bg.png"
alert "Unhovered"
class Slider
constructor: ->
#container = Raphael "raphael", 320, 200
#sliderTab = new Array()
AddAvatar: (pic) ->
#sliderTab.push new Avatar this, pic, 10, 10
window.onload = ->
avatar = new Slider()
avatar.AddAvatar "pics/daAvatar.png"
This actually works, except for the "#bg.src" part : I wrote it knowing that it wouldn't work, but well...
var paper = Raphael("placeholder", 800, 600);
var c = paper.image("apple.png", 100, 100, 600, 400);
c.node.href.baseVal = "cherry.png"
I hope, you get the idea.
This works for me (and across all browsers):
targetImg.attr({src: "http://newlocation/image.png"})
I was using rmflow's answer until I started testing in IE8 and below which returned undefined for image.node.href.baseVal. IE8 and below did see image.node.src though so I wrote functions getImgSrc, setImgSrc so I can target all browsers.
function getImgSrc(targetImg) {
if (targetImg.node.src) {
return targetImg.node.src;
} else {
return targetImg.node.href.baseVal;
}
}
function setImgSrc(targetImg, newSrc) {
if (targetImg.node.src) {
targetImg.node.src = newSrc;
} else {
targetImg.node.href.baseVal = newSrc;
}
}
Related
I cannot understund, why IE and EDGE has strange behavior.
If you rapidly move the mouse over and out of pentagram -- animation is distancing, but in other NORMAL web-browsers all in order.
http://jsfiddle.net/b1Lhjo4k/
var svgElement = Snap("#svg");
var pent = svgElement.select('#pentagram-one');
var hoverover = function() {
pent.stop().animate({transform: 'r180,500,515'}, 400);
};
var hoverout = function() {
pent.stop().animate({transform: 'r0,500,515'}, 400);
};
pent.hover(hoverover, hoverout);
Solved by prototype function; ie and edge works now correctly.
I don't have Edge to test, but I suspect its because its doing something funky with Matrix/transform interpolation when starting an animation before the previous one has finished (this may be wrong, but just what initial thoughts would be to look at), or it could be something odd with very small number strings sometime as I've seen those before with IE.
One way around this, would be to not let Snap do the interpolation using the browsers matrix values, but control it yourself. You could try a small plugin for it, if you will use it a lot, something like this...
Snap.plugin( function( Snap, Element, Paper, global ) {
Element.prototype.animRotate = function( from, to, dur ) {
var el = this;
this.stop();
Snap.animate(from, to, function( val ) {
el.transform('r' + val + ',500,515')
},dur)
return this;
};
});
var pent = svgElement.select('#pentagram-one');
var hoverover = function() {
this.animRotate(0, 180, 400)
};
var hoverout = function() {
this.animRotate(180, 0, 400)
};
jsfiddle
I would like to add category icons to a Wordpress page, each icon animated with snap.svg.
I added the div and inside an svg in the loop that prints the page (index.php). All divs are appearing with the right size of the svg, but blank.
The svg has a class that is targeted by the js file.
The js file is loaded and works fine by itself, but the animation appears only in the first div of that class, printed on each other as many times it is counted by the loop (how many posts there are on the actual page from that category).
I added "each()" and the beginning of the js, but is not allocating the animations on their proper places. I also tried to add double "each()" for the svg location and adding the snap object to svg too, but that was not working either.
I tried to add unique id to each svg with the post-id, but i could not pass the id from inside the loop to the js file. I went through many possible solutions I found here and else, but none were adaptable, because my php and js is too poor.
If you know how should I solve this, please answer me. Thank you!
// This is the js code (a little trimmed, because the path is long with many randoms, but everything else is there):
jQuery(document).ready(function(){
jQuery(".d-icon").each(function() {
var dicon = Snap(".d-icon");
var dfirepath = dicon.path("M250 377 C"+ ......+ z").attr({ id: "dfirepath", class: "dfire", fill: "none", });
function animpath(){ dfirepath.animate({ 'd':"M250 377 C"+(Math.floor(Math.random() * 20 + 271))+ .....+ z" }, 200, mina.linear);};
function setIntervalX(callback, delay, repetitions, complete) { var x = 0; var intervalID = window.setInterval(function () { callback(); if (++x === repetitions) { window.clearInterval(intervalID); complete();} }, delay); }
var dman = dicon.path("m136 ..... 0z").attr({ id: "dman", class:"dman", fill: "#222", transform: "r70", });
var dslip = dicon.path("m307 ..... 0z").attr({ id: "dslip", class:"dslip", fill: "#196ff1", transform:"s0 0"});
var dani1 = function() { dslip.animate({ transform: "s1 1"}, 500, dani2); }
var dani2 = function() { dman.animate({ transform: 'r0 ' + dman.getBBox().cx + ' ' + dman.getBBox(0).cy, opacity:"1" }, 500, dani3 ); }
var dani3 = function() { dslip.animate({ transform: "s0 0"}, 300); dman.animate({ transform: "s0 0"}, 300, dani4); }
var dani4 = function() { dfirepath.animate({fill: "#d62a2a"}, 30, dani5); }
var dani5 = function() { setIntervalX(animpath, 200, 10, dani6); }
var dani6 = function() { dfirepath.animate({fill: "#fff"}, 30); dman.animate({ transform: "s1 1"}, 100); }
dani1(); }); });
I guess your error is here:
var dicon = Snap(".d-icon");
You are passing a query selector to the Snap constructor, this means Snap always tries to get the first DOM element with that class, hence why you're getting the animations at the wrong place.
You can either correct that in two ways:
Declare width and height inside the constructor, for example var dicon = Snap(800, 600);
Since you are using jQuery you can access to the current element inside .each() with the $(this) keyword. Since you are using jQuery instead of the dollar you could use jQuery(this).
Please keep in mind this is a jQuery object and probably Snap will require a DOM object. In jQuery you can access the dom object by appending a [0] after the this keyword. If var dicon = Snap( jQuery(this) ); does not work you can try with var dicon = Snap( jQuery(this)[0] );
Additionally, you have several .attr({id : '...', in your code. I assume you are trying to associate to the paths an ID which are not unique. These should be relatively safe since they sit inside a SVG element and I don't see you are using those ID for future selection.
But if you have to select those at a later time I would suggest to append to these a numerical value so you wont have colliding ID names.
I'm developing some page when I use Raphael liblary to draw some items.
my App
So my problem is in that when I'm moving to some rect it growing up but when my mouse is on text which is positioning on my rect, it loss his hover. You can see it on my app example.
var paper = new Raphael(document.getElementById('holder'), 500, object.length * 100);
drawLine(paper, aType.length, bType.length, cType.length, cellSize, padding);
process = function(i,label)
{
txt = paper.text(390,((i+1)* cellSize) - 10,label.devRepo)
.attr({ stroke: "none", opacity: 0, "font-size": 20});
var a = paper.rect(200, ((i+1)* cellSize) - 25, rectWidth, rectHeight)
.hover(function()
{
this.animate({ transform : "s2"}, 1000, "elastic");
this.prev.animate({opacity: 1}, 500, "elastic");
this.next.attr({"font-size" : 30});
},
function()
{
this.animate({ transform : "s1" }, 1000, "elastic");
this.prev.animate({opacity: 0}, 500);
this.next.attr({"font-size" : 15});
});
}
I have tried e.preventDefault(); on hover of this.next and some other solutions but it's doesn't work.
Any help would be appreciated.
Most people will suggest you place a transparent rectangle over the box and the labels and attach the hover functions to that instead. (If memory serves, you have to make the opacity 0.01 instead of 0 to prevent the object from losing its attached events.) This works fine, but I don't love this solution; it feels hacky and clutters the page with unnecessary objects.
Instead, I recommend this: Remove the second function from the hover, making it functionally a mouseover function only. Before you draw any of the rectangles and labels, make a rectangular "mat" the size of the paper. Then, attach the function that minimizes the label as a mouseover on the mat. In other words, you're changing the trigger from mousing out of the box to mousing over the area outside of it.
I left a tiny bit of opacity and color on the mat to be sure it's working. You can just change the color to your background color.
var mat = paper.rect(0, 0, paper.width, paper.height).attr({fill: "#F00", opacity: 0.1});
Now, you want to make a container for all the rectangles so you can loop through them to see which need to be minimized. I made an object called "rectangles" that contains the objects we're concerned with. Then:
mat.mouseover(function () {
for (var c = 0; c < rectangles.length; c += 1) {
//some measure to tell if rectangle is presently expanded
if (rectangles[c].next.attr("font-size")) {
rectangles[c].animate({
transform : "s1"
}, 1000, "elastic");
rectangles[c].prev.animate({opacity: 0}, 500);
rectangles[c].next.attr({"font-size" : 15});
}
}
});
Then I just removed the mouseout function from the individual rectangles.
jsBin
To be clear, this will have some downsides: If people run the mouse around really fast, they can expand several rectangles at the same time. This is remedied as soon as the mouse touches the mat. I think the functionality looks pretty nice. But the invisible mats is always an option.
I wrote a small extension to Raphael - called hoverInBounds - that resolves this limitation.
Demo: http://jsfiddle.net/amustill/Bh276/1
Raphael.el.hoverInBounds = function(inFunc, outFunc) {
var inBounds = false;
// Mouseover function. Only execute if `inBounds` is false.
this.mouseover(function() {
if (!inBounds) {
inBounds = true;
inFunc.call(this);
}
});
// Mouseout function
this.mouseout(function(e) {
var x = e.offsetX || e.clientX,
y = e.offsetY || e.clientY;
// Return `false` if we're still inside the element's bounds
if (this.isPointInside(x, y)) return false;
inBounds = false;
outFunc.call(this);
});
return this;
}
I want to create an animation to scroll the page smoothly when clicking on anchor links, just like jQuery.ScrollTo plugin (http://demos.flesler.com/jquery/scrollTo/) does it.
I tried making it using YUI 2.x Animation utility, by animating the value of the property document.activeElement.scrollTop. It works on webkit only :'( - on the other browser, nothing happens - not even an error is raised.
goToAnchor = function(e, id) {
var targetToGo = Dom.get(id),
scrollToTarget = new Animation(document.activeElement,
{
scrollTop:
{
from: document.activeElement.scrollTop,
to: targetToGo.offsetTop
}
}, 1, Easing.easeOut
)
Event.preventDefault(e);
scrollToTarget.animate();
}
What I'd like to know is if there's a plugin that does this for YUI 2.x or how to do a cross browser compatible code to do so.
Thanks!
You need to keep in mind that depending on browser you might need to scroll the html or the body element.
(practially, you need to scroll both to be sure)
Also at http://developer.yahoo.com/yui/animation/#scroll i see
var element = document.getElementById('test');
var myAnim = new YAHOO.util.Scroll(element, {
scroll: {
to: [ 500, test.scrollTop ]
}
});
myAnim.animate();
Maybe that is what you are looking for (still you will have to animate both html and body)
<script>
(function() {
var scrollingBody = document.body;
if (YAHOO.env.ua.gecko){
scrollingBody = document.documentElement;
}
(new YAHOO.util.Scroll(
scrollingBody,
{
scroll:
{
to: [0, 50]
}
},
0.7,
YAHOO.util.Easing.easeOut
)).animate();
})();
</script>
I've just built an SVG map of New Zealand for use with the excellent javascript library Raphael, but unfortunately have stumbled upon what I can only imagine is a bug or syntactic variation in IE's javascript interpreter.
In Firefox and other browsers the onlick and onmouseover events work perfectly - however they do not fire in IE (tested in IE 7). Unfortunately there is no javascript error to help me debug this, so I can only assume IE handles these events in some fundamentally different way.
<script type="text/javascript" charset="utf-8">
window.onload = function() {
var R = Raphael("paper", 450, 600);
var attr = {
fill: "#3f3f40",
stroke: "#666",
"stroke-width": 1,
"stroke-linejoin": "round"
};
var nz = {};
nz.northland = R.path(attr, "M 193.34222,3.7847503 C 194.65463");
// SVG data stripped for sake of brevity
var current = null;
for (var region in nz) {
nz[region].color = Raphael.getColor();
(function(rg, region) {
rg[0].style.cursor = "pointer";
rg[0].onmouseover = function() {
current && nz[current].animate({ fill: "#3f3f40", stroke: "#666" }, 500) && (document.getElementById(current).style.display = "");
rg.animate({ fill: rg.color, stroke: "#ccc" }, 500);
rg.toFront();
R.safari();
document.getElementById(region).style.display = "block";
current = region;
};
rg[0].onclick = function() {
alert("IE never gets this far.");
//window.location.href = "my-page.aspx?District=" + region;
};
rg[0].onmouseout = function() {
rg.animate({ fill: "#3f3f40", stroke: "#666" }, 500);
};
if (region == "northland") {
rg[0].onmouseover();
}
})(nz[region], region);
}
};
</script>
Many thanks :)
The fix appears to be using the onmousedown event instead of onclick.
Changing:
rg[0].onclick = function() {
alert("IE never gets this far, but Firefox is happy.");
};
to
rg[0].onmousedown = function() {
alert("This works in IE and Firefox.");
};
resolved the issue. Thanks for everyone's input - got there in the end. If anyone actually knows why IE doesn't like onclick, I'd be interested to hear!
Have you tried attaching the events?
if (rg[0].attachEvent)
rg[0].attachEvent("onclick", function(){ /* IE */ });
else
rg[0].addEventListener("click", function(){ /* other */ }, false);
IE is not exactly known for working correctly. It would help if you mentioned which IE version you are using.
Generally an abstraction framework, like jquery or prototype is your best bet. They handle the browser differences for you. Also, you can subscribe to your events at a higher level... it's less expensive in the browser to subscribe to the mousemove/click, and determine what you're over, or clicking on from the event bubble than to many objects.
Joel Potter mentioned the subscriber model, using the dom and IE methods, which is better practice than the method assignment is.