Controls:

Click + Drag: Draw new rectangle. Shift + Click + Drag: Pan in workspace. scroll: Zoom In/Out

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <title>Adding Rectangles</title>
    <style>
    rect.rect-main {
        stroke: #d32f2f;
        stroke-width: 2;
        fill-opacity: 0;
        stroke-opacity: 0.5;
    }

    div.sample-div {
        position: absolute;
        top: 25%;
        left: 25%;
    }
    </style>
</head>

<body>
    <div class="sample-div">
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.js"></script>
    <script type="text/javascript">
    function SVGCanvas(options) {
        // An SVG-based drawing
        var self = this;

        // Define the global SVG options
        this.options = options || {};
        this.options.h = options.h || 250; // SVG Height and Width
        this.options.w = options.w || 250;
        this.options.addTo = options.addTo || 'body'; // Where to add the SVG (a css selector)
        this.options.addBorderRect = options.addBorderRect || true; // Whether to add a border around the SVG.

        // Make the SVG 
        this.svg = d3.select(this.options.addTo)
            .append('svg')
            .attr('height', this.options.h)
            .attr('width', this.options.w)
            .attr('class', 'display-svg');

        // Add border if requested
        if (this.options.addBorderRect) {
            this.svg.append('rect')
                .attr('height', this.options.h)
                .attr('width', this.options.w)
                .attr('stroke', 'black')
                .attr('stroke-width', 4)
                .attr('opacity', 0.25)
                .attr('fill-opacity', 0.0)
                .attr('class', 'border-rect');
        }

        // Add zoom and pan group
        this.zoomG = this.svg
            .append('g')
            .attr('class', 'zoom-group');

        // Rectangles
        this.Rect = {
            'r': null,
            'x0': null,
            'y0': null,
        }; // Current Selection
        this.Rects = []; // Collection

        // Transformation state
        this.transform = d3.zoomTransform(this.zoomG.node());

        // Load methods for behaviors
        this.makeAddRect(); // Add Rectangle Methods
        this.makeZoomPan(); // SVG Zooming and Panning Methods
        this.makeDragBehavior();

        // Dragging Behavior - account for both addRect and pan.
        this.svg.call(
            d3.drag()
            .on('start', self.dragBehavior.start())
            .on('drag', self.dragBehavior.drag())
            .on('end', self.dragBehavior.end())
        );

        // Zooming behavior
        this.svg.call(
                d3.zoom()
                .scaleExtent([1, 10])
                .on('zoom', this.zoomPan.zoom)
            )
            .on('.zoom', this.zoomPan.zoom)
            .on('mousedown.zoom', null)
            .on('mousemove.zoom', null)
            .on('mouseup.zoom', null);
    }


    SVGCanvas.prototype.makeZoomPan = function() {
        // Defines zooming and panning behavior from zoom listener

        var self = this;

        zoom = function() {
            self.transform = d3.event.transform;

            self.zoomG.attr('transform', self.transform);

            // Go back to initial position if zoomed out.
            if (d3.event.transform.k === 1) {
                self.zoomG
                    .transition(d3.transition()
                        .duration(100)
                        .ease(d3.easeLinear))
                    .attr('transform', 'translate(0,0) scale(1, 1)');

                self.transform = d3.zoomTransform(self.zoomG.node());
            }
        }

        var pan = function() {
            var m = d3.event;
            self.transform.x += m.dx;
            self.transform.y += m.dy;

            // Update Attribute
            d3.select('g.zoom-group').attr('transform', self.transform);
        }

        self.zoomPan = {
            zoom: zoom,
            pan: pan
        };

    }

    SVGCanvas.prototype.mouseOffset = function() {
        var m = d3.event;
        m.x = (-this.transform.x + m.x) / this.transform.k;
        m.y = (-this.transform.y + m.y) / this.transform.k;
        return m;
    }

    SVGCanvas.prototype.makeAddRect = function() {
        var self = this;

        start = function() {
            //Add a rectangle
            // 1. Get mouse location in SVG
            var m = self.mouseOffset(); //d3.event;
            self.Rect.x0 = m.x;
            self.Rect.y0 = m.y;
            // 2. Make a rectangle
            self.Rect.r = self.zoomG
                .append('g')
                .append('rect') // An SVG `rect` element
                .attr('x', self.Rect.x0) // Position at mouse location
                .attr('y', self.Rect.y0)
                .attr('width', 1) // Make it tiny
                .attr('height', 1)
                .attr('class', 'rect-main') // Assign a class for formatting purposes
            ;
        }

        drag = function() {
            // What to do when mouse is dragged
            // 1. Get the new mouse position
            var m = self.mouseOffset(); //d3.event;
            // 2. Update the attributes of the rectangle
            self.Rect.r.attr('x', Math.min(self.Rect.x0, m.x))
                .attr('y', Math.min(self.Rect.y0, m.y))
                .attr('width', Math.abs(self.Rect.x0 - m.x))
                .attr('height', Math.abs(self.Rect.y0 - m.y));
        }

        end = function() {
            // What to do on mouseup
            self.Rects.push(self.Rect);
        }

        self.addRect = {
            start: start,
            drag: drag,
            end: end
        };

    }

    SVGCanvas.prototype.makeDragBehavior = function() {
        var self = this;

        var start = function() {
            return function() {
                if (!d3.event.sourceEvent.shiftKey) {
                    self.addRect.start();
                }
                if (d3.event.sourceEvent.shiftKey) {
                    null;
                }
            }
        }


        var drag = function() {
            return function() {
                if (!(self.Rect.r === null) && !(d3.event.sourceEvent.shiftKey)) {
                    self.addRect.drag();
                }
                if (d3.event.sourceEvent.shiftKey) {
                    self.zoomPan.pan();
                }
            }
        }

        var end = function() {
            return function() {
                if (!(self.Rect.r === null) & !(d3.event.sourceEvent.shiftKey)) {
                    self.addRect.end();
                }
                if (d3.event.sourceEvent.shiftKey) {
                    null;
                }
            }
        }

        self.dragBehavior = {
            start: start,
            drag: drag,
            end: end
        };
    }

    /**********
        SETUP
    **********/
    options = {
        h: 300,
        w: 500,
        addTo: '.sample-div',
        addBorderRect: true,
    }

    var c = new SVGCanvas(options);
    </script>
</body>

</html>