• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] Problem mit Array-push in Canvas script

centauro

New member
Ich bin gerade dabei mich in Canvas einzuarbeiten und habe ein kleines Problem in meinem Canvas-Script beim "Undo" von Objekten (Pfeile, Kreise, Quadrate...), die ich auf ein Foto im Canvas setze. Bei dem Script handelt es sich um das bekannte "CanvasPaint.js" Script, welches ich aus dem Internet gezogen habe.

Ausgangssituation:
Wenn ich z.B. vier Pfeile auf das Foto setze, dann werden diese Pfeile nacheinander in das Array hineingeschrieben.

Bei einem "UNDO", also das "Rückgängig machen der letzten Aktionen" soll rückwärts der Reihenfolge nach wieder diese Objekte aus dem Array entfernt werden aber auch vom Canvas verschwinden.

Problem:
Das funktioniert leider nur teilweise. Das Script löscht wahllos array-nodes, also ohne die Reihenfolge zu berücksichtigen, wobei sich ein node nicht löschen lässt.

Code:
Hier das Script. Ich würde mich sehr freuen, wenn mir jemand helfen würde den Fehler zu beheben.

HTML

HTML:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

	<title>Foto speichern</title>

	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
	
	<script src="libs/export_canvas/createjs-2013.09.25.min.js"></script> 
	<script src="libs/export_canvas/jquery-2.0.3.js"></script>

	<script src="libs/export_canvas/base64.js" type="text/javascript"></script>
	<script src="libs/export_canvas/canvas2image.js" type="text/javascript"></script>
	
	<script type="text/javascript" src="libs/export_canvas/canvasPaint.js"></script>
    <style type="text/css">
      #container { position: relative; }
      #canvas { border: 1px solid #000; }
      #imageTemp { position: absolute; top: 1px; left: 1px; }
      
 </style>
</head>
<body onload="init()" bgcolor="#3399CC">

 <table border="0">
 	 	<tr>	
		<label>Markierungswerkzeug: 
		<select id="dtool">
        	<option value="line">Pfeil</option>
        	<option value="circ">Kreis</option>
        	<option value="rect">Viereck</option>
        	<option value="pencil">Stift</option>
    	</select></label>
 	 		</td>	
 	</tr>
 	 		</td>
 	 		<td>
 	 	<!--<button onclick="javascript:init();return false;">Neu beginnen</button>-->
 	 	<button onclick="javascript:location.href=location.href">Neu beginnen</button>
        Linienstärke : <select id="selWidth">
            <option value="1">1</option>
            <option value="3">3</option>
            <option value="5">5</option>
           <!-- <option value="7">7</option>
            <option value="9" selected="selected">9</option>
            <option value="11">11</option>-->
        </select> 
        Farbe : <select id="selColor">
            <option value="black">Schwarz</option>
            <option value="blue" selected="selected">Blau</option>
            <option value="red">Rot</option>
            <option value="green">Grün</option>
            <option value="yellow">Gelb</option>
            <option value="gray">Grau</option>
        </select>      
        <button onclick="javascript:cUndo();return false;">Rückgängig</button>
        <button onclick="javascript:cRedo();return false;">Redo</button>
 	 			
 	 		</td>	
 	</tr>
 	<tr><td align="right">
 	<div id="container">
	<canvas  width="800" height="600" id="canvas"></canvas>
	</div>
 	</td>
 	</tr>
 </table>

