Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D3 svg-overlay with draggable elements #1425

Closed
lesolorzanov opened this issue Mar 21, 2018 · 12 comments
Closed

D3 svg-overlay with draggable elements #1425

lesolorzanov opened this issue Mar 21, 2018 · 12 comments
Labels

Comments

@lesolorzanov
Copy link

I am using SVG overlay and calling D3 to draw elements on top of the the image when the user clicks on top of it. I understand from svg-overlay that I can use onClick(node, handler) to accept click events on a sub-node and it raises an OpenSeadragon.MouseTracker click event.

The thing is that I need to tell D3 and OSD that this a draggable element.

In this super cool OSD+D3 example there is a working example on D3 elements that it is possible to click and get an OSD event that provides a reference to the D3 element which can be called using D3 calls to do a click dependent action. But since dragging is another type of event I am not really sure what to do.

D3 dragging is done like so and I am trying to use the previous example and doing some kind of monster like this:

d3.selectAll(".aClass").each(function() { 
    return svg-overlay.onClick(this,
        d3.select(this.element)
            .call(d3.drag().on("start",dragged))
    ); 
} );

So I wonder if anyone has tried such a thing, dragging the elements created on top of the SVG-overlay. Any ideas or suggestions are welcome.

@iangilman
Copy link
Member

I've not tried to do such a thing, but I imagine it'll work better if you allow OSD to do the event tracking and not try to use D3's event tracking. Looking at onClick it looks like it's just a thin wrapper around MouseTracker, so you can probably just go around it like so:

function addTracking(node) {
  new $.MouseTracker({
    element: node,
    dragHandler: function(event) {
      // Move node by event.delta 
    }
  }).setTracking(true);
}

d3.selectAll(".aClass").each(function() {
  addTracking(this); 
});

@iangilman
Copy link
Member

Btw, if you get this working I'd love to see it! Sounds like a neat project :)

@lesolorzanov
Copy link
Author

Thank you! I will show you for sure! I thought that worst case scenario I would click on the element and kind of attach it to the cursor to move along and then click again to let go. I have an app doing this but with fabric.js whose objects all have inherent dragging capabilities. I will try and let you know. Thank you!

@lesolorzanov
Copy link
Author

Ok. I can confirm it works. Now I am having trouble finding the correct delta. The objects I place on the image look like this in html

I am placing them using normalized coordinates that I get when the registered click handler does this:

var click_handler= function(event) {    
    if(event.quick){
        var normalizedPoint=osdviewer.viewport.pointFromPixel( event.position );
        overlayUtils.drawTMCP("fixed",{x:normalizedPoint.x,y:normalizedPoint.y});
    }
};

So now from the event captured using your code I don't which coordinates in which space to use to modify the nodes.
The nodes are structured like this:

<g id="..." transform="...">
<path d="..." transform="..." stroke="..." style="..."></path>
<text text-anchor="..." transform="..." style="...">Hello</text>
</g>

Here we go out of the OSD domain to svg and D3.

SVG and elements can be transformed using the transform attribute. and the elements with x and y attributes. I capture the node with the OSD.MouseTracker dragHandler. I guess that I could append a "translate(dx,dy)" to the transform attribute. But I need dx and dy in OSD viewer coordinates and I am a bit lost on how to get them since so for I have found that the reported event.position from the drag is on the space of the node.

But in summary, it works, I can register a dragHandler using OSD.MouseTracker. So when it works I will upload a gif :)

Thank you!

@iangilman
Copy link
Member

The event.delta should be in web coordinates, and the svg should be in viewport coordinates (the svg overlay plugin handles that for you). To convert you just need to do:

var viewportDelta = viewer.viewport.deltaPointsFromPixels(event.delta);

@lesolorzanov
Copy link
Author

Ok so finally here it is
osd

In general the code looks like this:

//Draw marker when clicking
var click_handler = function(event) {
    if(event.quick){
        drawMarker(HTMLid);
    }
};
var mouse_tracker = new OpenSeadragon.MouseTracker({
    element: OSDviewer.canvas,
    clickHandler: click_handler
}).setTracking(true);

//Add drag handling to an element
var addTrackingToDOMElement = function(node) {
    new OpenSeadragon.MouseTracker({
        element: node,
        dragHandler: function(event) {
            var viewportDelta = OSDviewer.viewport.deltaPointsFromPixels(event.delta);
            var d3node=d3.select(node);
            //here I have to take the string that defines the SVG transformation, 
            //parse it, get the "translate" component, cast it to number, add the 
            //event.delta and parse the transformation to string again to add it 
            //to the D3selection attr("transform") so lets say: 
            d3node.attr("transform",getD3nodeOriginalPositionPlusEventDelta());
        }
    }).setTracking(true);
}
//Create a marker to draw and assign it the drag handler
var drawMarker = function(HTMLid){
    var elem=d3.select( OSDviewer.svgOverlay().node());
    var normalizedPoint=fixed_viewer.viewport.pointFromPixel( event.position );
    var elemEnter = elem
    .append("g")
    .attr("id",HTMLid)
    .attr( 'transform','translate('+ normalizedPoint.x +','+ normalizedPoint.y +')');
    
    d3.select(HTMLid).each(function() {
        addTrackingToDOMElement(this);
    });    
}

I would like to create a bl.ocks but I don't know how to, maybe someone wants to do something like
this.

@iangilman
Copy link
Member

Looks beautiful!

I've never made a bl.ocks myself, but it looks like it's pretty straight forward:

https://bl.ocks.org/-/about

Pretty clever, actually. Let me know if you make it and we can add it as part of openseadragon/site-build#186.

@lesolorzanov
Copy link
Author

Here you go:

Bl.ocks to create D3 elements on top of a OSD viewer and drag them

Just click around the image to create pretty stars using D3 and then you can drag them using OSD handlers

With love 😊

ps: This is where I work ☺️

@iangilman
Copy link
Member

Cool!

I'm seeing a couple of issues:

The button images don't seem to show up. Maybe use "https://cdnjs.cloudflare.com/ajax/libs/openseadragon/2.3.1/images/" for your prefixUrl?

I can't pan the image... Is that intentional? I think it must be possible to be able to support both dragging stars and panning (when you don't click on a star).

@lesolorzanov
Copy link
Author

You can only pan the image if it is not in the minimum zoom, when all the image is inside the viewer then it doesn't pan and that is set in the OSD initialization as you can see in the code here. I removed the constrainDuringPan: true. I had it because I need it for my work :)

I just changed the button images prefixUrl and it works :)

@iangilman
Copy link
Member

Oh, of course! I'm just not used to having the constrain turned on like that, I suppose.

Looking good! :)

@krushnavadan
Copy link

Hi I am new here i have project if anyone wants to work, email me [email protected]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants