放大/缩小干扰画布绘制位置

Zoom In/Out Interferes with Canvas Drawing Location

本文关键字:绘制 位置 布绘制 缩小 干扰 放大      更新时间:2023-09-26

我正在和一些同学一起做一个单词搜索游戏。基本上已经完成了,但我们面临的唯一问题是用户决定放大或缩小。当用户在画布中单击并拖动光标时,一条红线突出显示光标下的字母。放大后,高亮显示出现在鼠标下方以外的地方。

一开始,我们认为问题是由窗口滚动引起的,因为画布比屏幕大,所以我们把它们改成

您可以在这里重现这个问题,放大,向下滚动一点,并尝试突出显示一串字母。

请只包含Javascript的建议:不包括JQuery,其他库,任何其他语言。

<html>
<head>
    <title>Canvas Testing</title>
    <script type="text/javascript">
        var canvas,//canvas html tag
        wordsCanvas,//displays words
        wContext,//wordsCanvas context
        context,//to edit canvas items 
        words = ["KING","HOMEWORK","BASEBALL","SIDEWALK","CUPCAKE","WHITEHOUSE","ISLAND","SOCCER","INDEPENDENCE","LOVE","CALCULUS","BEACH","SUMMER","PET","MICHIGAN","CANDY","WORLD","SIX","SNOW","SWEET"],//array of the words users must find
        found = new Array(),
        w,//width
        h,//height
        w1,//width of each letter (board 6 letters wide right now)
        h1,//height of each letter (board 6 letters tall right now)
        draw = false,//tells when the game should highlight a letter
        letterPoints = new Array(),//holds the coordinates of each letter
        lines = new Array(),//holds the coordinates of each line for a correct word highlighted
        startLP = null,//holds the Letter object (letter, x, y) for the start of the line
        endLP = null;//holds the Letter object (letter, x, y) for the end of the line
        function init(){//initializes the canvas, context,w,h variables
            canvas = document.getElementById("canvas");
            context = canvas.getContext("2d");
            canvas.height = window.innerHeight*600/630;
            canvas.width = window.innerWidth*900/1380;
            w=canvas.width;
            h=canvas.height;
            w1 = w/20;
            h1 = h/20;
            context.font = h1+"px Courier";
            context.textAlign = "left";
            context.textBaseline = "top";
            wordsCanvas = document.getElementById("wordsCanvas");
            wordsCanvas.height = window.innerHeight*600/630;
            wordsCanvas.width = window.innerWidth*400/1380;
            wContext = wordsCanvas.getContext("2d");
            wContext.font = h1+"px Georgia";
            wContext.textAlign = "left";
            wContext.textBaseline = "top";
            background();
            fillWords(-1);
            //add event listeners for mouse actions
            canvas.addEventListener("mousedown", function(event) {
                setLine("press",event);
            });
            canvas.addEventListener("mouseup", function(event) {
                setLine("release",event);
            });
            canvas.addEventListener("mousemove", function(event) {
                setLine("drag",event);
            });
            canvas.addEventListener("mouseout", function(event) {
                setLine("out",event);
            });
        }
        function fillWords(greenIndex){//displays words to search for
            wContext.clearRect(0,0,300,h);
            var index = 0;
            if(greenIndex != -1)
            {
                found[found.length] = words.splice(greenIndex,1);
            }
            for(var i=0; i<h; i+=h1)
            {
                if(index<words.length)
                {
                    wContext.fillStyle = "red";
                    wContext.fillText(words[index],10,i);
                }
                else
                {
                    wContext.fillStyle = "green";
                    wContext.fillText(found[found.length-((h/h1)-index)],10,i);
                }
                index++;
            }
            wContext.fillStyle = "black";
        }
        function background(){//sets the background to the letters and then draws the lines that lay on correctly highlighted words words
            var letterCount = 0;//counts # of letters on the board
            //one string that represents all letters on the board
            var backLets = "harinavesenanotheasp"+
                           "oobalremmusicwonsdpa"+
                           "momvtcalclsxvaiybaev"+
                           "swttysumvkingbddmrzd"+
                           "ahaeuyacemjtavpniche"+
                           "nivpnagihcimbseaceoy"+
                           "tthesteatkijeodchqmj"+
                           "ueesnodbsggmaccutsea"+
                           "mhmikalviabahceraqwm"+
                           "motislandbtcvetiwmoi"+
                           "eurwtenkeeeterntanra"+
                           "tsoolotewheewhthsekl"+
                           "sewroxvrarwdbaseball"+
                           "nehtvxjglmsadalkazqt"+
                           "odceetenkenstcvepcap"+
                           "ttaabvdlcupcakepeaxm"+
                           "rieqindependenceplia"+
                           "afbacucakehowrkdkisf"+
                           "chldlrowbrqmmuscqflg"+
                           "amerivegdsuluclacoev";
            context.fillStyle = "black";
            for(var y=0; y<h; y+=h1)//goes through the board and draws each letter, then stores their coordinates in the letterPoints array
            {
                for(var x=0; x<w; x+=w1)
                {
                    r = y/h1 + 1;//tells the row that the letter is in
                    c = x/w1 + 1;//tells the column that the letter is in
                    temp = new Letter(backLets.charAt(letterCount),x,y,r,c);
                    context.fillText(temp.letter.toUpperCase(),temp.x,temp.y);
                    if(letterPoints.length<400)
                        letterPoints[letterPoints.length] = temp;
                    letterCount++;
                }
            }
            /*go through lines array holding coordinates for lines that lay on correct words*/
            for(var z=0; z<lines.length; z++)
            {
                //this if structure allows the words matching the list to be highlighted in different colors so adjacent words will not be highlighted into blocks 
                context.fillStyle = "lime";
                var coords = lines[z];//elements of lines array are not empty but the drawLine isn't processing them
                drawLine(coords[0],coords[1]);
            }
        }
        function setLine(action, e){//sets the coordinates for the lines to be drawn
            if(action == "press")
            {
                startLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);//gets nearest coordenate to a click and returns a letter object with that info
                if(startLP != null)
                {
                    draw = true;
                }
            }
            if(action == "drag")
            {
                //updates the last coordinates that the dragged mouse is on and draws a line to that point from the start
                if(draw)
                {
                    context.clearRect(0,0,w,h);
                    background();
                    endLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
                    context.fillStyle = "red";
                    drawLine(startLP, endLP);
                }
            }
            if(action == "release"  || (action == "out" && draw))
            {
                draw = false;
                /*If a correct word is highlighted, store the start and end coordinates
                else clear*/
                endLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
                //Get the letters that are highlighted by the line
                if(endLP != null)
                {
                    var word = getWord();//returns the word that was made from the start to end point by adding the characters that were highlighted
                    if(word != null && matchWords(word)/*This string will be replaced by a word from the list of word search targets*/)
                    {
                        lines[lines.length] = [startLP,endLP];
                    }
                }
                context.clearRect(0,0,w,h);//clears the board of any drawn lines
                background();//if the line highlighted a word from the list, this method should redraw that line
                if(words.length == 0)
                    alert("Congratulations! You win!");
            }
        }
        //searches through the words array to see if the highlighted word is there
        function matchWords(target){
            if(words.indexOf(target.toUpperCase()) != -1)
            {
                fillWords(words.indexOf(target.toUpperCase()));
                return true;
            }
            return false;
        }
        //uses coordinates from setLine() to draw the lines
        function drawLine(start, end){
            context.globalAlpha = 0.6;//sets transparency of lines
            /*
                Check up,down,left,right,diagonals
                See if start and end can make a valid line
                If yes, find the letters from start to end and store it as a word
            */
            if(start.x == end.x && end.y>start.y)//checking down
            {
                context.fillRect(start.x,start.y,w1,(end.y+h1)-start.y);
            }
            else if(start.x == end.x && end.y<start.y)//checking up
            {
                context.fillRect(end.x,end.y,w1,(start.y+h1)-end.y);
            }
            else if(start.y == end.y && end.x>start.x)//checking left to right
            {
                context.fillRect(start.x,start.y,(end.x+w1)-start.x,h1);
            }
            else if(start.y == end.y && end.x<start.x)//checking right to left
            {
                context.fillRect(end.x,end.y,(start.x+w1)-end.x,h1);
            }
            else if(start.y > end.y && start.x < end.x && ((start.r-end.r)/(end.c-start.c))==1)//checking left to right diagonal (down to up)
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if((start.x<=temp.x && end.x>=temp.x && start.y>=temp.y && end.y<=temp.y) && ((start.y-temp.y==0 && temp.x-start.x==0) || ((start.r-temp.r)/(temp.c-start.c))==1))//((start.r-temp.r)/(temp.c-start.c))==1  means if the slope of between two points is 1
                    {   
                        context.fillRect(temp.x,temp.y,w1,h1);
                    }
                }
            }
            else if(start.y < end.y && start.x < end.x && ((end.r-start.r)/(end.c-start.c))==1)//checking left to right diagonal (top to bottom)
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if((start.x<=temp.x && end.x>=temp.x && start.y<=temp.y && end.y>=temp.y) && ((start.y-temp.y==0 && temp.x-start.x==0) || ((temp.r-start.r)/(temp.c-start.c))==1))//((temp.r-start.r)/(temp.c-start.c))==1  means if the slope of between two points is 1
                    {   
                        context.fillRect(temp.x,temp.y,w1,h1);
                    }
                }
            }
            else if(start.y > end.y && start.x > end.x && ((start.r-end.r)/(start.c-end.c))==1)//checking right to left diagonal (down to up)
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if((start.x>=temp.x && end.x<=temp.x && start.y>=temp.y && end.y<=temp.y) && ((start.y-temp.y==0 && start.x-temp.x==0) || ((start.r-temp.r)/(start.c-temp.c))==1))
                    {   
                        context.fillRect(temp.x,temp.y,w1,h1);
                    }
                }
            }
            else if(start.y < end.y && start.x > end.x && ((end.r-start.r)/(start.c-end.c))==1)//checking right diagonal (top to bottom)
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if((start.x>=temp.x && end.x<=temp.x && start.y<=temp.y && end.y>=temp.y) && ((start.y-temp.y==0 && start.x-temp.x==0) || ((temp.r-start.r)/(start.c-temp.c))==1))
                    {   
                        context.fillRect(temp.x,temp.y,w1,h1);
                    }
                }
            }
            context.globalAlpha = 1.0;//sets transparency back to 1
        }           
        function findNearestLP(clickX,clickY){//finds the nearest letter coordinate from the user's click
            for(var z = 0; z<letterPoints.length; z++)
            {
                var lp = letterPoints[z];
                if((clickX<=lp.x+w1 && clickX>=lp.x) && (clickY<=lp.y+h1 && clickY>=lp.y))
                {   
                    return letterPoints[z];
                }
            }
            return null;
        }
        function getWord()
        {
            var result = "";
            /*
                Check up,down,left,right,diagonals
                See if startLP and endLP can make a valid line
                If yes, find the letters from start to end and store it as a word
            */
            if(startLP.x == endLP.x && endLP.y>startLP.y)//checking down
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if(temp.x == startLP.x && temp.y>=startLP.y && temp.y<=endLP.y)
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.x == endLP.x && endLP.y<startLP.y)//checking up
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if(temp.x == startLP.x && temp.y<=startLP.y && temp.y>=endLP.y)
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y == endLP.y && endLP.x>startLP.x)//checking left to right
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if(temp.y == startLP.y && temp.x>=startLP.x && temp.x<=endLP.x)
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y == endLP.y && endLP.x<startLP.x)//checking right to left
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if(temp.y == startLP.y && temp.x<=startLP.x && temp.x>=endLP.x)
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y > endLP.y && startLP.x < endLP.x && ((startLP.r-endLP.r)/(endLP.c-startLP.c))==1)//checking left to right diagonal (down to up)
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if((startLP.x<=temp.x && endLP.x>=temp.x && startLP.y>=temp.y && endLP.y<=temp.y) && ((startLP.y-temp.y==0 && temp.x-startLP.x==0) || ((startLP.r-temp.r)/(temp.c-startLP.c))==1))
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y < endLP.y && startLP.x < endLP.x && ((endLP.r-startLP.r)/(endLP.c-startLP.c))==1)//checking left to right diagonal (top to bottom)
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if((startLP.x<=temp.x && endLP.x>=temp.x && startLP.y<=temp.y && endLP.y>=temp.y) && ((startLP.y-temp.y==0 && temp.x-startLP.x==0) || ((temp.r-startLP.r)/(temp.c-startLP.c))==1))
                    {
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y > endLP.y && startLP.x > endLP.x && ((startLP.r-endLP.r)/(startLP.c-endLP.c))==1)//checking right to left diagonal (down to up)
            {
                for(var z = letterPoints.length-1; z>=0; z--)
                {
                    var temp = letterPoints[z];
                    if((startLP.x>=temp.x && endLP.x<=temp.x && startLP.y>=temp.y && endLP.y<=temp.y) && ((startLP.y-temp.y==0 && startLP.x-temp.x==0) || ((startLP.r-temp.r)/(startLP.c-temp.c))==1))
                    {   
                        result += temp.letter;
                    }
                }
            }
            else if(startLP.y < endLP.y && startLP.x > endLP.x && ((endLP.r-startLP.r)/(startLP.c-endLP.c))==1)//checking right diagonal (top to bottom)
            {
                for(var z = 0; z<letterPoints.length; z++)
                {
                    var temp = letterPoints[z];
                    if((startLP.x>=temp.x && endLP.x<=temp.x && startLP.y<=temp.y && endLP.y>=temp.y) && ((startLP.y-temp.y==0 && startLP.x-temp.x==0) || ((temp.r-startLP.r)/(startLP.c-temp.c))==1))
                    {   
                        result += temp.letter;
                    }
                }
            }
            if(result != "")
                return result;
            return null;
        }
        //letter class
        function Letter(letter,x,y){
            //The letter variable is mainly used for getting the highlighted word
            this.letter = letter.charAt(0);
            //the x and y coordinate variables are used for drawing the highlighting line and getting the letter variable at certain coordinates
            this.x = x;
            this.y = y;
            //the r and c variables are used to keep track of which rows and columns the letters are on.  This is very helpful for drawing the highlighting line on a diagonal
            this.r = r;
            this.c = c;
        }
    </script>
