I'm trying to make a simple game using Raphaƫl.js and I came across a problem very early in development, where I made a sight for aiming by attaching a circle to the cursors position, but when I click the target it doesn't trigger the targets click event , but the aim.
background.mousemove(function(e){
ePointer.attr({
'fill':'none',
'stroke':'none'
});
nx = e.clientX - $('#canvas').offset().left;
ny = e.clientY - $('#canvas').offset().top;
pointer.attr({
'fill':'none',
'stroke':'#CC0000',
'stroke-width': 3,
'cx':nx,
'cy':ny
});
});
background.click(function(e){
pointer.animate(bClick);
pointer.animate(bUnclick.delay(250));
pointer.animate(bUnclick);
});
enemies.mousemove(function(e){
pointer.attr({
'fill':'none',
'stroke':'none'
});
nx = e.clientX - $('#canvas').offset().left;
ny = e.clientY - $('#canvas').offset().top;
ePointer.attr({
'cx':nx,
'cy':ny,
'fill': '#00CC00',
'stroke':'none'
});
});
The jsfiddle link will say more then I could : http://jsfiddle.net/Uuqgx/6/
Thanks in advanced,
Giovanni.
Raphael's paper object has a method called Paper.getElementByPoint(x, y) that gets the top object under the mouse. Of course, this alone doesn't help you since the circle is still under the mouse. However, if you hide it, run this method inputing the current mouse coordinates, then unhide the circle, you'll get the rectangle under it without any visible interruption:
ePointer.click(function(e){
score++
tScore.attr({
'text': 'Score: ' + score
});
nx = e.clientX - document.getElementById('canvas').offsetLeft;
ny = e.clientY - document.getElementById('canvas').offsetTop;
//get targeted rectangle
this.hide();
console.log(paper.getElementByPoint(nx, ny));
this.show();
ePointer.animate(eClick);
ePointer.animate(eUnclick.delay(150));
ePointer.animate(eUnclick);
});
Here I've just logged the targeted rectangle to the console, but once you've found it like this you can do whatever you want to it.
Note that offset().Top becomes offsetTop in order for this to work in jsFiddle, since jQuery isn't present in the fiddle.
Updated fiddle.
Related
If the paper is too big for the div it's shown in, I'd like to make the paper draggable.
I tried the papers blank:pointerdown and pointerup events but was not able to just follow the mousemovement. I also tried to make the element of the paper draggable via jquery, but nothing seems to do the trick...
Is there any way to do this?
This can be achieved with a combination of JointJS events and document events. The graph display is encapsulated in a div:
<div id='diagram'></div>
Then add the event handlers for JointJS, first the pointerdown event where we store the start position of the drag:
paper.on('blank:pointerdown',
function(event, x, y) {
dragStartPosition = { x: x, y: y};
}
);
Then the end of the drag (pointerup) when we delete the variable we store the position in (it also acts as a flag whether there is an active drag in progress):
paper.on('cell:pointerup blank:pointerup', function(cellView, x, y) {
delete dragStartPosition;
});
As JointJS does not expose a "paper pointer move" event, we need to use the mousemove document event. For example, with JQuery:
$("#diagram")
.mousemove(function(event) {
if (dragStartPosition)
paper.translate(
event.offsetX - dragStartPosition.x,
event.offsetY - dragStartPosition.y);
});
We get the drag start coordinates and the current pointer position and update the paper position using the paper.translate() call.
WARNING: if you scale the paper (using paper.scale()), you have to also scale the starting position of the drag:
var scale = V(paper.viewport).scale();
dragStartPosition = { x: x * scale.sx, y: y * scale.sy};
The calls to paper.translate() will then update to the proper position.
I know this is a slightly old thread, but I was stuck with this for a while and came across a neat solution using the SVG Pan and Zoom library. Git hub link here
EDIT - I created a plunker of the steps below (plus some extras) here:
SVG panning with Jointjs
First step after creating the paper is to initialise SVG Pan and Zoom:
panAndZoom = svgPanZoom(targetElement.childNodes[0],
{
viewportSelector: targetElement.childNodes[0].childNodes[0],
fit: false,
zoomScaleSensitivity: 0.4,
panEnabled: false
});
Where targetElement is the div that the jointjs paper has gone into. Jointjs will create a SVG element within that (hence the childNodes[0]) and within that element the first element is the viewport tag (hence childNodes[0].childNodes[0] in the viewportselector). At this stage pan is disabled, because in my use case it would intefer with drag and drop elements on the paper. Instead what I do is keep a reference to the panAndZoom object and then switch pan on and off on the blank:pointerdown and blank:pointerup events:
paper.on('blank:pointerdown', function (evt, x, y) {
panAndZoom.enablePan();
});
paper.on('cell:pointerup blank:pointerup', function(cellView, event) {
panAndZoom.disablePan();
});
Just another way of tackling the issue I guess, but I found it a bit easier, plus it gives you zoom too and you can adjust the sensitivity etc.
I suggest the following:
register a handler for the paper blank:pointerdown event that will initiate the paper dragging (store a flag which you'll use in your mousemove handler to recognize the paper is in the "panning" state).
Put the big paper in a <div> container with CSS overflow: auto. This <div> will be your little window to the large paper.
register a handler for document body mousemove event (because you most likely want the paper to be dragged even if the mouse cursor leaves the paper area?). In this handler, you'll be setting the scrollLeft and scrollTop properties of your <div> container making the paper "panning". For adjusting the scrollLeft and scrollTop properties, you'll use the clientX and clientY properties of the event object together with the same properties that you stored previously in your blank:pointerdown handler. (in other words, you need those to find the offset of the panning from the last mousemove/blank:pointerdown).
register a handler for document body mouseup and in this handler, clear your paper dragging flag that you set in step 1.
I have a webpage with an SVG. On some of its shapes I need to display a tooltip. However, I can't get the tooltip to appear where it should, just some pixels away from the shape itself.
It appears way on the right hand side of the screen, maybe some 300px away.
The code I am using to get the coordinates is as follows:
d3.select("body")
.select("svg")
.select("g")
.selectAll("circle")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){
var svgPos = $('svg').offset(),
/*** Tooltip ***/
//This should be the correct one, but is displaying at all working at all.
/*x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;*/
//This displays a tool tip but way much to the left of the screen.
x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;
Tooltip
window.alert("svgPos: "+svgPos+" top: "+y+"px left: "+x+"px "+d3.event.target.cx.animVal.value);
return tooltip.style("top", x+"px").style("left",y+"px");
})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
I got to this code following this SO post.
I have changed $(ev.target).attr(cx) as it is not returning a value on my machine; d3.event.target.cx is, even though it seems it is not affecting the end result anyway.
What am I doing wrong? Could somebody help me please? Thank you very much in advance for your time.
If your tooltip is an HTML element, then you want to position it relative to the page as a whole, not the internal SVG coordinates, so accessing the cx/cy value is just complicating things. I can't say for sure without looking at your code, but if you have any transforms on your <svg> or <g> elements, then that could be what's throwing you off.
However, there is a much easier solution. Just access the mouse event's default .pageX and .pageY properties, which give the position of the mouse relative to the HTML body, and use these coordinates to position your tooltip div.
Example here: http://fiddle.jshell.net/tPv46/1/
Key code:
.on("mousemove", function () {
//console.log(d3.event);
return tooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Even with rotational transforms on the SVG circles, the mouse knows where it is on the page and the tooltip is positioned accordingly.
There are other ways to do this, including getting a tooltip to show up in a fixed location relative to the circle instead of following the mouse around, but I just checked the examples I was working on and realized they aren't cross-browser compatible, so I'll have to standardize them and get back to you. In the meantime, I hope this gets you back on track with your project.
Edit 1
For comparison, here is the same example implemented with both an HTML tooltip (a <div> element) and an SVG tooltip (a <g> element).
http://fiddle.jshell.net/tPv46/4/
The default mouse event coordinates may be great for positioning HTML elements that are direct children of <body>, but they are less useful for positioning SVG elements. The d3.mouse() function calculates the mouse coordinates of the current event relative to a specified SVG element's coordinate system, after all transformations have been applied. It can therefore be used to get the mouse coordinates in the form we need to position an SVG tooltip.
Key code:
.on("mousemove", function () {
var mouseCoords = d3.mouse(
SVGtooltip[0][0].parentNode);
//the d3.mouse() function calculates the mouse
//position relative to an SVG Element, in that
//element's coordinate system
//(after transform or viewBox attributes).
//Because we're using the coordinates to position
//the SVG tooltip, we want the coordinates to be
//with respect to that element's parent.
//SVGtooltip[0][0] accesses the (first and only)
//selected element from the saved d3 selection object.
SVGtooltip
.attr("transform", "translate(" + (mouseCoords[0]-30)
+ "," + (mouseCoords[1]-30) + ")");
HTMLtooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Note that it works even though I've scaled the SVG with a viewBox attribute and put the tooltip inside a <g> with a transform attribute.
Tested and works in Chrome, Firefox, and Opera (reasonably recent versions) -- although the text in the SVG tooltip might extend past its rectangle depending on your font settings. One reason to use an HTML tooltip! Another reason is that it doesn't get cut off by the edge of the SVG.
Leave a comment if you have any bugs in Safari or IE9/10/11. (IE8 and under are out of luck, since they don't do SVG).
Edit 2
So what about your original idea, to position the tooltip on the circle itself? There are definite benefits to being able to position the tip exactly: better layout control, and the text doesn't wiggle around with the mouse. And most importantly, you can just position it once, on the mouseover event, instead of reacting to every mousemove event.
But to do this, you can no longer just use the mouse position to figure out where to put the tooltip -- you need to figure out the position of the element, which means you have to deal with transformations. The SVG spec introduces a set of interfaces for locating SVG elements relative to other parts of the DOM.
For converting between two SVG transformation systems you use SVGElement.getTransformToElement(SVGElement); for converting between an SVG coordinate system and the screen, you use SVGElement.getScreenCTM(). The result are transformation matrices from which you can
extract the net horizontal and vertical translation.
The key code for the SVG tooltip is
var tooltipParent = SVGtooltip[0][0].parentNode;
var matrix =
this.getTransformToElement(tooltipParent)
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
SVGtooltip
.attr("transform", "translate(" + (matrix.e)
+ "," + (matrix.f - 30) + ")");
The key code for the HTML tooltip is
var matrix = this.getScreenCTM()
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
absoluteHTMLtooltip
.style("left",
(window.pageXOffset + matrix.e) + "px")
.style("top",
(window.pageYOffset + matrix.f + 30) + "px");
Live example: http://fiddle.jshell.net/tPv46/89/
Again, I'd appreciate a confirmation comment from anyone who can test this in Safari or IE -- or any mobile browser. I'm pretty sure I've used standard API for everything, but just because the API is standard doesn't mean it's universally implemented!
Ciao, I am trying to accomplish this: I would be able to know wich 3d object is double clicked in my scene, and animate camera position to bring the object in the center of the screen.
I tried without success to adapt the interactive cubes example that uses raycaster and projector...
http://www.gioblu.com/GiO/web/solarsystem/index_backup
As you can see you can navigate in space and change camera position with right and left mouse button. I would be able to come back to initial camera position (earth in the center of the screen) with a double click on the planet.
Why didn't you had success by adapting the example? What errors are occuring? Did you mean this example?
Because raycaster and projector is the way you are looking for.
First of all you need an eventListener for the ondblclick Event on your container. In the event function you can copy&paste from the linked example:
1) Saving mouse coordinates
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
2) Project coordinates to world system via the camera and create a ray
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
3) Check if the double clicked element is your planet
var intersects = raycaster.intersectObject( "your_planet" );
if ( intersects.length > 0 ) {
reset your camera
}
Hope this helps!
I've got two series in an Highchart graph. Both are handling event like mousemove (to show tooltip) and mouse click.
Unfortunatly one of those series is an area serie which block any event to be triggered by the other one.
Let's say I've got those series with those plot options :
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function() {
alert ('Category: '+ this.category +', value: '+ this.y);
}
}
}
}
},
series: [{
color:'red',
data: [29.9, 51.5, 16.4, 19.2, 44.0, 16.0, 35.6, 48.5, 26.4, 4.1, 9.6, 54.4]
},
{
type:'area',
data: [39.9, 75.5, 120.4, 150.2, 130.0, 120.0, 140.6, 158.5, 216.4, 194.1, 95.6, 54.4]}
]
}
as seen in this JSFiddle
I can never click or see the tooltip on the red series points.
What I could do is to change the order of the series so the red one is over the blue one.
( like this). But I've got situations where I've got several area graphs over each other like that. In this case, change the order won't help.
Is there a way I can handle events through the fill area?
Ok I found a way that suit my need : manimulating SVG element order as indicated in this topic :
SVG re-ordering z-index (Raphael optional)
I'm using this code in the redraw event of the chart :
redraw : function(){
//get a reference on the first series svg element.
var svgSeriesParent = document.querySelectorAll('.highcharts-series')[0];
//loop on all the path drawn by highchart
var pathArray = document.querySelectorAll('path');
for (var pathIndex = 0; pathIndex < pathArray.length; pathIndex++) {
var pathObj = pathArray[pathIndex];
//if attribute fill is not "none", this is an area path.
if ($(pathObj).attr('fill') !== "none") {
//move the current fill path before the first series svg element.
svgSeriesParent.insertBefore(pathObj, svgSeriesParent.firstChild);
}
}
}
See the result in this JSFiddle
Limitation :
The fill area doesn't have the same svg group anymore thus, hidding / showing the series is broken : all the fill area are considered being part of the first series. (click in the legend to see it).
In my case I don't need to hide series so I'll go on with this solution.
The svg order manipulation is not complicated that's why I would expect highchart to manage this case : add a fillZIndex parametter doesn't seem difficult and could be pretty usefull...
Only what comes to my mind is using 4 series, 2 area and 2 lines and use index.
http://jsfiddle.net/j8qYK/3/
A few small questions on this web page I am putting together.
http://dansiop.com/guyc/our-team/
I am looking to get the content that appears when you click on an image to slide with the mouse as you scroll down. Not sure if this is possible, if so can anyone help.
I have tried looking but as I am such a n00b to jquery I'm not sure what I am looking for. I found some code that used the tag slide and it was not what i wanted at all so not sure what the command would be.
You need to wrap your prof_1, prof_2, etc, in a wrapper I'm going to call this wrapper prof_wrapper
<div id="prof_wrapper"></div>
jQuery(document).ready( function(){
var orig_t = jQuery("#prof_wrapper").offset().top;
var mt = jQuery("#main").offset().top;
var mh = jQuery("#main").outerHeight();
jQuery(document).bind("scroll", function(event){
var ph = jQuery("#prof_wrapper").outerHeight();
var mo = (mt+mh) - jQuery(document).scrollTop();
var t = jQuery(document).scrollTop();
if(mo <= ph){ //Find out if the #main element's offset top plus it's height minus the scroll position is less than the prof_wrapper's height; if it is, leave the prof_wrapper at the bottom of the #main element
jQuery("#prof_wrapper").css({'position':'absolute', 'bottom':'0px', 'top':'auto'});
}else if(t >= orig_t){ //Otherwise, if we have scrolled past the prof_wrapper, make the prof_wrapper follow us down
jQuery("#prof_wrapper").css({'position':'fixed', 'top':'0px', 'bottom':'auto'});
}else{ //We are probably at the top of the page, so reset the original positioning
jQuery("#prof_wrapper").css({'position':'relative', 'top':'0px', 'bottom':'auto'});
}
});
});
You need to add - position:relative to your #main element, then I think this should be pretty close. Take a look at my comment about the padding and margin on the prof_wrapper.
The positioning of absolute, assumes that #main is the first parent element to have a position assigned to it; if so, then it will use this as the positioning for the bottom.
Something like that?