开始创建在线Drag-n-Drop设计师

时间:2017-08-10 08:37:26

标签: javascript jquery html html5 svg

我的任务是创建一个拖放式标签设计师,我几乎不知道如何实际做到这一点。

此刻它几乎看起来像这样,仍然是静止的,但它让我知道我想要的是什么。 screenshot of designer so far 人们需要能够将右侧的图标拖到空白标签(白色和绿色区域)上。在拖动时,将创建图标所代表的对象(通常是图像或文本)的新实例,我需要保存实例的放置位置和大小,并将空白标签图像作为绘图区域的边界。

目前,图片只是一个标签,图标被定义为标签,但我可能不得不改变这一点。

那么有哪些技术可用于此类功能?虽然我不确定它是否支持图像(来自精灵图像?)作为它的对象,但我听说过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>

0 个答案:

没有答案