5

I am trying to make my own chess analysis board to use when teaching chess. I currently have the board, pieces that I can drag and drop, as well as a method for clearing the board and setting up different board positions. I can also click on squares to highlight them.

I want to be able to draw arrows from one square to another to show lines of attack and influence but have no idea how to accomplish this. My board is made up of <div> tags. A short example is below (pseudo-code and actual code for brevity).

// a couple of CSS styles to define the width, height, and color of the squares CSS Style Class "dark square" CSS Style Class "light square"

//my board is made up of <div> tags

<div id="board">
    <div id="a1" class="lightsquare"></div>
    <div id="a2" class="darksquare"></div>
    <div id="a3" class="lightsquare"></div>
    //second rank
    <div id="b1" class="darksquare"></div>
    <div id="b2" class="lightsquare"></div>
    <div id="b3" class="darksquare"></div>

    //third rank
    <div id="c1" class="lightsquare"></div>
    <div id="c2" class="darksquare"></div>
    <div id="c3" class="lightsquare"></div>
</div>

I can place pieces on the board, move them around, take other pieces, clear the board, set up unique positions, and highlight individual squares just fine, but would also like to be able to have the user click, drag, and draw arrows live on the board while still being able to manipulate the pieces on the board.

I looked at using the tag but based on my reading and research, it doesn't seem like the <canvas> tag was designed to do what I am looking for.

Does anyone have any ideas on how to do this in JavaScript? I have not learned to use JQuery yet, and would prefer to avoid using JQuery as I don't want to have to download an extra file or necessarily be on the internet to use this program.

SteampunkWizard
  • 615
  • 7
  • 13
  • [this may help](http://stackoverflow.com/questions/20037122/draw-an-arrow-between-two-divs) – Pete Aug 27 '14 at 13:05
  • Hmm, rows and columns. Now only if there were an element in HTML that had those, so that one wouldn't need to use DIVs... – RoToRa Aug 27 '14 at 14:54

3 Answers3

4

So after a month of tinkering I have finally found a solution. As much effort as I put into using < div > tags and images to solve this problem, I could never get the arrow to position itself correctly when rotated. I therefore went back to trying out the < canvas > tag and I did finally find a solution that works.

function drawArrow(fromx, fromy, tox, toy){
            //variables to be used when creating the arrow
            var c = document.getElementById("myCanvas");
            var ctx = c.getContext("2d");
            var headlen = 10;

            var angle = Math.atan2(toy-fromy,tox-fromx);

            //starting path of the arrow from the start square to the end square and drawing the stroke
            ctx.beginPath();
            ctx.moveTo(fromx, fromy);
            ctx.lineTo(tox, toy);
            ctx.strokeStyle = "#cc0000";
            ctx.lineWidth = 22;
            ctx.stroke();

            //starting a new path from the head of the arrow to one of the sides of the point
            ctx.beginPath();
            ctx.moveTo(tox, toy);
            ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));

            //path from the side point of the arrow, to the other side point
            ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));

            //path from the side point back to the tip of the arrow, and then again to the opposite side point
            ctx.lineTo(tox, toy);
            ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));

            //draws the paths created above
            ctx.strokeStyle = "#cc0000";
            ctx.lineWidth = 22;
            ctx.stroke();
            ctx.fillStyle = "#cc0000";
            ctx.fill();
        }

This code will get its start and end coordinates based on the coordinates of the squares at the mouse down and mouse up events. It will then use those coordinates to determine how the arrow should be drawn with the proper rotation.

SteampunkWizard
  • 615
  • 7
  • 13
  • I used this code, it's great. but the problem is how we can overlay the canvas over chessboard div and pieces can be accessible in below of canvas? – Vahid Alimohamadi Sep 21 '15 at 17:50
  • @Vahid Alimohamadi I position the canvas directly over the top of the chessboard with CSS positioning. Now obviously you want to be able to click on the pieces on the board and move those around and still be able to draw arrows. For that I used the pointerEvents attribute. If I want to move pieces around, I simply click and drag the pieces. If I want to draw arrows, I hold down the shift key and then draw my arrow. – SteampunkWizard Sep 22 '15 at 21:46
  • I did implement it with svg, it's too easy when using Raphael. – Vahid Alimohamadi Oct 02 '15 at 07:43
1

If your asking how to approach this (fun) problem, I would go about it like this.

  1. Make your arrow with 2 pngs: 2 divs with background images (body and point of the arrow) inside one container div.
  2. Once you have the code to properly display the arrow, make sure you know how to append it to the DOM using javascript.
  3. Now try to make the arrow longer and shorter with javascript (by changing the size of the div containing the body of the arrow)
  4. Once you can do this, it's time for math. Use javascript to calculate the coordinates of the DIV that is clicked first when making an arrow, and the DIV that is clicked second. When you have these coordinates, calculate (using Pythagoras) the length of the arrow you need, and the amound of rotation you need (this is the tricky part, but I have done it before so it can certainly be done if you know some basic math). You are essentially creating triangles on the board.
  5. With the coordinates, length and rotation, you can now place your arrow on the playing board, adjust the length and the rotation, to display it just the way you need it. And remember you can also turn the arrow for say 350 degrees to get to -10 degrees.

This is by no means easy but a lot of fun if you like math, and considering chess, I guess you do.

