Плавное перетаскивание в холсте D3js

Я пытаюсь нарисовать дорогу с помощью d3js и сделать ее перетаскиваемой. Обратите внимание, что я успешно преобразовал его из svg в canvas. Функция плавного перетаскивания упоминается как How to Pan the Canvas.

Обратите внимание, что когда я попытался реализовать на обычном холсте, он работает, однако в этом модуле он не работает. Я полагаю, логика практически одинакова.

Мой фрагмент кода:

$(document).ready(function(){
let clientWidthz = $(window).width();
let clientHeightz = $(window).height();

var width = clientWidthz;
var height = clientHeightz;
console.log("this is height" + clientHeightz);
console.log("this is width" + clientWidthz);

var color_1 = d3.scaleOrdinal().range(["#605A4C", "#ff9900", "#ff1a1a"]);

console.log(clientWidthz);
console.log(clientHeightz);

//this array will has a total of 99 object
var sampleData = [{radar: 1, timestamp: Date.now(), CongestionLevel: 1400, kilometer: 2.33, bound: "north" }, 
{radar: 2, timestamp: Date.now(), CongestionLevel: 1600, kilometer: 4.66, bound: "north"}, 
{radar: 3, timestamp: Date.now(), CongestionLevel: 1200, kilometer: 6.99, bound: "north"}, 
{radar: 4, timestamp: Date.now(), CongestionLevel: 1700, kilometer: 9.32, bound: "north" }, 
{radar: 5, timestamp: Date.now(), CongestionLevel: 300, kilometer:11.65, bound: "north"}, 
{radar: 6, timestamp: Date.now(), CongestionLevel: 1100, kilometer: 13.98, bound: "north" }, 
{radar: 7, timestamp: Date.now(), CongestionLevel: 300, kilometer: 16.31, bound: "north" }, 
{radar: 8, timestamp: Date.now(), CongestionLevel: 1700, kilometer: 18.64, bound: "north" }];

var mainRoadValueData = [{section:0, rectWidth:1000, rectHeight: 390}, 
                         {section:1, rectWidth:1000, rectHeight: 10}, 
                         {section:2, rectWidth:1000, rectHeight: 1},
                         {section:3, rectWidth:1000, dashlineHeight:270},
                         {section:4, rectWidth:1000, rectHeight: 4},
                         {section:5, rectWidth:1000, dashlineHeight:430},
                         {section:6, rectWidth:1000, rectHeight: 1},
                         {section:7, rectWidth:1000, rectHeight: 10}];
//mouse attributes
var mouse = {
    x: 0,
    y: 0,
    w: 0,
    alt: false,
    shift: false,
    ctrl: false,
    buttonLastRaw: 0, // user modified value
    buttonRaw: 0,
    over: false,
    buttons: [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
};

var displayTransform = {
    x: 0,
    y: 0,
    ox: 0,
    oy: 0,
    scale: clientHeightz*0.003,
    rotate: 0,

    cx: 0, // chase values Hold the actual display
    cy: 0,
    cox: 0,
    coy: 0,
    cscale: 1,
    crotate: 0,

    dx: 0, // deltat values
    dy: 0,
    dox: 0,
    doy: 0,
    dscale: 1,
    drotate: 0,

    drag: 0.2, // drag for movements
    accel: 1, // acceleration
    matrix: [0, 0, 0, 0, 0, 0], // main matrix
    invMatrix: [0, 0, 0, 0, 0, 0], // invers matrix;
    mouseX: 0,
    mouseY: 0,
    ctx: context,
    sweetSpot: function(){
        sweetSpot();
    },
    setTransform: function() {
        var m = this.matrix;
        var i = 0;
        this.ctx.setTransform(m[i++], m[i++], m[i++], m[i++], m[i++], m[i++]);
    },
    setHome: function() {
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);

    },
    update: function() {
        // smooth all movement out. drag and accel control how this moves
        // acceleration 
        this.dx += (this.x - this.cx) * this.accel;
        this.dy += (this.y - this.cy) * this.accel;
        this.dox += (this.ox - this.cox) * this.accel;
        this.doy += (this.oy - this.coy) * this.accel;
        this.dscale += (this.scale - this.cscale) * this.accel;

        // drag
        this.dx *= this.drag;
        this.dy *= this.drag;
        this.dox *= this.drag;
        this.doy *= this.drag;
        this.dscale *= this.drag;

        // set the chase values. Chase chases the requiered values
        this.cx += this.dx;
        this.cy += this.dy;
        this.cox += this.dox;
        this.coy += this.doy;
        this.cscale += this.dscale;

        // create the display matrix
        this.matrix[0] = Math.cos(this.crotate) * this.cscale;
        this.matrix[1] = Math.sin(this.crotate) * this.cscale;
        this.matrix[2] = -this.matrix[1];
        this.matrix[3] = this.matrix[0];

        // set the coords relative to the origin
        this.matrix[4] = -(this.cx * this.matrix[0] + this.cy * this.matrix[2]) + this.cox;
        this.matrix[5] = -(this.cx * this.matrix[1] + this.cy * this.matrix[3]) + this.coy;

        // create invers matrix
        var det = (this.matrix[0] * this.matrix[3] - this.matrix[1] * this.matrix[2]);
        this.invMatrix[0] = this.matrix[3] / det;
        this.invMatrix[1] = -this.matrix[1] / det;
        this.invMatrix[2] = -this.matrix[2] / det;
        this.invMatrix[3] = this.matrix[0] / det;

        // check for mouse. Do controls and get real position of mouse.
        if (mouse !== undefined) { // if there is a mouse get the real canvas coordinates of the mouse
            if (mouse.oldX !== undefined && (mouse.buttonRaw & 1) === 1) { // check if panning (middle button)
                var mdx = mouse.x - mouse.oldX; // get the mouse movement
                var mdy = mouse.y - mouse.oldY;
                // get the movement in real space
                var mrx = (mdx * this.invMatrix[0] + mdy * this.invMatrix[2]);
                var mry = (mdx * this.invMatrix[1] + mdy * this.invMatrix[3]);
                this.x -= mrx;
                this.y -= mry;
            }
            // do the zoom with mouse wheel
            if (mouse.w !== undefined && mouse.w !== 0) {
                this.ox = mouse.x;
                this.oy = mouse.y;
                this.x = this.mouseX;
                this.y = this.mouseY;
                // zooming around the mouse 
                this.cox = mouse.x;
                this.coy = mouse.y;
                this.cx = this.mouseX;
                this.cy = this.mouseY;
                
                if (mouse.w > 0) { // zoom in
                    this.scale *= 1.1;
                    mouse.w -= 20;

                    if (mouse.w < 0) {
                        mouse.w = 0;
                    }
                }
                if (mouse.w < 0) { // zoom out
                        this.scale *= 1/1.1;
                        mouse.w += 20;
                        if(this.scale <=0.5){
                            this.scale = 0.5;
                        }
                    if (mouse.w > 0) {
                        mouse.w = 0;
                    }
                }
            }
            // get the real mouse position 
            var screenX = (mouse.x - this.cox);
            var screenY = (mouse.y - this.coy);
            this.mouseX = this.cx + (screenX * this.invMatrix[0] + screenY * this.invMatrix[2]);
            this.mouseY = this.cy + (screenX * this.invMatrix[1] + screenY * this.invMatrix[3]);
            mouse.rx = this.mouseX; // add the coordinates to the mouse. r is for real
            mouse.ry = this.mouseY;
            // save old mouse position
            mouse.oldX = mouse.x;
            mouse.oldY = mouse.y;
        }

    }
}

function mouseHandler(event){
 mouse.x = event.offsetX;
    mouse.y = event.offsetY;
    if (mouse.x === undefined) {
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    }
    console.log("these are the mouse.x and mouse.y:" + mouse.x + " " + mouse.y);
    mouse.alt = event.altKey;
    mouse.shift = event.shiftKey;
    mouse.ctrl = event.ctrlKey;
    if (event.type === "mousedown") {
        event.preventDefault();
        console.log("this is event.which (mousedown): " + event.which);
        mouse.buttonRaw |= mouse.buttons[event.which - 1]; //bitwise AND operator (convert to binary and do the operation)
        //console.log("this is value value: " + value);
    } else if (event.type === "mouseup") {
        //console.log("this is mouse.buttonRaw before (mouseup): " + mouse.buttonRaw);
        //console.log("this is event.which (mouseup): " + event.which);
        mouse.buttonRaw &= mouse.buttons[event.which + 2]; //bitwise OR operator (convert to binary and do the operation)
    } else if (event.type === "mouseout") {
        mouse.buttonRaw = 0;
        mouse.over = false;
    } else if (event.type === "mouseover") {
        mouse.over = true;
    } else if (event.type === "mousewheel") {
        event.preventDefault();
        mouse.w = event.wheelDelta;
    } else if (event.type === "DOMMouseScroll") { // FF you pedantic doffus
        mouse.w = -event.detail;
    } 
}

function setupMouse(e) {
    //syntax addEventListener (event, function, useCapture)
    e.addEventListener('mousemove', mouseHandler);
    e.addEventListener('mousedown', mouseHandler);
    e.addEventListener('mouseup', mouseHandler);
    e.addEventListener('mouseout', mouseHandler);
    e.addEventListener('mouseover', mouseHandler);
    e.addEventListener('mousewheel', mouseHandler);
    e.addEventListener('DOMMouseScroll', mouseHandler); // fire fox

    e.addEventListener("contextmenu", function(e) {
        e.preventDefault();
    }, false);
}

//2160 x 3840 resolution
//create a base for you to start
var canvas = d3.select('.roadModelCanvasDiv').append("canvas")
               .attr("id", "canvas")
               .style("position", "absolute")
               .style("top", "0")
               .style("left", "0")
               .attr("width", width)//optimization
               .attr("height", height);//optimization


//initialize the tools to start something on the context
var context = canvas.node().getContext("2d");

//over start binding the elements
var detachedContainer = document.createElement("custom");
var dataContainer = d3.select(detachedContainer);

//Draw the Road
var mainRoad = dataContainer.selectAll("custom.rect")
                             .data(mainRoadValueData)
                             .enter().append("custom")
                             .attr("x", function(d,i){
                                switch(d.section){
                                    case 0:
                                    return 100;
                                    break;
                                    case 1:
                                    return 100;
                                    break;
                                    case 2:
                                    return 100;
                                    break;
                                    case 3:
                                    break;
                                    case 4:
                                    return 100;
                                    break;
                                    case 5:
                                    break;
                                    case 6:
                                    return 100;
                                    break;
                                    case 7:
                                    return 100;
                                    break;
                                }
                             })
                             .attr("y", function(d,i){
                                switch(d.section){
                                    case 0:
                                    return 150;
                                    break;
                                    case 1:
                                    return 140;
                                    break;
                                    case 2:
                                    return 200;
                                    break;
                                    case 3:
                                    break;
                                    case 4:
                                    return 350;
                                    break;
                                    case 5:
                                    break;
                                    case 6:
                                    return 500;
                                    break;
                                    case 7:
                                    return 540;
                                    break;
                                    default:
                                    break;
                                }
                             })
                             .attr("class", "rect")
                             .attr("width", function(d){
                                return +d.rectWidth;
                             })
                             .attr("height", function(d){
                                return +d.rectHeight;
                             })
                             .attr("dottedOriginX", function(d, i){
                                if(d.section == 3 || d.section == 5){
                                    return 100;
                                }
                             })
                             .attr("dottedOriginY", function(d, i){
                                if(d.section == 3 || d.section == 5){
                                    return +d.dashlineHeight;
                                }
                             })
                             .attr("dottedEndX", function(d,i){
                                if(d.section == 3 || d.section == 5){
                                    return 1100;
                                }
                             })
                             .attr("dottedEndY", function(d,i){
                                if(d.section == 3 || d.section == 5){
                                    return +d.dashlineHeight;
                                }
                             })
                             .attr("strokeStyle", "grey")
                             .attr("fillStyle", function(d,i){
                                switch(d.section){
                                    case 0:
                                    return "#1C1C1C";
                                    break;
                                    case 1:
                                    return "grey";
                                    break;
                                    case 2:
                                    return "grey";
                                    break;
                                    case 3:
                                    return "grey";
                                    break;
                                    case 4:
                                    return "grey";
                                    break;
                                    case 5:
                                    return "grey";
                                    break;
                                    case 6:
                                    return "grey";
                                    break;
                                    case 7:
                                    return "grey";
                                    break;
                                    default:
                                    break;
                                }
                             });

drawRoadModel();
var canvasWithMouseListener = document.getElementById("canvas");
setupMouse(canvasWithMouseListener);


function drawRoadModel(){
 console.log("drawRoadModel");
    var elements = dataContainer.selectAll("custom.rect");
    context.save();
    elements.each(function(d, i){
        var node = d3.select(this);

        context.beginPath();
        context.fillStyle = node.attr("fillStyle");
        context.rect(node.attr("x"), node.attr("y"), node.attr("width"), node.attr("height"));
        context.fill();
        context.closePath();

        context.beginPath();
        context.strokeStyle = node.attr("strokeStyle");
        context.setLineDash([5,10]);
        context.moveTo(node.attr("dottedOriginX"), node.attr("dottedOriginY"));
        context.lineTo(node.attr("dottedEndX"), node.attr("dottedEndY"));
        context.stroke();
    });
    context.restore();
}

});
html{
    overflow: hidden;
}