CanvasPaint.js
Code:
// Keep everything in anonymous function, called on window load.  
if(window.addEventListener) {
window.addEventListener('load', function () {
  var canvas, context, canvaso, contexto;

  // The active tool instance.
  var tool;
  var tool_default = 'line';
  var cPushArray = new Array();
  var cStep = -1;
  var mousePressed=false;
  
  function init () {
    // Find the canvas element.
    canvaso = document.getElementById('canvas');
    if (!canvaso) {
      alert('Fehler: Es konnte kein Canvas-Element gefunden werden.<br>Bitte wenden Sie sich an Ihren Anwendungsadministrator!');
      return;
    }

    if (!canvaso.getContext) {
      alert('Fehler: no canvas.getContext!');
      return;
    }

    // Get the 2D canvas context.
    contexto = canvaso.getContext('2d');
    if (!contexto) {
      alert('Fehler: failed to getContext!');
      return;
    }

    // Add the temporary canvas.
    var container = canvaso.parentNode;
    canvas = document.createElement('canvas');
    if (!canvas) {
      alert('Fehler: Es konnte kein neues Canvas-Element erzeugt werden.<br>Bitte wenden Sie sich an Ihren Anwendungsadministrator!');
      return;
    }

    canvas.id     = 'imageTemp';
    canvas.width  = canvaso.width;
    canvas.height = canvaso.height;
    container.appendChild(canvas);

    context = canvas.getContext('2d');

    // Get the tool select input.
    var tool_select = document.getElementById('dtool');
    if (!tool_select) {
      alert('Es ist ein Fehler bei der Wahl des dtool-Elements aufgetreten.<br>Bitte wenden Sie sich an Ihren Anwendungsadministrator!');
      return;
    }
    tool_select.addEventListener('change', ev_tool_change, false);

    // Activate the default tool.
    if (tools[tool_default]) {
      tool = new tools[tool_default]();
      tool_select.value = tool_default;
    }
   
    
    // Attach the mousedown, mousemove and mouseup event listeners.
    canvas.addEventListener('mousedown', ev_canvas, false);
    canvas.addEventListener('mousemove', ev_canvas, false);
    canvas.addEventListener('mouseup',   ev_canvas, false);
  }

  // The general-purpose event handler. This function just determines the mouse 
  // position relative to the canvas element.
  function ev_canvas (ev) {
    if (ev.layerX || ev.layerX == 0) { // Firefox
      ev._x = ev.layerX;
      ev._y = ev.layerY;
    } else if (ev.offsetX || ev.offsetX == 0) { // Opera
      ev._x = ev.offsetX;
      ev._y = ev.offsetY;
    }

    // Call the event handler of the tool.
    var func = tool[ev.type];
    if (func) {
      func(ev);
    }
  }

  // The event handler for any changes made to the tool selector.
  function ev_tool_change (ev) {
    if (tools[this.value]) {
      tool = new tools[this.value]();
    }
  }
  
  // This function draws the #imageTemp canvas on top of #imageView, after which 
  // #imageTemp is cleared. This function is called each time when the user 
  // completes a drawing operation.
  function img_update () {
		contexto.drawImage(canvas, 0, 0);
		context.clearRect(0, 0, canvas.width, canvas.height);
		cPush();
  }

  // This object holds the implementation of each drawing tool.
  var tools = {};

  // The line tool.
  tools.line = function () {
    var tool = this;
    this.started = false;

    this.mousedown = function (ev) {
      tool.started = true;
      tool.x0 = ev._x;
      tool.y0 = ev._y;
      img_update();
    };

    this.mousemove = function (ev) {
      if (!tool.started) {
        return;
      }

      context.clearRect(0, 0, canvas.width, canvas.height);

      context.beginPath();
      context.moveTo(tool.x0, tool.y0);
      context.lineTo(ev._x,   ev._y);
      context.stroke();
      context.strokeStyle =  $('#selColor').val();
      context.lineWidth = $('#selWidth').val();
      
      // draw the ending arrowhead
      var endRadians=Math.atan((ev._y-tool.y0)/(ev._x-tool.x0));
      endRadians+=((ev._x>tool.x0)?90:-90)*Math.PI/180;
      tool.drawArrowhead(context,ev._x,ev._y,endRadians);
      
      context.closePath();
    };
    
        tool.drawArrowhead=function(context,_x,_y,radians){
            context.save();
            context.beginPath();
            context.translate(_x,_y);
            context.rotate(radians);
            context.moveTo(0,0);
            context.lineTo(5,20);
            context.lineTo(-5,20);
            context.closePath();
            context.restore();
            context.fill();
            context.fillStyle   =   $('#selColor').val(); 
            //context.lineWidth 	= 	$('#selWidth').val();
        }
        // end drawing ending arrowhead

    this.mouseup = function (ev) {
      if (tool.started) {
        tool.mousemove(ev);
        tool.started = false;
      }
    };
    };

ctx = document.getElementById('canvas').getContext("2d");

//function cPush() {
this.cPush = function (ev) {	
	cStep++;
    if (cStep < cPushArray.length) { cPushArray.length = cStep;}
    cPushArray.push(document.getElementById('canvas').toDataURL());
    document.title = cStep + ":" + cPushArray.length;
   
}

this.cUndo = function (ev) {
    if (cStep > 0) {
        cStep--;
        var canvasPic = new Image();
        canvasPic.src = cPushArray[cStep];
        canvasPic.onload = function () { ctx.drawImage(canvasPic, 0, 0); }
        document.title = cStep + ":" + cPushArray.length;
        //alert(cPushArray.length);
    }

}

  init();

}, false);}
 