</head>
<body onload="init()">
    <canvas id="canvas" style="border: 1px solid #000000;"></canvas>
    <canvas id="wordsCanvas" style="border: none;"></canvas>
</body>

通过在setLine中只使用offsetLeft和offsetTop,您没有考虑滚动。

这里的杀手武器是getBoundingClientRect,它会给你考虑到各个方面的画布的相对位置:

    function setLine(action, e){//sets the coordinates for the lines to be drawn
      var bRect = canvas.getBoundingClientRect();
      var relX = e.clientX  - bRect.left ;
      var relY = e.clientY - bRect.top;
      if(action == "press")
        {
            startLP = findNearestLP(relX, relY);//gets nearest coordenate to a click and returns a letter object with that info
            if(startLP != null)
            {
                draw = true;
            }
        }
        if(action == "drag")
        {
            //updates the last coordinates that the dragged mouse is on and draws a line to that point from the start
            if(draw)
            {
                context.clearRect(0,0,w,h);
                background();
                endLP = findNearestLP(relX, relY);
                context.fillStyle = "red";
                drawLine(startLP, endLP);
            }
        }
        if(action == "release"  || (action == "out" && draw))
        {
            draw = false;
            /*If a correct word is highlighted, store the start and end coordinates
            else clear*/
            endLP = findNearestLP(relX, relY);
            //Get the letters that are highlighted by the line
            if(endLP != null)
            {
                var word = getWord();//returns the word that was made from the start to end point by adding the characters that were highlighted
                if(word != null && matchWords(word)/*This string will be replaced by a word from the list of word search targets*/)
                {
                    lines[lines.length] = [startLP,endLP];
                }
            }
            context.clearRect(0,0,w,h);//clears the board of any drawn lines
            background();//if the line highlighted a word from the list, this method should redraw that line
            if(words.length == 0)
                alert("Congratulations! You win!");
        }
    }