我的任务是创建一个拖放式标签设计师,我几乎不知道如何实际做到这一点。
此刻它几乎看起来像这样,仍然是静止的,但它让我知道我想要的是什么。 人们需要能够将右侧的图标拖到空白标签(白色和绿色区域)上。在拖动时,将创建图标所代表的对象(通常是图像或文本)的新实例,我需要保存实例的放置位置和大小,并将空白标签图像作为绘图区域的边界。
目前,图片只是一个标签,图标被定义为标签,但我可能不得不改变这一点。
那么有哪些技术可用于此类功能?虽然我不确定它是否支持图像(来自精灵图像?)作为它的对象,但我听说过SVG可能会做到这一点。 我没有做过这种事情的经验,所以欢迎任何提示和指导。
这里是屏幕截图中显示的内容的HTML(从ASP.NET MVC视图生成)
<div class="col-md-12">
<div class="row designerBox">
<div class="col-md-10 designerCanvas">
<img class="col-md-12 blankLabel" id="imgLabel" src="/images/Temp/temp.png">
</div>
<div class="col-md-2 designerControlBox">
<h3 class="col-md-12">Controls</h3>
<span title="Hazard Icon" class="col-md-4 designerControl hazardIcon"></span>
<span title="Product Info" class="col-md-4 designerControl info"></span>
<span title="Product Name" class="col-md-4 designerControl prodName"></span>
<span title="Color Info 1" class="col-md-4 designerControl color1"></span>
<span title="Color Info 2" class="col-md-4 designerControl color2"></span>
<span title="Mix Icons" class="col-md-4 designerControl mixIcon"></span>
<span title="Usage Icons" class="col-md-4 designerControl useIcon"></span>
<span title="Batch Number" class="col-md-4 designerControl batch"></span>
<span title="Production Date" class="col-md-4 designerControl prodDate"></span>
<span title="VOC" class="col-md-4 designerControl voc"></span>
<span title="Content" class="col-md-4 designerControl content"></span>
<span title="Bar Code" class="col-md-4 designerControl barcode"></span>
<span title="Address" class="col-md-4 designerControl address"></span>
<span title="Phone" class="col-md-4 designerControl phone"></span>
<span title="Fax" class="col-md-4 designerControl fax"></span>
<span title="Email address" class="col-md-4 designerControl email"></span>
<span title="Site" class="col-md-4 designerControl website"></span>
</div>
<div class="col-md-10">
<div class="col-md-6">
<label class="col-md-3 control-label" for="Width">Width</label>
<div class="col-md-9">
<input name="Width" class="form-control" id="Width" style="width: 50px; display: inline-block;" type="number" value="0" data-val-required="Please enter the Width." data-val="true"> mm
</div>
</div>
<div class="col-md-6">
<label class="col-md-3 control-label" for="Heigth">Heigth</label>
<div class="col-md-9">
<input name="Heigth" class="form-control" id="Heigth" style="width: 50px; display: inline-block;" type="number" value="0" data-val-required="Please enter the Heigth." data-val="true"> mm
</div>
</div>
<div class="col-md-12">
<label class="col-md-2 control-label" for="Name">Label Name</label>
<div class="col-md-10">
<input name="Name" class="form-control" id="Name" type="text" value="" data-val-required="Please enter the Label Name." data-val="true">
</div>
</div>
<div class="col-md-12">
<label class="control-label col-md-12">Can be used for containers from <input class="form-control" style="width: 50px; display: inline-block;" asp-for="Name"> till <input class="form-control" style="width: 50px; display: inline-block;" asp-for="Name"> liter.</label>
</div>
</div>
<div class="col-md-10">
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Name"></span>
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Width"></span>
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Heigth"></span>
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MinVolume"></span>
<span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MaxVolume"></span>
</div>
</div>
更新
所以我几乎让它按照我想要的方式工作,现在只有第一个图标(红色钻石)。我现在可以在我添加的svg上拖动该图标,我可以选择(和使用shift或ctrl的多个),用+/-调整大小并使用箭头键移动它。在IE上,我也可以通过再次拖动来移动放置的图标,但由于某种原因,后者在chrome中不起作用:(
FF甚至不支持放弃svg上的图标。
imgLabel有点编辑,现在由SVG容器加入:
<div class="col-md-10 designerCanvasContainer">
<img id="imgLabel" src="/images/Temp/temp.png" class="col-md-12 blankLabel" />
<svg id="svgLabel" class="col-md-12 designerCanvas" ondrop="drop(event)" ondragover="allowDrop(event)"></svg>
</div>
以下是相关的css vor:
.designerCanvasContainer {
background-color: white;
padding:0;
position:relative;
}
.designerCanvas {
position: absolute;
top: 0;
left: 0;
bottom: 0;
padding: 0;
height: 100%;
}
.blankLabel {
padding: 0;
background-color: white;
}
包含div和img需要相对定位(因此div会根据图像调整大小)并且svg需要是绝对的并且对齐div的边缘,因此它被放置在图像上。 / p>
当然我也在图标中添加了一个拖动事件,getOffset函数用于确定光标所在的图标位置,或者它用光标绘制已删除的图标0,0的图标,把它移出原位:
<span draggable="true" ondragstart="drag(event)" onmousedown="getOffset(event)" class="col-md-4 designerControl hazardIcon" title="Hazard Icon" />
不用说,所有的东西都被一些js魔法所触动:
var xOffset = 0;
var yOffset = 0;
var xOld = 0;
var yOld = 0;
var hazardDrag = new Image();
var id;
$(document).ready(function () {
hazardDrag.src = '/images/dragHazardIcon.png';
//Part of setdragimage-ie.js to support event.dataTransfer.setDragImage(img, x, y) in IE
//Function will preload the custom drag image.
setDragImageIEPreload(hazardDrag);
});
$(document).keydown(function (e) {
if (e.key !== "Shift" && $(".selected").length)
{
//Only when we have selected SVG objects we have use for the key actions
//The e.preventDefault() are to prevent the default actions of the key
//(e.g.scrolling with the arrows or going back to the previous page with backspace)
switch (e.key)
{
case "ArrowUp":
case "Up": {
move(0, -1);
e.preventDefault();
break;
}
case "ArrowDown":
case "Down": {
move(0, 1);
e.preventDefault();
break;
}
case "ArrowLeft":
case "Left": {
move(-1, 0);
e.preventDefault();
break;
}
case "ArrowRight":
case "Right": {
move(1, 0);
e.preventDefault();
break;
}
case "Subtract":
case "-": {
resize(-1);
e.preventDefault();
break;
}
case "Add":
case "+": {
resize(1);
e.preventDefault();
break;
}
case "Del":
case "Delete":
case "Backspace": {
$(".selected").each(function (i) {
$("#" + this.id + "-border").remove();
});
$(".selected").remove();
e.preventDefault();
break;
}
case "Enter": {
$(".selected").removeClass("selected");
e.preventDefault();
break;
}
}
//Always update the selection frames so they don't get left behind.
drawSelectBoxes();
}
});
//All dropped icons will have a svg class attached to it.
//This function will provide (de)select functionality to those objects.
$(document).on('click', function (e) {
var id = "#" + e.target.id;
if (id !== "#" && $(id).hasClass("svg")) {
if (!e.ctrlKey && !e.shiftKey) {
$(".selected").removeClass("selected");
$(id).addClass("selected");
}
else if ($(id).hasClass("selected"))
$(id).removeClass("selected");
else
$(id).addClass("selected");
}
else
$(".selected").removeClass("selected");
drawSelectBoxes();
});
//Small function to execute the keyboard move
function move(x, y) {
$(".selected").each(function (i) {
$(this).attr("x", (parseFloat($(this).attr("x")) + x));
$(this).attr("y", (parseFloat($(this).attr("y")) + y));
});
}
//Small function to execute the +/- resizes
function resize(by) {
$(".selected").each(function (i) {
$(this).attr("height", (parseFloat($(this).attr("height")) + by));
$(this).attr("width", (parseFloat($(this).attr("width")) + by));
});
}
//Turns of default drop behaviour on svg container
function allowDrop(ev) {
ev.preventDefault();
}
//Gets the current cursor coordinates, using this to nove a svg object
function getCoord(ev) {
xOld = ev.x;
yOld = ev.y;
}
//As svg objects do not support css borders, I'm using svg rectangle to draw a border
//around a selected svg object.
function drawSelectBoxes()
{
$(".svg").each(function (x) {
if ($(this).hasClass("selected")) {
if (!$("#" + this.id + "-border").length) {
var border = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
border.setAttributeNS(null, 'height', $(this).attr('height'));
border.setAttributeNS(null, 'width', $(this).attr('width'));
border.setAttributeNS(null, 'x', $(this).attr('x'));
border.setAttributeNS(null, 'y', $(this).attr('y'));
border.setAttributeNS(null, 'stroke', '#cccccc');
border.setAttributeNS(null, 'fill', 'none');
border.setAttributeNS(null, 'id', this.id + "-border");
$("#svgLabel").append(border);
}
else {
var border = "#" + this.id + "-border";
$(border).attr('height', $(this).attr('height'));
$(border).attr('width', $(this).attr('width'));
$(border).attr('x', $(this).attr('x'));
$(border).attr('y', $(this).attr('y'));
}
}
else
$("#" + this.id + "-border").remove();
});
}
//Get's the offset of the cursor towards the start of an icon you're dragging.
//We'll use this offset to draw the svg object at the correct location.
//If we don't do this the cursor will be the 0,0 coordinate of the dropped image and
//it'll move compared to the shown drag image.
function getOffset(ev) {
xOffset = ev.offsetX;
yOffset = ev.offsetY;
$(".selected").removeClass("selected");
drawSelectBoxes();
}
//The drag event, it sets the correct parameters so the drop event will work accordingly.
//I'm using the "text" data variable cause the original icon, and thus it's class names,
//is not available in the drop event when droping the external icons.
function drag(ev) {
var img;
if ($(ev.target).hasClass("hazardIcon")) {
img = hazardDrag;
ev.dataTransfer.setDragImage(img, xOffset, yOffset);
ev.dataTransfer.setData("text", "hazard");
}
else if ($(ev.target).hasClass("svg")) {
id = "#" + ev.target.id;
ev.dataTransfer.setData("text", "svg");
}
}
//The drop event set on the svg container.
//It'll create a new svg object when a new icon is dropped on the container
//or update the x/y of an existing svg object when that is dragged.
function drop(ev) {
ev.preventDefault();
var svg;
if (ev.target.id === "svgLabel")
svg = ev.target;
else
svg = ev.target.parentNode;
var type = ev.dataTransfer.getData("text");
if (type != "svg")
{
var mxy = svg.createSVGPoint();
mxy.x = ev.clientX;
mxy.y = ev.clientY;
pos = mxy.matrixTransform(svg.getScreenCTM().inverse());
}
switch (type) {
case "hazard": {
//Here we'll create a new svg <image> for our hazad icon.
//We can't use a standard jquery append() as it'll translate the <image>
//to <img> and this is not a valid svg object.
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
img.setAttributeNS(null, 'height', '50');
img.setAttributeNS(null, 'width', '50');
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '' + hazardDrag.src + '');
img.setAttributeNS(null, 'x', '' + pos.x - xOffset + '');
img.setAttributeNS(null, 'y', '' + pos.y - yOffset + '');
img.setAttributeNS(null, 'draggable', 'true');
img.setAttributeNS(null, 'ondragstart', "drag(event)");
img.setAttributeNS(null, 'onmousedown', "getCoord(event)");
img.setAttributeNS(null, 'visibility', 'visible');
img.setAttributeNS(null, 'class', 'svg');
img.setAttributeNS(null, 'id', 'hazard-' + parseInt(pos.x) + '-' + parseInt(pos.y));
$("#svgLabel").append(img);
break;
}
case "svg": {
//Svg objects just have their coordinated updated.
$(id).attr("x", (parseFloat($(id).attr("x")) + (ev.x - xOld)));
$(id).attr("y", (parseFloat($(id).attr("y")) + (ev.y - yOld)));
}
}
drawSelectBoxes();
}
所以这是设计师到目前为止,如果有人知道如何在Chrome和FF中使其完全正常运行,我将非常感激。 对于你知道的IE公司来说客户端很好,但我想至少让它在3大浏览器中运行。
简而言之,Chrome仍然缺少对svg对象的拖拽,FF无法放在svg容器上,drop事件根本不会被触发。
更新2
所以将drop和dragover事件移动到周围的div而不是svg容器,FF之前就像Chrome一样工作,其他2个浏览器没有变化。 drop函数没有受到影响,因为生成的目标仍然是svg容器。 现在所需要的就是能够使用Chrome和FF中的鼠标移动svg对象。
编辑HTML:
<div class="col-md-10 designerCanvasContainer" ondrop="drop(event)" ondragover="allowDrop(event)">
<img id="imgLabel" src="/images/Temp/temp.png" class="col-md-12 blankLabel" />
<svg id="svgLabel" class="col-md-12 designerCanvas"></svg>
</div>