Hi,
in this.cUndo wird überhaut nix gelöscht du gehst mit den Index ein schritt zurück aber direkt löschen tust Du nix.
Ändre mal folgendes um:
var cPushArray = new Array();, der Array Konstruktor ist mist weg damit
So ist's besser:
var cPushArray = [];
Setz cStep mal auf 0
var cStep = 0;
In die DataURL würde ich noch image/png packen:
cPushArray.push(document.getElementById('canvas').toDataURL("image/png"));
Diese Zeile kannst Du löschen:
if (cStep < cPushArray.length) { cPushArray.length = cStep;}

Zum löschen von Array Elmenten benötigst du die Pop Methode.
canvasPic.src = cPushArray.pop();



Ps: Hoffe es ist kein Problem das ich keine Code Tags benutzt habe für die paar Zeilen.

PPS.: onload="init()" wird auch nicht erreicht

Hoffe das Hilft.
 
Zuletzt bearbeitet von einem Moderator:
Hey,
besten Dank für Deine Hilfe.

Noch eine Frage.

Ich möchte gerne die Pfeile und die anderen Objekte über den canvas bewegen. Gibt es diese Möglichkeit, z.B. mit drag and drop?

Vielen Dank im voraus.

Viele Grüße
 
Hoffe es ist kein Problem das ich keine Code Tags benutzt habe für die paar Zeilen.
Im Grund nicht, ne - da es die Übersichtlichkeit in diesem Fall nicht weiter stört. Dennoch täte ich dir empfehlen, für genau solche Situationen die -Tags zu benutzen, mit denen man Code auf den ersten Blick als solchen erkennbar macht.
 
Eine weitere Frage bitte:

Ich möchte gerne die Farben, die zurzeit über eine dropdown-box selektiert werden können, tabellarisch darstellen und selektieren können.
Das heißt, dass ich eine Tabelle machen möchte, in der jede Zelle eine der Farben aus dem dropdown enthält.
Beim Anklicken einer der Farben soll die id="selColor" an die entsprechende Funktion übergeben werden.

So ungefähr:

Vorher:
HTML:
        Farbe : <select id="selColor">
            <option value="black">Schwarz</option>
            <option value="blue" selected="selected">Blau</option>
            <option value="red">Rot</option>
            <option value="green">Grün</option>
            <option value="yellow">Gelb</option>
            <option value="gray">Grau</option>
        </select>

Nachher:

HTML:
<html>
<body>
<table>
   <tr>
      <td>schwarz</td><td>blau</td><td>rot</td>
   </tr>
   <tr>
      <td>grün</td><td>gelb</td><td>grau</td>
   </tr>
</body>
</html>

Wenn ich in den jeweiligen <td>-Tag die id setze, dann funktioniert es leider nicht.
Wie mache ich das am besten?
Hat jemand Rat?

Besten Dank im Voraus.
 
Ich versteh dich jetzt leider nicht. Kannst du mal den "nicht funktionierenden" Code zeigen?

PS: für ein Farbwahl-Menü bietet sich input type="color" an.
 
gerne.

