将形状从调色板拖到 Konvajs 舞台上
Dragging shapes from a palette onto a Konvajs stage
在 Konvajs 聊天流中,有人最近要求提供从调色板拖放到 Konvajs 库前面的 HTML5 canvas 的示例。没有现成的例子,我很好奇如何实现它。
我在 codepen 中回答了这个问题,但决定 post 在这里作为(我自己的)未来参考。请参阅下面我的回答。
这是我使用 jquery UI 可拖放的解决方案。 Konvajs 需要 jquery,因此使用 jquery UI 只是向前迈出了一小步。调色板是一组小 canvas 元素,每个可拖动项目绘制一个形状。调色板可以放在任何 html 元素上,不需要以任何方式附加到主舞台。
// Set up the canvas to catch the dragged shapes
var s1 = new Konva.Stage({container: 'container1', width: 500, height: 200});
// add a layer to host the 'dropped' shapes.
var layer1 = new Konva.Layer({draggable: false});
s1.add(layer1);
// set up the palette of draggable shapes - 5 sample shapes.
var palletteEle = $('#pallette');
var d, ps, l, c;
for (var i = 0; i<5; i = i + 1){
// make a div to hold the shape
d = $('<div id="shape' + i + '" class="draggable">Shape</div>')
palletteEle.append(d)
// make a mini stage to hold the shape
ps = new Konva.Stage({container: 'shape' + i, width: 50, height: 50});
// make a layer to hold the shape
l = new Konva.Layer();
// add layer to palette
ps.add(l);
// make a shape - red circles for example
c = new Konva.Circle({x: 24, y: 24, radius: 22, fill: 'red', stroke: 'black'})
l.add(c);
ps.draw();
}
// make a crosshair to give some idea of the drop location
var cross = new Konva.Line({points: [10, 0, 10, 20, 10, 10, 0, 10, 20, 10],
stroke: 'gold',
strokeWidth: 1,
lineCap: 'round',
lineJoin: 'round'})
layer1.add(cross);
//s1.draw();
// make the main stage a drop target
$('#container1').addClass('droppable');
// function to move the cross hairs
function moveCross(x, y){
cross.x(x);
y = y - $('#container1').offset().top;
cross.y(y < 0 ? 0 : y);
s1.draw();
}
// draggable setup. Movecross used to move the crosshairs. More work needed but shows the way.
$( ".draggable" ).draggable({
zIndex: 100,
helper: "clone",
opacity: 0.35,
drag: function( event, ui ) {moveCross(ui.offset.left , ui.offset.top + $(this).offset().top)}
});
// set up the droppable
$( ".droppable" ).droppable({
drop: function( event, ui ) {
dropShape(ui.position.left, ui.position.top)
}
});
// Function to create a new shape when we drop something dragged from the palette
function dropShape() {
var c1 = new Konva.Circle({x: cross.x(), y: cross.y(), radius: 22, fill: 'red', stroke: 'black'});
layer1.add(c1);
cross.x(0); cross.y(0);
cross.moveToTop(); // move the cross to the top to stop going bahind previously dropped shapes.
s1.draw();
}
p
{
padding: 4px;
}
#container1
{
display: inline-block;
width: 500px;
height: 200px;
background-color: silver;
overflow: hidden;
}
#pallette
{
height: 52px; width: 500px;
border: 1px solid #666;
margin-bottom: 10px;
z-index: 10;
}
.draggable
{
width:50px;
height: 50px;
display: inline-block;
border: 1px solid #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Drag a red circle from the pallette and drop it on the grey canvas.
</p>
<div id='pallette'></div>
<div id='container1'></div>
我试过 Vanquished Wombat 的解决方案,这是一个很好的例子。但最终我希望我的调色板与 Konva 分开。所以我修改了原始片段以使用 Html5 拖放,没有任何 jQuery。请参阅下面的代码段。您可以将调色板中的星星和圆圈拖到 Konva canvas 中。目前您必须拖放到另一个形状上,但您可以轻松修改它以拖放到 canvas 上的任意位置。我为调色板项目使用文本,为拖动对象使用自定义图像只是为了好玩。但是您可以只使用 img 而不是使用 setDragImage 代码。
const CUSTOM_DATA_TYPE = 'text/x-node-type';
// Set up the canvas to catch the dragged shapes
var s1 = new Konva.Stage({
container: 'container1',
width: 500,
height: 200
});
// add a layer to host the 'dropped' shapes.
var layer1 = new Konva.Layer({
draggable: false
});
s1.add(layer1);
for (let t = 0; t < 10; t++) {
let rect = document.getElementById('container1').getBoundingClientRect();
let x = Math.floor(Math.random() * rect.width);
let y = Math.floor(Math.random() * rect.height);
let type = Math.floor(Math.random() * 100) % 2 == 0 ? 'circle' : 'star';
dropShape(x, y, type);
}
// Function to create a new shape when we drop something dragged from the palette
function dropShape(x, y, type) {
var shape;
if (type == 'circle') {
shape = new Konva.Circle({
x: x,
y: y,
radius: 22,
fill: 'blue',
stroke: 'black'
});
} else {
shape = new Konva.Star({
x: x,
y: y,
numPoints: 5,
innerRadius: 10,
outerRadius: 20,
fill: 'purple',
stroke: 'black'
});
}
layer1.add(shape);
s1.draw();
}
function cursorToCanvasPos(e) {
let clientRect = document.getElementById('container1').getBoundingClientRect();
let pointerPosition = {
x: e.clientX - clientRect.x,
y: e.clientY - clientRect.y,
};
return pointerPosition;
}
function getHoveredShape(e) {
let pointerPosition = cursorToCanvasPos(e);
return s1.getIntersection(pointerPosition);
}
function onDragStart(e, type) {
// Do this or other things can mess with your drag
e.stopPropagation();
e.dataTransfer.setData(CUSTOM_DATA_TYPE, type);
e.dataTransfer.effectAllowed = "all";
var dragIcon = document.createElement('img');
dragIcon.src = 'https://placehold.it/100x100';
dragIcon.width = 100;
e.dataTransfer.setDragImage(dragIcon, 150, 150);
}
function onDragOver(e) {
// Might break if you don't have this
e.stopPropagation();
// Breaks for sure if you don't have this
e.preventDefault();
let thing = getHoveredShape(e);
if (thing) {
e.dataTransfer.dropEffect = "move";
// Just fire off a custom even if you want to, this does nothing in this example.
thing.fire('htmlDragOver');
} else {
e.dataTransfer.dropEffect = "none";
}
}
function onDrop(e) {
e.stopPropagation();
let type = e.dataTransfer.getData(CUSTOM_DATA_TYPE);
let pos = cursorToCanvasPos(e);
dropShape(pos.x, pos.y, type);
}
p {
padding: 4px;
}
#container1 {
display: inline-block;
width: 500px;
height: 200px;
background-color: silver;
overflow: hidden;
}
#palette {
height: 52px;
width: 500px;
border: 1px solid #666;
margin-bottom: 10px;
z-index: 10;
}
#palette span {
width: 50px;
height: 25px;
display: inline-block;
border: 1px solid #666;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Drag circle/star from the palette onto an existing shape on the canvas below.
</p>
<div id='palette'>
<!-- Pre-load this image so it'll be used for our drag -->
<img src="https://placehold.it/100x100" style="display: none">
<span draggable="true" ondragstart="onDragStart(event, 'circle')">circle</span>
<span draggable="true" ondragstart="onDragStart(event, 'star')">star</span>
</div>
<div id='container1' ondragover="onDragOver(event)" ondrop="onDrop(event)"></div>
在 Konvajs 聊天流中,有人最近要求提供从调色板拖放到 Konvajs 库前面的 HTML5 canvas 的示例。没有现成的例子,我很好奇如何实现它。
我在 codepen 中回答了这个问题,但决定 post 在这里作为(我自己的)未来参考。请参阅下面我的回答。
这是我使用 jquery UI 可拖放的解决方案。 Konvajs 需要 jquery,因此使用 jquery UI 只是向前迈出了一小步。调色板是一组小 canvas 元素,每个可拖动项目绘制一个形状。调色板可以放在任何 html 元素上,不需要以任何方式附加到主舞台。
// Set up the canvas to catch the dragged shapes
var s1 = new Konva.Stage({container: 'container1', width: 500, height: 200});
// add a layer to host the 'dropped' shapes.
var layer1 = new Konva.Layer({draggable: false});
s1.add(layer1);
// set up the palette of draggable shapes - 5 sample shapes.
var palletteEle = $('#pallette');
var d, ps, l, c;
for (var i = 0; i<5; i = i + 1){
// make a div to hold the shape
d = $('<div id="shape' + i + '" class="draggable">Shape</div>')
palletteEle.append(d)
// make a mini stage to hold the shape
ps = new Konva.Stage({container: 'shape' + i, width: 50, height: 50});
// make a layer to hold the shape
l = new Konva.Layer();
// add layer to palette
ps.add(l);
// make a shape - red circles for example
c = new Konva.Circle({x: 24, y: 24, radius: 22, fill: 'red', stroke: 'black'})
l.add(c);
ps.draw();
}
// make a crosshair to give some idea of the drop location
var cross = new Konva.Line({points: [10, 0, 10, 20, 10, 10, 0, 10, 20, 10],
stroke: 'gold',
strokeWidth: 1,
lineCap: 'round',
lineJoin: 'round'})
layer1.add(cross);
//s1.draw();
// make the main stage a drop target
$('#container1').addClass('droppable');
// function to move the cross hairs
function moveCross(x, y){
cross.x(x);
y = y - $('#container1').offset().top;
cross.y(y < 0 ? 0 : y);
s1.draw();
}
// draggable setup. Movecross used to move the crosshairs. More work needed but shows the way.
$( ".draggable" ).draggable({
zIndex: 100,
helper: "clone",
opacity: 0.35,
drag: function( event, ui ) {moveCross(ui.offset.left , ui.offset.top + $(this).offset().top)}
});
// set up the droppable
$( ".droppable" ).droppable({
drop: function( event, ui ) {
dropShape(ui.position.left, ui.position.top)
}
});
// Function to create a new shape when we drop something dragged from the palette
function dropShape() {
var c1 = new Konva.Circle({x: cross.x(), y: cross.y(), radius: 22, fill: 'red', stroke: 'black'});
layer1.add(c1);
cross.x(0); cross.y(0);
cross.moveToTop(); // move the cross to the top to stop going bahind previously dropped shapes.
s1.draw();
}
p
{
padding: 4px;
}
#container1
{
display: inline-block;
width: 500px;
height: 200px;
background-color: silver;
overflow: hidden;
}
#pallette
{
height: 52px; width: 500px;
border: 1px solid #666;
margin-bottom: 10px;
z-index: 10;
}
.draggable
{
width:50px;
height: 50px;
display: inline-block;
border: 1px solid #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Drag a red circle from the pallette and drop it on the grey canvas.
</p>
<div id='pallette'></div>
<div id='container1'></div>
我试过 Vanquished Wombat 的解决方案,这是一个很好的例子。但最终我希望我的调色板与 Konva 分开。所以我修改了原始片段以使用 Html5 拖放,没有任何 jQuery。请参阅下面的代码段。您可以将调色板中的星星和圆圈拖到 Konva canvas 中。目前您必须拖放到另一个形状上,但您可以轻松修改它以拖放到 canvas 上的任意位置。我为调色板项目使用文本,为拖动对象使用自定义图像只是为了好玩。但是您可以只使用 img 而不是使用 setDragImage 代码。
const CUSTOM_DATA_TYPE = 'text/x-node-type';
// Set up the canvas to catch the dragged shapes
var s1 = new Konva.Stage({
container: 'container1',
width: 500,
height: 200
});
// add a layer to host the 'dropped' shapes.
var layer1 = new Konva.Layer({
draggable: false
});
s1.add(layer1);
for (let t = 0; t < 10; t++) {
let rect = document.getElementById('container1').getBoundingClientRect();
let x = Math.floor(Math.random() * rect.width);
let y = Math.floor(Math.random() * rect.height);
let type = Math.floor(Math.random() * 100) % 2 == 0 ? 'circle' : 'star';
dropShape(x, y, type);
}
// Function to create a new shape when we drop something dragged from the palette
function dropShape(x, y, type) {
var shape;
if (type == 'circle') {
shape = new Konva.Circle({
x: x,
y: y,
radius: 22,
fill: 'blue',
stroke: 'black'
});
} else {
shape = new Konva.Star({
x: x,
y: y,
numPoints: 5,
innerRadius: 10,
outerRadius: 20,
fill: 'purple',
stroke: 'black'
});
}
layer1.add(shape);
s1.draw();
}
function cursorToCanvasPos(e) {
let clientRect = document.getElementById('container1').getBoundingClientRect();
let pointerPosition = {
x: e.clientX - clientRect.x,
y: e.clientY - clientRect.y,
};
return pointerPosition;
}
function getHoveredShape(e) {
let pointerPosition = cursorToCanvasPos(e);
return s1.getIntersection(pointerPosition);
}
function onDragStart(e, type) {
// Do this or other things can mess with your drag
e.stopPropagation();
e.dataTransfer.setData(CUSTOM_DATA_TYPE, type);
e.dataTransfer.effectAllowed = "all";
var dragIcon = document.createElement('img');
dragIcon.src = 'https://placehold.it/100x100';
dragIcon.width = 100;
e.dataTransfer.setDragImage(dragIcon, 150, 150);
}
function onDragOver(e) {
// Might break if you don't have this
e.stopPropagation();
// Breaks for sure if you don't have this
e.preventDefault();
let thing = getHoveredShape(e);
if (thing) {
e.dataTransfer.dropEffect = "move";
// Just fire off a custom even if you want to, this does nothing in this example.
thing.fire('htmlDragOver');
} else {
e.dataTransfer.dropEffect = "none";
}
}
function onDrop(e) {
e.stopPropagation();
let type = e.dataTransfer.getData(CUSTOM_DATA_TYPE);
let pos = cursorToCanvasPos(e);
dropShape(pos.x, pos.y, type);
}
p {
padding: 4px;
}
#container1 {
display: inline-block;
width: 500px;
height: 200px;
background-color: silver;
overflow: hidden;
}
#palette {
height: 52px;
width: 500px;
border: 1px solid #666;
margin-bottom: 10px;
z-index: 10;
}
#palette span {
width: 50px;
height: 25px;
display: inline-block;
border: 1px solid #666;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Drag circle/star from the palette onto an existing shape on the canvas below.
</p>
<div id='palette'>
<!-- Pre-load this image so it'll be used for our drag -->
<img src="https://placehold.it/100x100" style="display: none">
<span draggable="true" ondragstart="onDragStart(event, 'circle')">circle</span>
<span draggable="true" ondragstart="onDragStart(event, 'star')">star</span>
</div>
<div id='container1' ondragover="onDragOver(event)" ondrop="onDrop(event)"></div>