Zoeken…
Hoe vormen en afbeeldingen ECHT (!) "Bewegen" op het canvas
Een probleem: Canvas onthoudt alleen pixels, geen vormen of afbeeldingen
Dit is een afbeelding van een ronde strandbal en je kunt de bal natuurlijk niet rond de afbeelding slepen.
Het zal je misschien verbazen dat je, net als een afbeelding, die cirkel niet op een canvas kunt slepen. Dat komt omdat het canvas niet meer weet waar het de cirkel heeft getekend.
// this arc (==circle) is not draggable!!
context.beginPath();
context.arc(20, 30, 15, 0, Math.PI*2);
context.fillStyle='blue';
context.fill();
Wat het canvas niet weet ...
- ... waar je de cirkel hebt getekend (het weet niet x, y = [20,30]).
- ... de grootte van de cirkel (het weet geen straal = 15).
- ... de kleur van de cirkel. (het weet niet dat de cirkel blauw is).
Wat het canvas wel weet ...
Canvas kent de kleur van elke pixel op het tekenoppervlak.
Het canvas kan u vertellen dat op x, y == [20,30] er een blauwe pixel is, maar het weet niet of deze blauwe pixel deel uitmaakt van een cirkel.
Wat dit betekent...
Dit betekent dat alles wat op het canvas is getekend permanent is: onverplaatsbaar en onveranderlijk.
- Canvas kan de cirkel niet verplaatsen of het formaat van de cirkel wijzigen.
- Canvas kan de cirkel niet opnieuw kleuren of de cirkel wissen.
- Canvas kan niet zeggen of de muis over de cirkel zweeft.
- Canvas kan niet zeggen of de cirkel tegen een andere cirkel botst.
- Canvas kan een gebruiker de cirkel niet over het canvas laten slepen.
Maar Canvas kan de ILLUSIE van beweging geven
Canvas kan de illusie van beweging geven door de cirkel continu te wissen en opnieuw in een andere positie te tekenen. Door het canvas vele malen per seconde opnieuw te tekenen, wordt het oog voor de gek gehouden en ziet de cirkel over het canvas bewegen.
Wis het canvas
Werk de positie van de cirkel bij
Teken de cirkel opnieuw in zijn nieuwe positie
Herhaal, herhaal, herhaal ...
Deze code geeft de illusie van beweging door continu een cirkel in nieuwe posities te hertekenen.
// 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);
}
Cirkels en rechthoeken rond het canvas slepen
Wat is een "vorm"?
U slaat uw vormen meestal op door een JavaScript-vormobject te maken dat elke vorm vertegenwoordigt.
var myCircle = { x:30, y:20, radius:15 };
Natuurlijk bewaar je niet echt vormen. In plaats daarvan bewaart u de definitie voor het tekenen van de vormen.
Plaats vervolgens elk vormobject in een array voor eenvoudige referentie.
// 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'} );
Muisgebeurtenissen gebruiken om te slepen
Als u een vorm of afbeelding wilt slepen, moet u reageren op deze muisgebeurtenissen:
Op muizenval:
Test of er een vorm onder de muis zit. Als een vorm onder de muis zit, is de gebruiker van plan die vorm te slepen. Houd dus een verwijzing naar die vorm en stel een waar / onwaar isDragging
Vlag slepen om aan te geven dat er wordt isDragging
.
Op muismat:
Bereken de afstand die de muis is gesleept sinds de laatste mousemove
en wijzig de positie van de gesleepte vorm met die afstand. Als u de positie van de vorm wilt wijzigen, wijzigt u de eigenschappen x,y
in het object van die vorm.
Bij mouse-up of mouse-out:
De gebruiker is van plan de sleepbewerking te stoppen, dus verwijder de vlag "isDragging". Slepen is voltooid.
Demo: cirkels en rechthoeken op het canvas slepen
Deze demo sleept cirkels en rechthoeken op het canvas door te reageren op muisgebeurtenissen en de illusie van beweging te geven door te wissen en opnieuw te tekenen.
// 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);
}
}
}
Onregelmatige vormen over het canvas slepen
De meeste Canvas-tekeningen zijn rechthoekig (rechthoeken, afbeeldingen, tekstblokken) of cirkelvormig (cirkels).
Cirkels en rechthoeken worden wiskundig getest om te controleren of de muis erin zit. Dit maakt het testen van cirkels en rechthoeken eenvoudig, snel en efficiënt. Je kunt honderden cirkels of rechthoeken in een fractie van een seconde "raken".
U kunt ook onregelmatige vormen slepen. Maar onregelmatige vormen hebben geen snelle wiskundige hit-test. Gelukkig hebben onregelmatige vormen een ingebouwde hit-test om te bepalen of een punt (muis) zich in de vorm bevindt: context.isPointInPath
. Hoewel isPointInPath
goed werkt, is het lang niet zo efficiënt als puur wiskundige hit-tests - het is vaak tot 10x langzamer dan pure wiskundige hit-tests.
Een vereiste bij het gebruik van isPointInPath
is dat u het pad dat wordt getest moet "herdefiniëren" onmiddellijk voordat u isPointInPath
. "Opnieuw definiëren" betekent dat u de padtekeningopdrachten moet geven (zoals hierboven), maar u hoeft het pad niet te isPointInPath
() of te vullen () voordat u het test met isPointInPath
. Op deze manier kunt u eerder getekende paden testen zonder die eerdere paden op het canvas zelf te moeten overschrijven (lijn / vulling).
De onregelmatige vorm hoeft niet zo gewoon te zijn als de alledaagse driehoek. Je kunt ook wild onregelmatige paden testen.
Dit geannoteerde voorbeeld laat zien hoe u onregelmatige padvormen, cirkels en rechthoeken sleept:
// 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();
}
Afbeeldingen over het canvas slepen
Zie dit voorbeeld voor een algemene uitleg over het slepen van vormen over het canvas.
Dit geannoteerde voorbeeld laat zien hoe u afbeeldingen over het canvas sleept
// 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);
}
}
}