Ich möchte den selektierten Farbcode an eine Canvas-Funktion senden.

Der HTML-Code:
HTML:
    <table width="230px" height="10px" border="0">
   <tr>
      <td width="55px" height="10"><b>Farbe:</b></td>
      <td bgcolor="black" align="center">
      	<input type="radio" name="selColor" id="selColor" value="black">
      </td>
      <td bgcolor="blue" align="center">
      	<input type="radio" name="selColor" id="selColor" value="blue">
      </td>
      <td bgcolor="red" align="center">
      	<input type="radio" name="selColor" id="selColor" value="red">
      </td>
      <td bgcolor="green" align="center">
      <input type="radio" name="selColor" id="selColor" value="green">
     </td>
     <td bgcolor="yellow" align="center">
     	<input type="radio" name="selColor" id="selColor" value="yellow">
     </td>
     <td bgcolor="white" align="center">
     	<input type="radio" name="selColor" id="selColor" value="white">
     </td>
   </tr>
   </table>


Der js-code:
Code:
  // The line tool.
  tools.line = function () {
    var tool = this;
    this.started = false;

    this.mousedown = function (ev) {
      tool.started = true;
      tool.x0 = ev._x;
      tool.y0 = ev._y;
      img_update();
    };

    this.mousemove = function (ev) {
      if (!tool.started) {
        return;
      }

      context.clearRect(0, 0, canvas.width, canvas.height);

      context.beginPath();
      context.moveTo(tool.x0, tool.y0);
      context.lineTo(ev._x,   ev._y);
      context.stroke();
      context.strokeStyle =  $('#selColor').val();
      context.lineWidth = $('#selWidth').val();
      
      // draw the ending arrowhead
      var endRadians=Math.atan((ev._y-tool.y0)/(ev._x-tool.x0));
      endRadians+=((ev._x>tool.x0)?90:-90)*Math.PI/180;
      tool.drawArrowhead(context,ev._x,ev._y,endRadians);
      
      context.closePath();
    };
    
        tool.drawArrowhead=function(context,_x,_y,radians){
            context.save();
            context.beginPath();
            context.translate(_x,_y);
            context.rotate(radians);
            context.moveTo(0,0);
            context.lineTo(5,20);
            context.lineTo(-5,20);
            context.closePath();
            context.restore();
            context.fill();
            context.fillStyle   =   $('#selColor').val(); 
            //context.lineWidth 	= 	$('#selWidth').val();
        }
        // end drawing ending arrowhead

    this.mouseup = function (ev) {
      if (tool.started) {
        tool.mousemove(ev);
        tool.started = false;
        img_update();
      }
    };
    };

Wie kriege ich es zustande, dass der selektierte Farbcode die hier gezeigte Funktion erreicht.

Zu berücksichtigen ist hier, dass ich noch weitere Funktionen habe, die mit der Variablen "dtool" angesprochen werden können.
Bei den Funktionen handelt es sich um welche, mit denen ich "Pfeile", "Vierecke", "Kreise" und auch freizeichnen kann.

Mit dem folgenden Selektionsverfahren funktioniert es einwandfrei:

HTML:
<select id="selColor">
            <option value="black" style="background: black; color: white;">Schwarz</option>
            <option value="blue" style="background: blue; color: white;" selected="selected">Blau</option>
            <option value="red" style="background: red; color: white;">Rot</option>
            <option value="green" style="background: green; color: white;">Grün</option>
            <option value="yellow" style="background: yellow; color: black;">Gelb</option>
            <option value="white" style="background: white; color: black;">Weiss</option>
      </select>

Diese gefällt mir aber nicht so, sodass ich gerne auf Radiobuttons umsteigen möchte.
Nur wie mache ich es richtig?

Vielen Dank für jegliche Hilfestellung.
 
Jau....ich hab's schon.

Das klappt super mit input type="color" id="selColor".

Besten Dank!

- - - Aktualisiert - - -

Fehlt nur noch das ich die auf den Canvas gelegten Elemente wie "Pfeile", "Vierecke" .... verschieben kann.
Dann wäre ich glaube ich mit meiner Wunschliste durch. :)
 
