Поиск…
Как фигуры и изображения ДЕЙСТВИТЕЛЬНО (!) «Перемещаются» на холсте
Проблема: Canvas только запоминает пиксели, а не фигуры или изображения
Это изображение кругового пляжного мяча, и, конечно же, вы не можете перетащить мяч вокруг изображения.
Это может удивить вас так же, как изображение, если вы нарисуете круг на холсте, вы не можете перетащить этот круг вокруг холста. Это потому, что холст не будет помнить, где он нарисовал круг.
// this arc (==circle) is not draggable!!
context.beginPath();
context.arc(20, 30, 15, 0, Math.PI*2);
context.fillStyle='blue';
context.fill();
Что Холст НЕ знает ...
- ... где вы нарисовали круг (он не знает x, y = [20,30]).
- ... размер круга (он не знает радиуса = 15).
- ... цвет круга. (он не знает, что круг синий).
Что знает Холст ...
Холст знает цвет каждого пикселя на поверхности рисования.
Холст может сказать вам, что в x, y == [20,30] есть синий пиксель, но он не знает, является ли этот синий пиксель частью круга.
Что это значит...
Это означает, что все, нарисованное на холсте, является постоянным: недвижимым и неизменным.
- Холст не может перемещать круг или изменять размер круга.
- Холст не может перекрасить круг или стереть круг.
- Холст не может сказать, висит ли мышь над кругом.
- Холст не может сказать, сталкивается ли круг с другим кругом.
- Canvas не может позволить пользователю перетащить круг вокруг холста.
Но Холст может дать ИЛЛЮЗИЮ движения
Холст может дать иллюзию движения , непрерывно стирая круг и перерисовывая его в другом положении. Перерисовывая холст много раз в секунду, глаз обманывается, когда круг перемещается по холсту.
Стереть холст
Обновить положение круга
Перерисовать круг в новом положении
Повторите, повторите, повторите ...
Этот код дает иллюзию движения , непрерывно перерисовывая круг в новых позициях.
// create a canvas
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.fillStyle='red';
document.body.appendChild(canvas);
// a variable indicating a circle's X position
var circleX=20;
// start animating the circle across the canvas
// by continuously erasing & redrawing the circle
// in new positions
requestAnimationFrame(animate);
function animate(){
// update the X position of the circle
circleX++;
// redraw the circle in it's new position
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc( circleX, 30,15,0,Math.PI*2 );
ctx.fill();
// request another animate() loop
requestAnimationFrame(animate);
}
Перетаскивание кругов и прямоугольников вокруг холста
Что такое «Форма»?
Вы обычно сохраняете свои фигуры, создавая объект «формы» JavaScript, представляющий каждую фигуру.
var myCircle = { x:30, y:20, radius:15 };
Конечно, вы не сохраняете формы. Вместо этого вы сохраняете определение того, как рисовать фигуры.
Затем поместите каждый объект формы в массив для удобства.
// save relevant information about shapes drawn on the canvas
var shapes=[];
// define one circle and save it in the shapes[] array
shapes.push( {x:10, y:20, radius:15, fillcolor:'blue'} );
// define one rectangle and save it in the shapes[] array
shapes.push( {x:10, y:100, width:50, height:35, fillcolor:'red'} );
Использование событий мыши для перетаскивания
Перетаскивание фигуры или изображения требует ответа на эти события мыши:
На mousedown:
Проверьте, находится ли какая-либо фигура под мышью. Если фигура находится под мышью, пользователь намерен перетащить эту фигуру. Поэтому сохраните ссылку на эту фигуру и установите флаг true / false isDragging
указывающий, что выполняется перетаскивание.
На mousemove:
Вычислите расстояние, которое мышь вытащила с момента последнего события mousemove
и измените положение перетаскиваемой фигуры на это расстояние. Чтобы изменить положение фигуры, вы изменяете свойства позиции x,y
в объекте этой фигуры.
В режиме мыши или мыши:
Пользователь намерен остановить операцию перетаскивания, поэтому очистите флаг «isDragging». Перетаскивание завершено.
Демонстрация: перетаскивание кругов и прямоугольников на холст
Эта демонстрация тянет круги и прямоугольники на холсте, реагируя на события мыши и создавая иллюзию движения путем очистки и перерисовки.
// canvas related vars
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
document.body.appendChild(canvas);
canvas.style.border='1px solid red';
// used to calc canvas position relative to window
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
canvas.onresize=function(e){ reOffset(); }
// save relevant information about shapes drawn on the canvas
var shapes=[];
// define one circle and save it in the shapes[] array
shapes.push( {x:30, y:30, radius:15, color:'blue'} );
// define one rectangle and save it in the shapes[] array
shapes.push( {x:100, y:-1, width:75, height:35, color:'red'} );
// drag related vars
var isDragging=false;
var startX,startY;
// hold the index of the shape being dragged (if any)
var selectedShapeIndex;
// draw the shapes on the canvas
drawAll();
// listen for mouse events
canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
// given mouse X & Y (mx & my) and shape object
// return true/false whether mouse is inside the shape
function isMouseInShape(mx,my,shape){
if(shape.radius){
// this is a circle
var dx=mx-shape.x;
var dy=my-shape.y;
// math test to see if mouse is inside circle
if(dx*dx+dy*dy<shape.radius*shape.radius){
// yes, mouse is inside this circle
return(true);
}
}else if(shape.width){
// this is a rectangle
var rLeft=shape.x;
var rRight=shape.x+shape.width;
var rTop=shape.y;
var rBott=shape.y+shape.height;
// math test to see if mouse is inside rectangle
if( mx>rLeft && mx<rRight && my>rTop && my<rBott){
return(true);
}
}
// the mouse isn't in any of the shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
// clear the canvas and
// redraw all shapes in their current positions
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
if(shape.radius){
// it's a circle
ctx.beginPath();
ctx.arc(shape.x,shape.y,shape.radius,0,Math.PI*2);
ctx.fillStyle=shape.color;
ctx.fill();
}else if(shape.width){
// it's a rectangle
ctx.fillStyle=shape.color;
ctx.fillRect(shape.x,shape.y,shape.width,shape.height);
}
}
}
Перетаскивание нерегулярных фигур вокруг холста
Большинство рисунков холста являются прямоугольными (прямоугольники, изображения, текстовые блоки) или круговые (круги).
Круги и прямоугольники имеют математические тесты, чтобы проверить, находится ли мышь внутри них. Это делает тестирование кругов и прямоугольников простым, быстрым и эффективным. Вы можете «опробовать» сотни кругов или прямоугольников за долю секунды.
Вы также можете перетаскивать нерегулярные фигуры. Но нерегулярные формы не имеют быстрого математического теста. К счастью, нерегулярные фигуры имеют встроенный хит-тест, чтобы определить, находится ли точка (мышь) внутри формы: context.isPointInPath
. Хотя isPointInPath
работает хорошо, он не так эффективен, как чисто математический хит-тестов - он часто до 10 раз медленнее, чем чистые математические тесты.
Одним из требований при использовании isPointInPath
является то, что вы должны «переопределить» проверяемый путь непосредственно перед вызовом isPointInPath
. «Переопределить» означает, что вы должны выдать команды рисования пути (как указано выше), но вам не нужно обходить () или заполнять () путь перед его тестированием с помощью isPointInPath
. Таким образом, вы можете протестировать ранее нарисованные пути, не перезаписывая (инсультировать / заполнять) те предыдущие пути на самом холсте.
Неправильная форма не обязательно должна быть такой же общей, как обычный треугольник. Вы также можете опробовать любые дико нерегулярные пути.
Этот аннотированный пример показывает, как перетаскивать нерегулярные формы пути, а также круги и прямоугольники:
// canvas related vars
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
document.body.appendChild(canvas);
canvas.style.border='1px solid red';
// used to calc canvas position relative to window
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
canvas.onresize=function(e){ reOffset(); }
// save relevant information about shapes drawn on the canvas
var shapes=[];
// define one circle and save it in the shapes[] array
shapes.push( {x:20, y:20, radius:15, color:'blue'} );
// define one rectangle and save it in the shapes[] array
shapes.push( {x:100, y:-1, width:75, height:35, color:'red'} );
// define one triangle path and save it in the shapes[] array
shapes.push( {x:0, y:0, points:[{x:50,y:30},{x:75,y:60},{x:25,y:60}],color:'green'} );
// drag related vars
var isDragging=false;
var startX,startY;
// hold the index of the shape being dragged (if any)
var selectedShapeIndex;
// draw the shapes on the canvas
drawAll();
// listen for mouse events
canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
// given mouse X & Y (mx & my) and shape object
// return true/false whether mouse is inside the shape
function isMouseInShape(mx,my,shape){
if(shape.radius){
// this is a circle
var dx=mx-shape.x;
var dy=my-shape.y;
// math test to see if mouse is inside circle
if(dx*dx+dy*dy<shape.radius*shape.radius){
// yes, mouse is inside this circle
return(true);
}
}else if(shape.width){
// this is a rectangle
var rLeft=shape.x;
var rRight=shape.x+shape.width;
var rTop=shape.y;
var rBott=shape.y+shape.height;
// math test to see if mouse is inside rectangle
if( mx>rLeft && mx<rRight && my>rTop && my<rBott){
return(true);
}
}else if(shape.points){
// this is a polyline path
// First redefine the path again (no need to stroke/fill!)
defineIrregularPath(shape);
// Then hit-test with isPointInPath
if(ctx.isPointInPath(mx,my)){
return(true);
}
}
// the mouse isn't in any of the shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
// clear the canvas and
// redraw all shapes in their current positions
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
if(shape.radius){
// it's a circle
ctx.beginPath();
ctx.arc(shape.x,shape.y,shape.radius,0,Math.PI*2);
ctx.fillStyle=shape.color;
ctx.fill();
}else if(shape.width){
// it's a rectangle
ctx.fillStyle=shape.color;
ctx.fillRect(shape.x,shape.y,shape.width,shape.height);
}else if(shape.points){
// its a polyline path
defineIrregularPath(shape);
ctx.fillStyle=shape.color;
ctx.fill();
}
}
}
function defineIrregularPath(shape){
var points=shape.points;
ctx.beginPath();
ctx.moveTo(shape.x+points[0].x,shape.y+points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(shape.x+points[i].x,shape.y+points[i].y);
}
ctx.closePath();
}
Перетаскивание изображений вокруг холста
См. Этот пример для общего объяснения перетаскивания фигур вокруг холста.
Этот аннотированный пример показывает, как перетаскивать изображения вокруг холста
// canvas related vars
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
canvas.width=378;
canvas.height=378;
var cw=canvas.width;
var ch=canvas.height;
document.body.appendChild(canvas);
canvas.style.border='1px solid red';
// used to calc canvas position relative to window
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
canvas.onresize=function(e){ reOffset(); }
// save relevant information about shapes drawn on the canvas
var shapes=[];
// drag related vars
var isDragging=false;
var startX,startY;
// hold the index of the shape being dragged (if any)
var selectedShapeIndex;
// load the image
var card=new Image();
card.onload=function(){
// define one image and save it in the shapes[] array
shapes.push( {x:30, y:10, width:127, height:150, image:card} );
// draw the shapes on the canvas
drawAll();
// listen for mouse events
canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
};
// put your image src here!
card.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/card.png';
// given mouse X & Y (mx & my) and shape object
// return true/false whether mouse is inside the shape
function isMouseInShape(mx,my,shape){
// is this shape an image?
if(shape.image){
// this is a rectangle
var rLeft=shape.x;
var rRight=shape.x+shape.width;
var rTop=shape.y;
var rBott=shape.y+shape.height;
// math test to see if mouse is inside image
if( mx>rLeft && mx<rRight && my>rTop && my<rBott){
return(true);
}
}
// the mouse isn't in any of this shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
// clear the canvas and
// redraw all shapes in their current positions
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
if(shape.image){
// it's an image
ctx.drawImage(shape.image,shape.x,shape.y);
}
}
}