我使用纯JavaScript来使用canvas
元素开发一个简单的绘图应用程序。当用户拖动屏幕时,该应用会创建圈子。其次,双击圆圈将删除特定圆圈。第三,我想点击它时拖动一个圆圈。
var canvas,
context, shapes,
dragging = false, draggingtoMove = false,dragstopped = 0,
dragStartLocation,dragEndLocation,
snapshot;
var numShapes;
function initiate() {
numShapes = 100;
shapes = [];
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
canvas.addEventListener('dblclick', dblclickerase);
}
function dblclickerase(evt){
dragstopped = 0;
shapes.pop();
shapes.pop();
var i, j;
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left)*(canvas.width/bRect.width);
mouseY = (evt.clientY - bRect.top)*(canvas.height/bRect.height);
//Now find which shape was clicked
for (i=0; i < shapes.length; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
// That particular circle is I am going to delete at position i
shapes[i].x = -1;
shapes[i].y = -1;
shapes[i].rad = -1;
shapes[i].color = -1;
eraseCanvas(); // clear canvas
}
}
redraw();
}
function redraw() {
var j;
eraseCanvas(); // clear canvas and redraw it
for(j= 0; j< shapes.length; j++)
{
if(shapes[j].x != -1)
{
context.beginPath();
context.arc(shapes[j].x, shapes[j].y, shapes[j].rad, 0, 2*Math.PI, false);
context.fillStyle = shapes[j].color;
context.fill();
}
}
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function draw(position) {
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
var i=0;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
tempRad = radius;
tempX = dragStartLocation.x;
tempY = dragStartLocation.y;
tempColor = getRndColor();
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor};
if (dragstopped) { shapes.push(tempShape); }
context.beginPath();
context.arc(tempX, tempY, tempRad, 0, 2*Math.PI, false);
//context.closePath();
context.fillStyle = tempColor;
context.fill();
i++;
}
function dragStart(evt) {
//// Here I will check whether if circle overlaps then drag the circle else draw new one.
var i, j;
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left)*(canvas.width/bRect.width);
mouseY = (evt.clientY - bRect.top)*(canvas.height/bRect.height);
//Now find which shape was clicked
for (i=0; i < shapes.length; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
// That particular circle is I am going to delete at position i
//eraseCanvas(); // clear canvas
console.log("clicking on circle");
// return;
}
}
//Draw Circle
dragging = true;
dragstopped = 0;
dragStartLocation = getCanvasCoordinates(evt);
takeSnapshot();
}
function hitTest(shape,mx,my) {
var dx;
var dy;
dx = Math.abs(mx - shape.x);
dy = Math.abs(my - shape.y);
//if it is inside any circle radius will let us know
return (Math.sqrt(dx*dx + dy*dy) < shape.rad);
}
function drag(event) {
dragstopped = 0;
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
draw(position);
}
}
function dragStop(event) {
dragstopped += 1;
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
dragEndLocation = getCanvasCoordinates(event);
draw(position);
}
function getRndColor() {
var r = 255 * Math.random() | 0,
g = 255 * Math.random() | 0,
b = 255 * Math.random() | 0;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
shapes = [];
}
addEventListener("load",initiate);
body {
}
canvas{
border: 1px solid gray;
}
button {
font-size: 128%;
margin-right: -12px;
position: absolute;
}
<canvas id="canvas" width="600" height="400"></canvas>
我的第一个问题是:如何使用在dragStart()
发生时调用的相同mousedown
函数拖动一个圆圈?作为解决方案,我在想,如果我点击圆圈,那么我需要将其拖动。然后找到光标的位置我正在检查hitTest()
是否给出了位置是否在圆圈内,但是如果圆圈被击中那么我又需要拖动它,因为我怎么能绑定使用mousemove
事件,以便我可以说我想拖动圆圈而不是像我的代码在drag()
中所说的那样绘制它?
我的第二个问题:我猜我的代码没有处理双击事件,而是删除了一个清除画布的圆圈。我想删除画布并使用shapes
数组重绘整个画布。
答案 0 :(得分:1)
我没有想到使用你的想法来解决这个项目,但我有一些可能对你有帮助的评论。
1:如果您想要与放置的形状进行交互,我建议不要使用画布,主要是因为画布在绘图时效率很高,所以它只保存像素信息。您也可以尝试使用SVG甚至新的HTML 5功能来拖放元素(http://www.w3schools.com/html/html5_draganddrop.asp)。但是,如果您希望继续使用画布和 javascript ,您应该尝试使用 onmousedown 而不是 onclick 来触发拖动功能,因为订单那些事件是onmousedown,onmouseup然后onclick(http://www.w3schools.com/tags/ev_onmousedown.asp)。
2:也许您可以制作动画循环,也就是说,您在固定时间绘制并清除整个画布,这样您就可以在“渲染器”数组中绘制每个形状,如果您希望删除一个形状,只需将其从数组中删除即可。 另外,我没有找到双击的事件监听器,你是否尝试将 ondblclick =“myFunction()”附加到画布上? (http://www.w3schools.com/jsref/event_ondblclick.asp)
答案 1 :(得分:1)
不是让一个mousemove
处理程序试图处理所有可能的操作,而是拥有专门的mousemove
处理程序是有意义的。
制作一个圆圈:在做任何事情之前拍摄画布的快照,然后附上一个mousemove
处理程序,用于恢复快照并在顶部绘制一个所需大小的新圆圈。
拖动圆圈:擦除画布,绘制除要拖动的圆圈以外的所有圆圈,然后拍摄快照。现在附上一个mousemove
处理程序,用于恢复快照并将圆圈绘制在新位置。
在每种情况下,您还应该分配一个mouseup
处理程序,在拖动操作结束时将mousemove
处理程序和mouseup
处理程序本身分开。
至于双击问题,你遇到了麻烦,因为你没有正确地擦除形状。您的函数以几个shapes.pop()
调用开始,因此从数组中删除最后两个形状,然后您的eraseCanvas
函数丢弃整个数组。擦除画布时,请勿篡改形状阵列。
要从数组中间删除位置i
的形状,请将所有数组元素向下移动一步,然后将最后一个元素弹出结尾:
for (var j = i + 1; j < shapes.length; ++j) {
shapes[j - 1] = shapes[j];
}
shapes.pop();
完成后,您可以擦除画布并绘制阵列中剩余的所有形状。
我已在以下代码段中实施了这些更正。
var canvas,
context, shapes,
dragging = false, draggingtoMove = false,dragstopped = 0,
dragStartLocation,dragEndLocation,
snapshot;
var numShapes;
function initiate() {
numShapes = 100;
shapes = [];
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.addEventListener('mousedown', click, false);
canvas.addEventListener('dblclick', dblclickerase);
}
function dblclickerase(evt){
var coordinates = getCanvasCoordinates(event),
clickX = coordinates.x,
clickY = coordinates.y;
//Now find which shape was clicked
for (var i = 0; i < shapes.length; ++i) {
if (hitTest(shapes[i], clickX, clickY)) {
console.log('double-clicked on circle ' + i);
// Delete this shape from the array.
for (var j = i + 1; j < shapes.length; ++j) {
shapes[j - 1] = shapes[j];
}
shapes.pop();
// Redraw the other shapes.
eraseCanvas();
for (var j = 0; j < shapes.length; ++j) {
paintCircle(shapes[j]);
}
return;
}
}
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function draw(position) {
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
var i=0;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
tempRad = radius;
tempX = dragStartLocation.x;
tempY = dragStartLocation.y;
tempColor = getRndColor();
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor};
if (dragstopped) { shapes.push(tempShape); }
paintCircle(tempShape);
i++;
}
function click(event) {
// Here I will check whether if circle overlaps then drag the circle else draw new one.
var i, j;
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var coordinates = getCanvasCoordinates(event),
clickX = coordinates.x,
clickY = coordinates.y;
//Now find which shape was clicked
for (i=0; i < shapes.length; i++) {
var shape = shapes[i];
if (hitTest(shape, clickX, clickY)) {
console.log('clicked on circle ' + i);
// Erase this circle and take a snapshot.
eraseCanvas();
for (var j = 0; j < shapes.length; ++j) {
if (j != i) {
paintCircle(shapes[j]);
}
}
takeSnapshot();
paintCircle(shape);
var originalX = shape.x,
originalY = shape.y;
canvas.onmousemove = function (dragEvent) {
var dragCoordinates = getCanvasCoordinates(dragEvent),
dx = dragCoordinates.x - clickX,
dy = dragCoordinates.y - clickY;
shape.x = originalX + dx;
shape.y = originalY + dy;
restoreSnapshot();
paintCircle(shape);
};
canvas.onmouseup = function (upEvent) {
canvas.onmousemove = undefined;
canvas.onmouseup = undefined;
};
return;
}
}
//Draw Circle
dragging = true;
dragstopped = 0;
dragStartLocation = getCanvasCoordinates(event);
takeSnapshot();
canvas.onmousemove = makeCircle;
canvas.onmouseup = finishCircle;
}
function hitTest(shape,mx,my) {
var dx;
var dy;
dx = Math.abs(mx - shape.x);
dy = Math.abs(my - shape.y);
//if it is inside any circle radius will let us know
return (Math.sqrt(dx*dx + dy*dy) < shape.rad);
}
function paintCircle(shape) {
context.fillStyle = shape.color;
context.beginPath();
context.arc(shape.x, shape.y, shape.rad, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
function makeCircle(event) {
dragstopped = 0;
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
draw(position);
}
}
function finishCircle(event) {
canvas.onmousemove = undefined;
canvas.onmouseup = undefined;
dragstopped += 1;
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
dragEndLocation = getCanvasCoordinates(event);
draw(position);
}
function getRndColor() {
var r = 255 * Math.random() | 0,
g = 255 * Math.random() | 0,
b = 255 * Math.random() | 0;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
addEventListener("load",initiate);
&#13;
body {
}
canvas{
border: 1px solid gray;
}
button {
font-size: 128%;
margin-right: -12px;
position: absolute;
}
&#13;
<canvas id="canvas" width="600" height="400"></canvas>
&#13;