centauro schrieb:
Fehlt nur noch das ich die auf den Canvas gelegten Elemente wie "Pfeile", "Vierecke" .... verschieben kann.
Ai kramba, das wird schwierig, zumiest mit den bisherigen Algorithmus.

Grund: Du arbeitest immer nur mit einem Canvas, dein Pfeil wird ja auf das eine Canvas gerdert und ist dann halt ein Teil von den Canvas (kling komisch ist aber so) Beim rückgängig machen stellst Du ja nur das vorherige canvas wider her.
Zum verschieben müsste man Layern.
Am einfachsten geht das wenn man einen Pfeil als eigenständiges canvas hat, dieses "Pfeil canvas" rendert man dann auf das DOM sichtbare canvas. Das Pfeil canvas wird mit dem Koordinaten in einem Array abspeichert oder ein Objekt Literal. Dann braucht's eine Methode die abgleicht ob Cursor über "Pfeil canvas", bei onklick/onmove fährt eine weitere Methode los die das angeklickte Pfeil canvas verschiebt zusätzlich muss der DOM canvas context auch mit aktualisiert werden, da sonst kein sichtbarer Effekt.

Für sowas gibt es eine lib, nennt sich kineticJS:
fiddle: Edit fiddle - JSFiddle
oder:
HTML5 Canvas Drag and Drop

Ob sich das nun mit deinem Code verträgt weiß ich nicht. Habe sowas auch noch nicht gemacht.
Schwierig ist es alle mal.

VG,
Xorg1990
 
centauro schrieb:
Mit kinetic müsste das doch anhand des folgenden Beispiels doch einfach sein, oder?
Du möchtest doch jeden Pfeil einzeln verschieben oder? Dann musst du eine Möglichkeit schaffen das jeder Pfeil ein eigens Objekt ist, denn nur die kannst du verschieben. So wie dein Skript aktuell ist wider jeder Pfeil auf ein canvas gerendert klickst Du nun ein Pfeil an würde sich das komplette canvas verschieben mit allen Pfeilen. Du müsstest an deinem Skript einige umbau arbeiten vornehmen, damit dein wunsch in Erfüllung gehen soll mit oder ohne KineticJS.


Nehmen wir an Du hast ein Pfeil als eigens canvas Objekt auf das dom/main canvas gereedert.
Dann brauchst du ein array , das füllst du mit der x,y Koordinate und mit der länge und höhe.
Pfeile.push(x + ";" + y + ";" + PfeilWidth + ";" + PfeilHeight + ";" + href);
In den DOM/main canvas machst du ein Maus event handler.

DOMcanvas.addEventListener("click", on_click, false);

Funktion on_click schaut dann so aus:
Code:
function on_click (ev) {
            var x, y;

            // Get the mouse position relative to the canvas element
            if (ev.layerX || ev.layerX == 0) { // For Firefox
                x = ev.layerX;
                y = ev.layerY;
            }
            x-=canvas.offsetLeft;
            y-=canvas.offsetTop;

            // hover
            for (var i = Pfeile.length - 1; i >= 0; i--) {
                var params = [];

                // Get Pos params back from array
                params = Pfeile[i].split(";");

                var Pfeil_X = parseInt(params[0]),
                    Pfeil_Y = parseInt(params[1]),
                   Pfeil_Width = parseInt(params[2]),
                    Pfeil_Height = parseInt(params[3]);

                // Check if cursor is in the right area
                if (x >= Pfeil_X && x <= (Pfeil_X + Pfeil_Width) && y >=  Pfeil_Y && y <= (Pfeil_Y + Pfeil_Height )){
                    document.body.style.cursor = "pointer";
                    Zusätzlich noch eine Methode die das bewegen rendert
                }
                else {
                    document.body.style.cursor = "";
                }
            };
        }
Das soll nur als Fundament dienen, und ist mir auf die schnelle durch den Kopf gegangen.
Schau mal das an:
http://rectangleworld.com/blog/archives/15
 
Zurück
Oben