.overflowXY{
    position:absolute;
    margin-left:-14px;
    z-index:2;
}

.overflowXY > canvas {
}
.widget {
    position: absolute;
    z-index:3;
}

.chart-container {
    padding: 5px;
}

.shadow {
    -webkit-filter: drop-shadow( 0px 3px 3px rgba(0, 0, 0, .5));
    filter: drop-shadow( 0px 3px 3px rgba(0, 0, 0, .5));
}

body {
  margin: 0;
  padding: 0;
  height: 100vh;
    background-color: #a0a0a0;
    background-image: linear-gradient(to left top, #434343, #606060, #808080, #a0a0a0, #c2c2c2);
    z-index:-1;
}
.canvasRoadModel{
  background-color: transparent;
  background-image: linear-gradient(0deg, transparent 24%, rgba(255, 255, 255, .05) 25%, rgba(255, 255, 255, .05) 26%, transparent 27%, transparent 74%, rgba(255, 255, 255, .05) 75%, rgba(255, 255, 255, .05) 76%, transparent 77%, transparent), linear-gradient(90deg, transparent 24%, rgba(255, 255, 255, .05) 25%, rgba(255, 255, 255, .05) 26%, transparent 27%, transparent 74%, rgba(255, 255, 255, .05) 75%, rgba(255, 255, 255, .05) 76%, transparent 77%, transparent);
  height:100%;
  background-size:50px 50px;
  z-index:1;
}
<html>
<script
  src="https://code.jquery.com/jquery-1.10.2.min.js"
  integrity="sha256-C6CB9UYIS9UJeqinPHWTHVqh/E1uhG5Twh+Y5qFQmYg="
  crossorigin="anonymous"></script></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">

<body id="bodyBackground">
    <div class="container-fluid">
        <div class="row">
            <div class="col-lg-12 canvasRoadModel" style="height:100vh;background-color:#303030">
                <div id="chart" class="Chart"></div>
                <div id="roadmodelcanvas" class="roadModelCanvasDiv"></div>
            </div>
        </div>
    </div>
</body>
</html>

0 ответов

Другие вопросы по тегам