Pan + Zoom to center of SVG

2,382 views
Skip to first unread message

Tom Herold

unread,
Jun 13, 2013, 6:09:42 AM6/13/13
to d3...@googlegroups.com
Hi everyone,

I am struggling to write my own zoom + pan function, that zooms into the center of my SVG graphic when I use my mousewheel. To be more specific I don't translate + zoom the whole SVG element but rather just one group (that accounts for almost the whole image anyway.) So far I tried using SVG Matrices but end up jumping around in my SVG instead of smoothling zooming to the center of the screen. Here is what I have so far:

panAfterZooming : (zoomLevel, position) =>

    $svg = @$el.find("#process-graph")
    svgRoot = $svg[0]

    midX = $svg.width() / 2
    midY = $svg.height() / 2

    groupElement = @graph.graphContainer[0][0]
    transformationMatrix = groupElement.getCTM()

    p = svgRoot.createSVGPoint()
    p.x = midX
    p.y = midY

    p = p.matrixTransform(transformationMatrix.inverse())

    panNZoomMatrix = svgRoot.createSVGMatrix().translate(p.x, p.y).scale(zoomLevel).translate(-p.x, -p.y)
    transformationMatrix = transformationMatrix.multiply(panNZoomMatrix)

    newTransformation = @decomposeMatrix(transformationMatrix)

    @panTo(newTransformation.translateX, newTransformation.translateY, newTransformation.scaleX)


panTo : (x, y, zoom) =>

    transformation = d3.transform(@graph.graphContainer.attr("transform"))
    transformation.translate = [x, y]
    transformation.scale = zoom || app.view.process.zoom

    @graph.graphContainer.attr("transform", transformation.toString())

    app.trigger "behavior:panning"


 decomposeMatrix : (matrix)  ->

      return {
        translateX: matrix.e
        translateY: matrix.f
        scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b)
        scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d)
      }

Can anyone help? What am I doing wrong?

Johnson Chetty

unread,
Jun 16, 2013, 11:42:19 AM6/16/13
to d3...@googlegroups.com
Hey,

For zooming and panning:, this might be of help.
Check out: http://thisismattmiller.com/blog/add-zoom-slider-to-d3-js/

For zooming to the center of the graph.
I've used a function perform a transition:

transformVis(visWidth, visHeight, scale) {
vis.attr("transform", "translate(" + (visWidth - (visWidth *
scale))/2.0 + ',' +
(visHeight - (visHeight * scale))/2.0 + ")scale(" + scale + ")");
}


For easing it out, you can try: (Any better suggestions welcome!)

transformVisWithEasing(visWidth, visHeight, scale, prevScale) {
diff = abs(prevScale-Scale)
diffunits = 0.03 //tweak for easing
for (i=prevScale, i<=Scale, i+=diffunits ) {
vis.attr("transform", "translate(" + (visWidth - (visWidth *
scale))/2.0 + ',' + (visHeight - (visHeight * scale))/2.0 + ")scale("
+ i + ")");
}
}

Lemme know how it goes..

Cheers,
> --
> You received this message because you are subscribed to the Google Groups
> "d3-js" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to d3-js+un...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
>


--
Regards,
Johnson Chetty

Tom Herold

unread,
Jun 16, 2013, 11:48:44 AM6/16/13
to d3...@googlegroups.com
Thanks for the reply. I managed to figure it out after a few painful days of matrix math... I took this as inspiration https://code.google.com/p/svgpan/ and ended up with the following code:

panZoom : (zoomLevel, position) =>

    $svg = $(@svgRoot)
    groupElement = @graph.graphContainer[0][0]

    if position

      mouse =
        x: position[0] - $svg.offset().left
        y: position[1] - $svg.offset().top

    else

      mouse =
        x: $svg.width() / 2
        y: $svg.height() / 2

    scale = zoomLevel / @oldZoomLevel

    p = @transformPointToLocalCoordinates(mouse)

    transformationMatrix = @svgRoot.createSVGMatrix()
      .translate(p.x, p.y)
      .scale(scale)
      .translate(-p.x, -p.y)

    @setCTM(groupElement.getCTM().multiply(transformationMatrix))

    @oldZoomLevel = zoomLevel

transformPointToLocalCoordinates : (point) ->

    p = @svgRoot.createSVGPoint()
    p.x = point.x
    p.y = point.y

    p.matrixTransform(@transformationGroup.getCTM().inverse())

setCTM : (matrix) ->

    matrixString = "#{matrix.a} #{matrix.b} #{matrix.c} #{matrix.d} #{matrix.e} #{matrix.f}"
    @graph.graphContainer.attr("transform", "matrix(#{matrixString})")

Thanks for taking interest, though. 

Johnson Chetty

unread,
Jun 16, 2013, 2:11:14 PM6/16/13
to d3...@googlegroups.com
Oh that's nice! I think this might pave way for a nice... slide +
pan+zoom view for the d3 geo projections. And also a rotate function
.... I think given the plethora of projections we have... this would
fit right there..

On 16/06/2013, Tom Herold <hero...@gmail.com> wrote:
> Thanks for the reply. I managed to figure it out after a few painful days
> of matrix math... I took this as inspiration
> https://code.google.com/p/svgpan/ <https://code.google.com/p/svgpan/> and
>> > email to d3-js+un...@googlegroups.com <javascript:>.
Reply all
Reply to author
Forward
0 new messages