我有一些带拖放支持的d3代码。我有一个导入的SVG,里面有一个有限的多边形区域。我需要将RECS添加到此区域并限制此区域边界内的拖放支持。有谁知道如何做到这一点?主要问题是我无法计算这个区域的函数,因为它是可变的。
非常感谢你的帮助!
注意:我创建了一个jsfiddle链接。
SVG文件XML(图像数据已被删除)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="3386"
height="1498"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="SAL_default_at.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="1220.1429"
inkscape:cy="749"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="1138"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
borderlayer="false"
showborder="false"
inkscape:showpageshadow="false" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(1343,216.63782)">
<image
y="-216.63782"
x="-1343"
id="image2993"
xlink:href="data has been elminated"
height="1498"
width="3386" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="capaAreaTrabajo"
transform="translate(1343,216.63782)">
<path
style="opacity:0.05;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -1285.7143,58.076468 677.14287,2.857143 -5.71428,551.428569 539.999996,-2.85714 -2.857143,368.57143 -1128.571443,-2.85714 z"
id="d3AreaTrabajo"
inkscape:connector-curvature="0"
inkscape:label="d3AreaTrabajo" />
</g>
</svg>
D3通话功能
function d3plantExplorerGenerarMapa(movilabDataset, w, h, anchoSalaCm, altoSalaCm, dwh, urlImagenFondoSvg, reescalado) {
var urlFondo2 = urlImagenFondoSvg.replace(".png", "_at.svg");
d3.xml(urlFondo2, "image/svg+xml", function(xml) {
var importedNode = document.importNode(xml.documentElement, true);
d3.select("#d3PlantExplorer").node().appendChild(importedNode);
var svgTmp = d3.select("#svg2");
d3plantExplorerGenerarMapaProcess(movilabDataset, w, h, anchoSalaCm, altoSalaCm, dwh, urlImagenFondoSvg, reescalado, svgTmp);
});
}
function d3plantExplorerGenerarMapaProcess(movilabDataset, w, h, anchoSalaCm, altoSalaCm, dwh, urlImagenFondoSvg, reescalado, rootSvgImported) {
console.log("d3plantExplorerGenerarMapa --> init");
//
// Imagen de fondo para usar. Representa la planta.
//
var urlFondo = urlImagenFondoSvg;
//
// Datos para pintar en formato JSON
//
// NOTA: Si los datos NO viajan no sobreescribimos.
if(movilabDataset != null) {
svgJsonData = JSON.parse(movilabDataset);
jsonMovilabDataset = svgJsonData;
}
//
// Definimos la escala
//
// - Dominio -> Datos REALES que viajarán en el dataset.
// en nuestro caso posicion X,Y en CM en una sala. Por tanto nuestro dominio es {0, ancho/alto sala}
//
// - Rango -> Representa nuestro máximo valor en la VISUAL, es decir, en el explorador.
// En nuestro caso representa el alto y el ancho DE LA IMAGEN EN NAVEGADOR, es decir {0, ancho/alto imagens svg}
//
var widthCm = anchoSalaCm;
var higthtCm = altoSalaCm;
widthScaleCm2Px = d3.scale.linear() //this.
.domain([0, widthCm])
.range([0, w]);
heightScaleCm2Px = d3.scale.linear() //this.
.domain([0, higthtCm])
.range([0, h]);
/*
this.widthScalePx2Cm = d3.scale.linear()
.domain([0, w])
.range([0, widthCm]);
this.heightScalePx2Cm = d3.scale.linear()
.domain([0, h])
.range([0, higthtCm]);
*/
//
// Ejes
//
/*
xAxis = d3.svg.axis()
.scale(widthScaleCm2Px)
.orient("bottom")
.tickSize(-h);
yAxis = d3.svg.axis()
.scale(heightScaleCm2Px)
.orient("left")
.ticks(5)
.tickSize(-w);
*/
//
// Variable para controlar el zoom.
//
var zoomListener = d3.behavior.zoom()
.on("zoom", d3zoomHandler);
var zoomListener2 = d3.behavior.zoom()
.on("zoom", d3zoomHandler2);
//
// Variable para controlar los eventos Drag&Drop usado el motor D3
// Nota: El evento dragend se dispara con el evento onclick.
//
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", d3dragstarted)
.on("drag", d3dragged)
.on("dragend", d3dragended);
//
// Comprobamos que no exista la imagen SVG ya creada en el cliente
// Si NO existe la creamos y si existe borramos todos los elementos
// creados y repintamos.
//
// NOTA: La consistencia de los datos a pintar la mantiene el codigo java del backing bean
var svgBackgrounImageId = "d3PlantExplorerBackgroundImage";
var svgBackgrounImageQueryId = "#" + svgBackgrounImageId;
var svgName = "d3PlantExplorerSvg";
var svgQueryName = "#" + svgName;
console.log("d3plantExplorerGenerarMapa [01] --> OK");
if(rootSvgImported != null) {
rootSvg = rootSvgImported;
}
if(d3.select(svgQueryName).empty()) {
/*
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.attr("id", "d3PlantExplorerSvgTooltip")
.text("Sin Datos");
svg = d3.select("#d3PlantExplorer")
.append("svg")
.attr("id", "d3PlantExplorerSvg")
.attr("width", w)
.attr("height", h)
.style("border", "1px solid black");
*/
if(rootSvgImported == null) {
rootSvg = d3.select("#d3PlantExplorer")
.append("svg")
.attr("id", svgName)
.attr("width", w)
.attr("height", h)
.style("border", "0px solid black")
.append("g");
}
if(rootSvgImported == null) {
imgs = rootSvg.selectAll("image").data([0]);
imgs.enter()
.append("svg:image")
.attr("id", svgBackgrounImageId)
.attr("xlink:href", urlFondo)
.attr("x", "0")
.attr("y", "0")
.attr("width", w)
.attr("height", h);
}
console.log("d3plantExplorerGenerarMapa [02] --> OK");
} else if(reescalado == 1) {
console.log("d3plantExplorerGenerarMapa [03] --> OK");
} else {
rootSvg = d3.select(svgQueryName);
rootSvg.selectAll("rect").data([]).exit().remove();
rootSvg.attr("width", w)
.attr("height", h);
imgs = d3.select(svgBackgrounImageQueryId);
imgs.attr("width", w)
.attr("height", h);
console.log("d3plantExplorerGenerarMapa [04] --> OK");
}
//
// Añadimos los racks
//
svg = rootSvg.append("g");
//svg = rootSvg;
svg.selectAll("rect")
.data(svgJsonData)
.enter()
.append("rect")
.on("mousedown", function(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
console.log("rect [mousedown] --> OK");
d3highlightElement(d.cfgInsRack_identificador, fillColorOn, true);
})
.on("click", function(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
console.log("rect [click] --> OK");
// Actualizamos el tooltip
d3.select("#d3TooltipRackSeleccionado").text(d.cfgInsRack_info);
d3.select("#d3TooltipRackNoSeleccionado").text(d.cfgInsRack_info);
d3generateMouseTooltip(d.cfgInsRack_identificador, null, 0);
// Comprobamos el click
if(d3CiSelected != d.cfgInsRack_identificador) {
// Marcamos el elemento actual como selccionado
d3highlightElement(d3CiSelected, null, false);
d3CiSelected = d.cfgInsRack_identificador;
d3highlightElement(d3CiSelected, fillColorOn, true);
// Mostramos y ocultamos el panel de información de rack
//d3ShowHideElement("d3PanelOperacionesRacks",1,0,0);
d3ShowHideElement("d3PanelOperacionesRacksRackSeleccionado",1,0,0);
d3ShowHideElement("d3PanelOperacionesRacksRackNoSeleccionado",0,0,0);
// Notificamos a Movilab el CI que estamos seleccionando.
d3plantExplorerClickHandler(2, d3.mouse(this), d);
} else {
}
})
.on("dblclick", function(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
//d3plantExplorerClickHandler(2, d3.mouse(this), d);
//d3plantExplorerClickHandler(51, d3.mouse(this), d);
})
.on("contextmenu", function(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
// Actualizamos el tooltip
d3.select("#d3TooltipRackSeleccionado").text(d.cfgInsRack_info);
d3.select("#d3TooltipRackNoSeleccionado").text(d.cfgInsRack_info);
//d3plantExplorerClickHandler(2, d3.mouse(this), d);
//d3plantExplorerClickHandler(51, d3.mouse(this), d);
// Mostramos el panel de informacion
//d3PanelInfoCiShowHide(0);
// Menu contextual para el plano
d3ContextMenuAttach(d.cfgInsRack_identificador, contextualMenuRectDataSet, "contextmenu");
})
.on("mouseover", function(d) {
if(!isDragging) {
d3.event.stopPropagation();
d3.event.preventDefault();
console.log("rect [mouseover] --> OK");
// Menu contextual para el plano
d3highlightElement(d.cfgInsRack_identificador, fillColorOn, true);
// Actualizamos el tooltip
d3.select("#d3TooltipRackNoSeleccionado").text(d.cfgInsRack_info);
// Tooltip
d3generateMouseTooltip(d.cfgInsRack_identificador, d.cfgInsRack_info + "<br>Estado: " + d.cfgInsRack_estado, 1);
}
// Notificamos a Movilab el CI que estamos seleccionando.
//d3plantExplorerClickHandler(2, d3.mouse(this), d);
})
.on("mouseout", function(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
console.log("rect [mouseout] --> OK");
d3.select("#d3TooltipRackNoSeleccionado").text("");
d3generateMouseTooltip(d.cfgInsRack_identificador, null, 0);
if(d3CiSelected != d.cfgInsRack_identificador) {
d3highlightElement(d.cfgInsRack_identificador, d.color, false);
}
//d3PanelInfoCiShowHide(0);
})
.call(drag)
.attr("id", function(d) { return d.cfgInsRack_identificador; })
.attr("x", function(d) {
var px = widthScaleCm2Px(d.x_axisCm);
console.log("d3Info [" + d.cfgInsRack_identificador + "] --> " + "(x)=px:" + px + ", cm:" + d.x_axisCm);
d.x_axisPx = px;
return d.x_axisPx;
})
.attr("y", function(d) {
var px = heightScaleCm2Px(d.y_axisCm);
console.log("d3Info [" + d.cfgInsRack_identificador + "] --> " + "(y)=px:" + px + ", cm:" + d.y_axisCm);
d.y_axisPx = px;
return d.y_axisPx;
})
.attr("width", function(d) {
var px = widthScaleCm2Px(d.x_anchoCm);
console.log("d3Info [" + d.cfgInsRack_identificador + "] --> " + "(width)=px:" + px + ", cm:" + d.x_anchoCm);
d.x_anchoPx = px;
return d.x_anchoPx;
})
.attr("height", function(d) {
var px = heightScaleCm2Px(d.y_altoCm);
console.log("d3Info [" + d.cfgInsRack_identificador + "] --> " + "(height)=px:" + px + ", cm:" + d.y_altoCm);
d.y_altoPx = px;
return d.y_altoPx;
})
//.attr("rx", 10) // curva redondeada
//.attr("ry", 10) // curva redondeada
.attr("stroke", strokeOpacityColorOff)
.attr("fill", function(d) { return d.color; })
.attr("stroke-opacity", strokeOpacityOff) //0.1
.attr("fill-opacity", function(d){
if(d.ocupado==1) {
return 0.5;
}
return 0.5;
})
.attr("display", function(d) {
// Verificamos que tenemos que verlo, es decir que la posicion no sea 0,0
if(d.x_axisCm == 0 || d.y_axisCm == 0) {
return "none";
}
return "block";
});
console.log("d3plantExplorerGenerarMapa [07] --> OK");
// Listener para ZOOM
zoomListener(rootSvg);
console.log("d3plantExplorerGenerarMapa [08] --> OK");
// Eventos asociados al "RECT" que acabamos de crear-
// Menu contextual para el plano
d3ContextMenuAttach(svgName, contextualMenuSvgDataSet, "contextmenu");
// Mostramos y ocultamos el panel de información de rack
//d3ShowHideElement("d3PanelOperacionesRacks",0,0,0);
d3ShowHideElement("d3PanelOperacionesRacksRackSeleccionado",0,0,0);
d3ShowHideElement("d3PanelOperacionesRacksRackNoSeleccionado",1,0,0);
// Al hacer click sobre el plano borramos todos los menus
rootSvg.on("click", function(d) {
d3ContextMenuRemove(null);
})
console.log("d3plantExplorerGenerarMapa [09] --> OK");
// Marcamos el SVG como creado.
svgCreated = true; //this.
console.log("d3plantExplorerGenerarMapa --> fin");
}
答案 0 :(得分:1)
问题解决了。
1)画一个SVG。
2)绘制区域(RECTANGLES)并将其分类为“区域Trabajo Limitada”。
3)绘制可拖动元素(RECTANGLES)。
4)如果在有限区域内拖动元素,脚本将还原它。
注意:
使用此脚本,您可以轻松限制掉落区域或检测碰撞。
您可以扩展使用圈子而不是rects的方法。
感谢大家的帮助。
HTML
<div id="area">
</div>
<div id="info"></div>
CSS
.areaTrabajoLimitada {
fill:green;
fill-opacity: .1;
}
.objeto {
fill:red;
fill-opacity: .1;
}
.DnD {
stroke: red;
}
脚本:
//
// Area de trabajo
//
var jsonAreasTrabajo = [{
"width": 200,
"height": 200,
"x": 10,
"y": 10
}, {
"width": 200,
"height": 200,
"x": 300,
"y": 10
}];
var xwidth = 400;
var yheight = 400;
var xmin = 50;
var xmax = xmin + xwidth;
var ymin = 50;
var ymax = ymin + yheight;
var isDentroAreaLimitada = false;
var dragPxOrigen = 0;
var dragPxDestino = 0;
var isDragging = false;
var msgDnDNoPermitido = "No drop in this area!"
function puntoDentroArea(point, vs) {
// Algoritmo basado en
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
//
// (x,y) ------------ (xmax,y)
// | |
// | |
// (x,ymax) --------- (xmax,ymax)
//
console.log("puntoDentroArea -->", point, vs);
var x = point[0],
y = point[1];
var inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
var xi = vs[i][0],
yi = vs[i][1];
var xj = vs[j][0],
yj = vs[j][1];
var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
console.log("puntoDentroArea -->",inside);
return inside;
}
function dentroDelAreaLimitada(px, py, w, h) {
// Creamos los puntos para evaluar. Los puntos son los cuatro puntos
// del restangulo que estamos arrastrando
var ipxw = px + parseFloat(w);
var ipyh = py + parseFloat(h);
var jsonPuntos = [
{"x":px, "y":py},
{"x":px, "y": ipyh},
{"x":ipxw, "y":py},
{"x":ipxw, "y":ipyh}];
// Recorremos todas las areas de trabajo limitadas.
// Estas areas son todos los rectangulos RECT en el SVG
// que tengan el atributo class="areaTrabajoLimitada"
d3.selectAll(".areaTrabajoLimitada").each(function (d, i) {
// Datos del poligono. Es un rectangulo que delimita el area
// Limitada.
// - Para obtener los datos en NUMEROS hay que llamar a node().getBBox()
var bbox = d3.select(this).node().getBBox();
var x = bbox.x;
var y = bbox.y;
var width = bbox.width;
var height = bbox.height;
var xmax = x + width;
var ymax = y + height;
// El poligono respresenta el area limitada
var poligono = [
[x, y],
[x, ymax],
[xmax, ymax],
[xmax, y]
];
// Recorremos todos lo puntos del rectangulo q arrastramos.
var resultado = false;
for(var i=0; i<jsonPuntos.length;i++){
// Evaluamos los resultados para cada uno de los puntos del cuadrado.
//var punto = [px, py];
var punto = [jsonPuntos[i].x, jsonPuntos[i].y];
console.log("punto", punto);
resultado = puntoDentroArea(punto, poligono);
if(resultado == true) {
break;
}
}
if (resultado == true) {
isDentroAreaLimitada = resultado;
}
});
}
//
// Define drag behavior
//
//var drag = d3.behavior.drag().on("drag", dragmove);
var drag = d3.behavior.drag()
.on("dragstart", d3dragstarted)
.on("drag", d3dragged)
.on("dragend", d3dragended);
function d3dragstarted(d) {
isDragging = false; //this.
d3.event.sourceEvent.stopPropagation(); // cancelamos listeners
console.log("d3dragstarted");
dragPxOrigen = d3.select(this).attr("x");
dragPyOrigen = d3.select(this).attr("y");
}
function d3dragged(d) {
isDragging = true; //realmente esta moviendolo. //this.
if (isDragging == true) { //this.
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
d3.select(this)
.attr("x", x)
.attr("y", y);
}
}
function d3dragended(d) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var xoffset = d3.select(this).attr("width");
var yoffset = d3.select(this).attr("height");
dentroDelAreaLimitada(x, y, xoffset, yoffset);
d3.select("#info").text(x + "," + y + " --> " + isDentroAreaLimitada);
if (isDragging == true) { //this.
if (isDentroAreaLimitada) {
d3.select(this)
.attr("x", dragPxOrigen)
.attr("y", dragPyOrigen);
alert(msgDnDNoPermitido);
} else {
d3.select(this)
.attr("x", x)
.attr("y", y);
}
}
isDragging = false; //this.
isDentroAreaLimitada = false;
console.log("d3dragended");
}
function dragmove(d) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var xoffset = d3.select(this).attr("width");
var yoffset = d3.select(this).attr("height");
dentroDelAreaLimitada(x, y, xoffset, yoffset);
d3.select("#info").text(x + "," + y + " --> " + isDentroAreaLimitada);
}
//
// Click
//
function click() {
// Ignore the click event if it was suppressed
if (d3.event.defaultPrevented) return;
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
console.log(" --> " + veredicto);
}
//var root = d3.select("#d3PlantExplorerSvg");
var root = d3.select("#area").append("svg")
.attr("id", "mysvg")
.attr("width", 600)
.attr("height", 600)
.style("border", "1px solid black");
var areaTrabajo = root.append("g").selectAll("rect")
.data(jsonAreasTrabajo)
.enter()
.append("rect")
.attr("id", "areaTrabajo01")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("width", function (d) {
return d.width;
})
.attr("height", function (d) {
return d.height;
})
.attr("class", "areaTrabajoLimitada");
var objeto = root.append("g").append("rect")
.attr("x", 300)
.attr("y", 300)
.attr("width", 30)
.attr("height", 30)
.classed("objeto", true)
.classed("DnD", true)
.on("click", click)
.call(drag);