Странное поведение от RaphaelJS при попытке переместить SVG - PullRequest
1 голос
/ 04 ноября 2011

Я работаю над графической демонстрацией с использованием RaphaelJS и jQuery.SVG. Прямо сейчас у меня есть динамически созданная сетка прямоугольников 8x8, которую пользователи могут щелкнуть, чтобы выбрать. После того, как два из ритов выбраны, предполагается, что выбранные риты меняются местами друг с другом (как в Bejeweled).

Я дошел до того, что могу получить ректы для обмена, но они не попадают в правильные позиции. Согласно коду, я заменяю координаты x и y каждого выбранного прямоугольника в функции анимации. Ректы всегда заканчиваются тестированием. Визуальный эффект довольно аккуратный, но я действительно хочу, чтобы трюки были в курсе.

http://img407.imageshack.us/img407/2222/raphaelerror.jpg

http://imageshack.us/photo/my-images/593/raphaelerror2.jpg/

Строки отладки, которые я создаю, говорят мне, что положения ректов не изменились. Очевидно, что это не так ... что здесь происходит?

Вот соответствующий код для этой проблемы:

        // swapTiles
/*

Swaps two tiles on the board that have been set to 'selected' with an animation.    

*/
function swapTiles(toSwap) {

    var holdX = $(toSwap[0]).attr('x');
    var holdY = $(toSwap[0]).attr('y');

    // Pre-swap debugging
    statusString += "<br><b>BEFORE SWAP</b><br>";
    statusString += "<br>First cell: " + selectedCellNames[0];
    statusString += "<br>2nd cell: " + selectedCellNames[1];
    statusString += "<br>$(toSwap[0]).attr('x'): " + $(toSwap[0]).attr('x').toString();
    statusString += "<br>$(toSwap[0]).attr('y'): " + $(toSwap[0]).attr('y').toString();
    statusString += "<br>$(toSwap[1]).attr('x'): " + $(toSwap[1]).attr('x').toString();
    statusString += "<br>$(toSwap[1]).attr('y'): " + $(toSwap[1]).attr('y').toString();
    statusString += "<br>";

    /*
    This line is using jQuery SVG to grab the DOM elements. This is being used because trying to grab the elements through
    raphael was returning undefined errors. There seems to be some DOM scope issue that is causing me to use this and the temp.node 
    workarounds in order to reference SVG objects created through raphael.

    What did not work:

    $(toSwap[0]).animate({ x: 50, y: 100, 'stroke-width': 3 }, 250, '<'); --raphael animate function
    $(toSwap[0]).node.animate({ x: 50, y: 100, 'stroke-width': 3 }, 250, '<'); 

    When I tried to access the tiles using jQuery and the cellName user-defined attribute, I got '0' added to my debug output.        

    */
    $(toSwap[0]).animate({ svgX: $(toSwap[1]).attr('x'), svgY: $(toSwap[1]).attr('y'), svgStrokeWidth: 2, svgStroke: "#000" }, 250);
    $(toSwap[1]).animate({ svgX: holdX, svgY: holdY, svgStrokeWidth: 2, svgStroke: "#000" }, 250);

    // Post-swap debugging
    statusString += "<br><b>SWAP COMPLETE</b><br>";        
    statusString += "<br>$(toSwap[0]).attr('x'): " + $(toSwap[0]).attr('x').toString();
    statusString += "<br>$(toSwap[0]).attr('y'): " + $(toSwap[0]).attr('y').toString();
    statusString += "<br>$(toSwap[1]).attr('x'): " + $(toSwap[1]).attr('x').toString();
    statusString += "<br>$(toSwap[1]).attr('y'): " + $(toSwap[1]).attr('y').toString();
    statusString += "<br>";

}