By the way, this problem can certainly be solved with plain JS but using jQuery would make it easier en less work. You can also use jQuery offline by downloading the library.

Derk Arts
  • 3,432
  • 2
  • 18
  • 36
  • It seems that the text editor on this site doesnt let me use tag names in the main body of the question. I was trying to do this using the < canvas > tag, but couldn't for the life of me figure out how to do it. So you are suggesting to skip the canvas all together and just go straight with an image file using size, length and rotation? I consider myself to be fairly good at math, but my programming skills are a bit lacking. Is there a resource you can direct me to in order to learn how to do rotation? Thanks for the help! – SteampunkWizard Aug 27 '14 at 18:46
  • I havent used canvas at all, just plain html and JS, and that was a fun project. Check here for some rotation info: http://www.w3schools.com/cssref/playit.asp?filename=playcss_transform_rotate Your main challenge is calculating length and rotation from two coordinates but being good at math that would take you an evening tops (I think). The programming part is easy, really. Google `appendChild` , 'update style javascript' and thats all you need basically. – Derk Arts Aug 28 '14 at 11:25
  • I actually was able to find most of the information myself thanks to your suggestions. I was able to find the Math.atan2 operator and have now been able to successfully click, drag, and release my mouse click, and based on the direction and and length, get the arrow to point in one of four directions as well as change length. What's driving me nuts now is I cannot for the life of me get the arrow to change location to where I want. I get my start coordinates "startXY" and my end coordinates "endXY, subtract the smaller x and y from the larger x and y. – SteampunkWizard Aug 30 '14 at 17:19
  • Once I have that difference, I divide by 2 and subtract from larger x and y to hopefully get an XY coordinate that rests between my start and end coordinates and use that to decide my left: x; top: y; coordinates. When I do this though, the Arrow appears in a different location entirely. I switched the x and y thinking that maybe I juxtaposed the coordinates, and that didn't work either.... So arrow now changes length and rotation, but doesn't position itself where I want it..... – SteampunkWizard Aug 30 '14 at 17:24
  • Great that you've gotten so far, did you also manage to fix the positioning? I think you are making it more complicated than it should be. If you just position the `arrow div` at the initial `mouseclick location` - `(height_of_arrow_div/2)` and use the same left coordinate I think that should work. If you put what you've got so far in a JSFiddle I would love to try to help you with the last bit. – Derk Arts Sep 04 '14 at 13:49
  • No, it's still not positioning itself correctly. I've tried a number of different solutions and so far nothing I've tried has worked. The arrow also changes it's position based on its length. It only positions itself correctly in the horizontal place. Nothing else. I want to thank you for the advice you had on the arrow. You didn't just "tell" me the answer. You gave me a way to think about it so I could answer it myself. I put up my attempts at arrows on my website. www.meyerqde.com/thirdtest.html It's really roughly coded due to many writes and re-writes. Thanks for the help! – SteampunkWizard Sep 06 '14 at 06:02
  • Lets fiddle with it on this fiddle: http://jsfiddle.net/5xyhkous/. First off, more math, less if/else. You can generalize all this by using math effectively. I will paste some code I wrote 2.5 year ago to draw lines. Second, do you want the arrow to start from the center of the square? Or just where the mouse is clicked? – Derk Arts Sep 08 '14 at 08:59
  • Also I would start using jQuery and read about how to attach functions to elements with a class (it's really easy). You can just say `$(".lightsquare").mousedown(function(){getStart(this);});` instead of specifying it each time. Looking at all this I'm afraid you will have to invest in a bit more in learning JS/jQuery and programming in general, which is a great thing to learn :). – Derk Arts Sep 08 '14 at 09:10
  • Ideally I'd like to have the arrow start from the middle of the origin square to the center of the destination square. I was trying to accomplish this with the use of pre-determined coordinates assigned to each div. So far I haven't touched jQuery at all, and was trying to write everything in straight javascript since I wanted to reduce how many external files I had to link to. However, I am also trying to play around with html2canvas (also giving me trouble), which uses jQuery, so I may not have a choice. – SteampunkWizard Sep 09 '14 at 05:53
  • Looking at the code for the arrow, I guess I'm not sure how to accomplish the same taks with 'more math, less if/else'. I will agree that my code tends to be quite logic heavy, but I'm not sure how to shorten the code. My background in programming consists of one computer science course in college where we learned Python using the command line. Everything else in programming I've been self taught. Other programmers tend to wince at the inefficiency of my code, but I lack the experience to make it more streamlined. I am very curious to learn how to use math instead of if/else on this. – SteampunkWizard Sep 09 '14 at 10:38
  • I don't have time to go into any real detail unfortunately, but look here: http://jsfiddle.net/5xyhkous/2/ at the bottom i posted my code to create a line. Apart from the first ifelse statement (I forgot why that was needed) it's full math. Maybe you can use that as a starting point? – Derk Arts Sep 09 '14 at 11:45
0

To activate and disable pointerEvents I used this code.

//checks to see if the shift key was pressed

    function disableImage(ev){
        if(ev.shiftKey == 1){
            var canvas = document.getElementById("arrowCanvas");
            canvas.style.pointerEvents = "all";
        }
    }

    function enableImage(){
        var canvas = document.getElementById("arrowCanvas");
        canvas.style.pointerEvents = "none";
    }
SteampunkWizard
  • 615
  • 7
  • 13