I'm working on a project with HTML5 Canvas and KineticJS. A half-transparent overlay (a KineticJS group or layer) is placed all over the scene. The problem is: Mouse events bound to KineticJS objects under this overlay are not processed.
How can I make this overlay (or any other object) "transparent" to mouse events?
NOTE: The question is only about the Canvas, there are no other HTML elements interfering to it. (To make clear what was asked below.)
Assuming you mean HTML elements are placed over your canvas, try looking at pointer events: pointer events at MDN
e.g.
#foo {
pointer-events:none;
}
Setting opacity in layer level also sets opacity into object level.
layer.setOpacity(0.1);
"Mouse events bound to KineticJS objects under this overlay are not processed."
Hmm, mouse events bound to object are processed although your overlay(layer) has opacity of 0. this seems working fine, I don't know what you are into.
"How can I make this overlay (or any other object) "transparent" to mouse events?"
For me, mouseover/mouseout both work fine on layer level to make it half transparent.
layer.on('mouseover', function() {
this.setOpacity(0.5);
this.draw();
});
layer.on('mouseout', function() {
this.setOpacity(1);
this.draw();
});
Are you missing layer.draw()?
Yes you can click-through top nodes to the bottom nodes similar to pointer-events:none
You just tell your top object not to listen to events…like this:
myTopObject.setListening(false);
Now all mouse events will bubble down to the bottom object.
See this SO answer for code and Fiddle: pointer-events in Kineticjs
Related
I'm working with D3+leaflet and have a question - my SVG elements seem to disappear outside the scope of the map frame when I zoom in. Meaning, that when I zoom in the map, and then drag to a different frame, the SVG elements aren't loaded. So i'd like to execute a map.on( ) function for drag events but I'm having trouble.
So for when you zoom in, replotting the SVGs work fine with the map.on("viewreset") - but map.on("drag") or map.on("dragend") seems to have no effect.
var cheat_function = function() { reset(data); drawHour( orderedColumns[currentFrame] )};
map.on("viewreset", cheat_function);
map.on("dragend", cheat_function)
The contents inside cheat_function just do stuff like recalculating the coordinates and redrawing the SVGs.
Any ideas or hacks? thanks guys.
Try with the moveend event:
Fired when the view of the map stops changing (e.g. user stopped
dragging the map).
How do I bind a mouseover or any event for that matter to a drawn object on the canvas? For instance, I tried this:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
//STEP ONE
var stepOneRec = ctx.rect(20, 60, 266, 50);
ctx.stroke();
stepOneRec.addEventListener("mouseover", function() { alert('it works!'); });
On one site I looked at it showed a method using Kinetic.js. If that's the only way, I'll use it, I just assume that there's a way to bind events to drawn elements without extra plug-ins. Sorry Canvas noob. I made a fiddle with my code here: http://jsfiddle.net/jyBSZ/2/
(I started this as a posted comment, then realized it's an actual answer.)
Unfortunately, in javascript on it's own, you can't. There are no canvas objects, just the canvas as a whole, and whatever you drew on to its context. Plugins like kinetic can make objects for you, but the whole point of canvas is that the browser can think of it as a single static image.
If you want to, you can bind mousemove events to the canvas, track its position and the position where you drew stuff, and imply on your own that it's over "that object" (effectively what the plugins do), but it's all mousemove events on a single canvas rather than mouseover events on components of it. (You could even make your event binding simulate a mouseover event for the "objects", but underneath, it's still based on checking movement and checking your own object setup.)
The objects drawn within a canvas element are not HTML elements, just pixels, and therefore will not throw DOM events the way that HTML elements would.
You would need to track the locations of your objects yourself and handle the canvas' onmousemove event in order to determine when the mouse is over one of your drawn objects.
you can use jCanvas, take a look here
i made a jsfiddle example for your problem.
just modify next callbacks for desired result
function mouseOut(layer){
$("#mouse-over-text").html('none options selected');
}
function mouseIn(layer){
$("#mouse-over-text").html(getTextForId(layer.name));
}
I have a circle on layer1 and then a square on layer2. The circle diameter is the square's width, their centers are the same, and the circle is on top of the square. How do I set things up such that if I trigger any event on layer1 the same even will be executed at the same location in layer2. So in this case if I click on the circle, the square's click event will also be triggered. Of course, this doesn't mean I simply want to hook up the circle's events to the square's, but rather I basically want the events to be triggered on both layers.
So for example if
circle.on( "click", function() {
alert("foo");
});
square.on( "click", function() {
alert("bar");
});
then clicking on the center of circle should trigger the square click event.
And NOT by
circle.on( "click", function() {
alert("foo");
square.simulate("click"); // don't want this
});
How about adding the circle and square to a group, and then attatch the click-event to the group instead?
Kinetic has two things that can help you.
getIntersections() -- returns all shapes under the mouse click point
fire() -- fires an event
The Kinetic docs mention the getIntersections() performs slowly, so they prefer getIntersection(), but I have never used either, so I can't say how much.
http://kineticjs.com/docs/Kinetic.Stage.html#getIntersection
Hope this helps.
I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.
FWIW - I'm using Backbone.js views to modularize the shape logic.
Approach 1
My initial thought was to use a combination of foreignObject for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject, and therefore this approach had to be abandoned.
Approach 2
With the beside the canvas object, add divs that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div. There are a couple of problems with this approach:
Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?
Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support pointer-events, this is easily done:
div.shape-text-overlay {
position: absolute;
background: none;
pointer-events: none;
}
However, other browsers (like IE8 and below) need forwarding of the events:
var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
this.$elText.on(forwardedEvents, function(e) {
var original = e.originalEvent;
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent(e.type, true, true);
}
else {
event = document.createEventObject();
event.eventType = e.type;
}
// FYI - This is the most simplistic approach to event forwarding.
// My real implementation is much larger and uses MouseEvents and UIEvents separately.
event.eventName = e.type;
_.extend(event, original);
if (document.createEvent) {
that.el.node.dispatchEvent(event);
}
else {
that.el.node.fireEvent('on' + event.eventType, event);
}
});
Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:
This approach is what I'm currently using but it just feels like a huge hack.
Approach 3
Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.
Question
Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?
I built this project (no longer live) using Raphael. What I did is actually abandoned the idea of using HTML inside of SVG because it was just too messy. Instead, I absolutely positioned an HTML layer on top of the SVG layer and moved them around together. When I wanted the HTML to show, I merely faded it in and faded the corresponding SVG object out. If timed and lined up correctly, it's not really noticeable to the untrained eye.
While this may not necessarily be what you're looking for, maybe it will get your mind thinking of new ways to approach your problem! Feel free to look at the JS on that page, as it is unminified ;)
PS, the project is a lead-gathering application. If you just want to see how it works, select "Friend" in the first dropdown and you don't have to provide any info.
Unless another answer can be provided and trump my solution, I have continued with the extra div layer. Forwarding events was the most difficult part (if anyone requires the forwarding events code, I can post it here). Again, the largest downside to this solution is that overlapping shapes will cause their text to overlap above the actual drawn shape.
I'm working on a script to do several things. In a nutshell, here's what it needs to do:
Read the coordinates from a page and be able to pop up a box within a specific region.
The pop up box needs to be able to follow the mouse around.
I need to be able to modify the box to look however I want (I was thinking a div container that is set to display:hidden, and then the JS sets the display to block when your mouse is in the specified region).
I need to be able to modify it easily (aka, add and subtract objects and coordinate sets)
I was originally using HTML maps (), and that worked great, until I resized my browser, and the div that I had following the mouse no longer lined up correctly. Something about the offset not working correctly, and I couldn't get it to work correctly, so I switched to an HTML canvas.
And now I've got the coordinates in the canvas correctly, I just can't figure out how to get something to pop up when the mouse is inside of a certain section. Here's my current code:
function drawLines(numbers, color){
//poly [x,y, x,y, x,y.....];
var poly=numbers;
context.fillStyle = color;
context.beginPath();
context.moveTo(poly[0], poly[1]);
for( item=2 ; item < poly.length-1 ; item+=2 )
{context.lineTo( poly[item] , poly[item+1] )};
context.closePath();
context.fill();
}
I've got each region inside of an array, which I then pass to the function one by one. The color was a test, and I can easily get each region to show up as a specified color, but that doesn't solve my problem. Any ideas? Thanks!
Seems strange to jump to canvas over a style issue, but ignoring that...
You could bind mousemove events on the canvas element and then do hit tests on your region to see if the mouse is inside the region.
Doing the hit test efficiently might be tricky depending on the number of regions your testing, but it's definitely doable.
The canvas is just like any other block level element, so the same events apply and are bound in the same way.
Here's one example of mouse events interacting with canvas. In this example, the events are bound to the document, but similar ideas apply.
http://dev.opera.com/articles/view/blob-sallad-canvas-tag-and-javascrip/