// drawRect
/*

returns a Raphael rectangle object with the inputted options
color = the value that the color attr will be set to for this rect
x, y = x & y position of the top-left corner of the rect
w, h = width and height of the rect
corner = radius for the rounded corners

*/
function drawRect(color, x, y, w, h, corner) {

    var temp = canvas.rect(x, y, w, h, corner);
    temp.attr("fill", color);
    temp.node.setAttribute("selected", "false"); // Will be used to check for selected tiles in main loop    
    temp.node.setAttribute("cellName", cellName); // Give this rect a name

    // Add the mouseover/hover events
    temp.node.onmouseover = function () {

        temp.animate({ scale: 1.5, stroke: "#FFF", 'stroke-width': 3 }, 250, 'bounce').toFront();

    }; // end temp.node.onmouseover

    temp.node.onmouseout = function () {

        if (temp.node.getAttribute("selected") == "true") {

            temp.animate({ scale: 1, stroke: "#FFF", 'stroke-width': 4 }, 250, 'elastic').toFront();

        }
        else {

            temp.animate({ scale: 1, stroke: "#000", 'stroke-width': 2 }, 250, 'elastic').toBack();

        }

        // Update the status debugging
        statusString += "<br>(x: " + x.toString() + ", y: " + y.toString() + ") temp.node.getAttribute('selected') after .node.onmouseout = " + temp.node.getAttribute("selected");
        statusString += "<br>selectedCount = " + selectedCount.toString();
        statusString += "<br>cellName = " + temp.node.getAttribute("cellName");
        statusString += "<br>selectedCells[0] = " + selectedCells[0].toString();
        statusString += "<br>selectedCells[1] = " + selectedCells[1].toString();
        $("#status").html(statusString);
        statusString = "";

    }; // end temp.node.onmouseout

    // Toggle between selected / not-selected
    temp.node.onclick = function () {

        // if not selected yet 
        if (temp.node.getAttribute("selected") != "true") {

            temp.animate({ scale: 1, stroke: "#FFF", 'stroke-width': 4 }, 250, 'elastic').toFront();
            temp.node.setAttribute("selected", "true");
            selectedCount += 1;

            // Place the current cell into our array
            if (selectedCount == 1) {

                selectedCells[0] = temp.node;
                selectedCellNames[0] = temp.node.getAttribute("cellName");

            }
            // We have two cells selected: time to swap
            else if (selectedCount == 2) {

                selectedCells[1] = temp.node;
                selectedCellNames[1] = temp.node.getAttribute("cellName");

                swapTiles(selectedCells);

                // Clean up selectedCells and the selected attributes
                /*

                Since selectedCells already contains references to the temp.nodes of each SVG object, 
                use this format to set the attributes. No jQuery SVG call or additional .node is needed.

                */
                selectedCells[0].setAttribute("selected", "false");
                selectedCells[1].setAttribute("selected", "false");

                selectedCells[0] = "";
                selectedCells[1] = "";

                selectedCellNames[0] = "";
                selectedCellNames[1] = "";

                selectedCount = 0;

            }
            else {
                // Something went wrong
                statusString += "<br><b>ERROR: selectedCells out of range!</b>";
            }


        }
        // if already selected de-select
        else {

            temp.animate({ scale: 1, stroke: "#000", 'stroke-width': 2 }, 250, 'elastic').toBack();
            temp.node.setAttribute("selected", "false");
            selectedCount -= 1;

            // Remove the current cell name from our array
            if (selectedCount == 0) {

                selectedCells[0] = "";
                selectedCellNames[0] = "";

            }
            else if (selectedCount == 1) {

                selectedCells[1] = "";
                selectedCellNames[1] = "";

            }
            else {
                // Something went wrong
                statusString += "<br><b>ERROR: selectedCells out of range!</b>";
            }

        }

        // Update the status debugging
        statusString += "<br>(x: " + x.toString() + ", y: " + y.toString() + ") temp.node.getAttribute('selected') after .node.onclick = " + temp.node.getAttribute("selected");
        statusString += "<br>selectedCount = " + selectedCount.toString();
        statusString += "<br>selectedCells[0] = " + selectedCells[0].toString();
        statusString += "<br>selectedCells[1] = " + selectedCells[1].toString();
        $("#status").html(statusString);
        statusString = "";

    }; // end temp.node.onclick        

    return temp;

    /*

    NOTES:

    temp.node.[function] - the node raphael property lets me directly access the DOM element that I just created

    I will need to add all of my onclick and other direct access events for my tiles in this function.
    I will get errors if I try to reference the DOM elements from another part of this program, probably because it is only
    in this function that I actually create the DOM object. Once this function finishes, then the temp object reference is passed 
    back into my initialization loop, where at that point I cannot add any more DOM elements. 

    .animate - Raphael's animation function. Note that the properties that are animated are defined in the first argument,
    between a set of {}. The second argument is the time in milliseconds that the animation will take.

    .toFront() & .toBack() - These functions move the object to the top or bottom of the drawing stack. In other words, if I use 
    toFront on an object, it should be drawn on top of everything else, and vice-versa. This eliminates a graphical issue where the 
    tiles overrlap while expanded.

    .node.setAttribute / .getAttribute(key, value) - These are the DOM functions to set and get attributes of specific DOM elements.
    I am using these instead of .attr(attr, value) because I can't perform jQuery operations on the DOM elements at this point in the script.

    */

} // end drawRect

// Initialize the grid of rectangles
for (var i = 0; i < grid.length; i++) {

    for (var j = 0; j < grid[i].length; j++) {

        cellName = "cell " + i + ":" + j;

        // drawRect(color, x, y, width, height, corner radius)
        grid[i][j] = drawRect(colors[Math.floor(Math.random() * colors.length)], startx + (width * j), starty + (height * i), width, height, cornerRadius);

        // used for debugging
        debugString += "<br>grid[" + i + "][" + j + "] x: " + (startx + (width * j)) + " y: " + (starty + (height * i));

    } // end inner for loop


} // end outer for loop

Я не эксперт по Javascript, поэтому любая помощь будет признательна. Спасибо!

...