简体   繁体   中英

Scale a dot's X Y on HTML5 canvas by percentage

I'm working on my first canvas project, and it requires a partial map of the US, with a zoom and center on a state when clicked.

I was able to find XY arrays of points to draw the country, with each state being its own array. I needed the states to be drawn out larger then these dimensions, so I introduced a scale varaible to multiply each point by.

My next challenge was that the client only wanted 13 states drawn out, but not placed to scale against each other. (Example, put Ohio and Illinois next to each other on the canvas and ignore Indiana). My solution to that was to introduce a fixed X, Y "constant" for each state, that after the scaling happens, add the XY value for that state and make that the spot to draw on.

for ( var j = 0; j < state.myPolygons.length; ++j) {
    context.beginPath();
    context.lineWidth = lineWidth;
    context.strokeStyle = stateStroke;
    context.fillStyle = stateFill;
    for ( var k = 0; k < state.myPolygons[j].myXVals.length; ++k ) {
        var x = parseFloat(state.myPolygons[j].myXVals[k]*state.scale)+state.posX;
        var y = parseFloat(state.myPolygons[j].myYVals[k]*state.scale)+state.posY;
        y = canvas.height - y;
        if ( k == 0 ) 
            context.moveTo(x,y);
        else 
            context.lineTo(x,y);
    }
    context.closePath();
    context.fill();
    context.stroke();
}

The effect of clicking on a state, and growing it and centering on the canvas was accomplished by defining a target scale and number of steps. I get the difference between the target scale and current scale, and divide that by number of steps to figure out how much to add to the scale of the state at each "frame". Example: Ohio's initial scale is 1.97 of the found coords. My target for Ohio scale is 3.75%. I get the difference (1.78), and divide that by 45 (the defined set of steps) to draw. This gives me 0.039 as an incrementer to my scale at each frame. I then loop through while my states current scale is less than the target scale. Again however, since I need to manipulate the XY of the rendering, I have then a zoomx and zoomy constant for each state that gets added to the calculated XY so it can "slide" to the center of the canvas.

All of this works perfectly and I have California zoom/sliding from left to right, Ohio sliding right to left, etc. --- Here is my problem.

I have a series of dots to indicate client loctions in the state. These are simple X Ys that I draw a circle on. The initial rendering of the map includes a loop to run through each states set of locations. I'm applying the same scale factor, and posX,posY variables to adjust final placement of the dot in relation to final rendering of the state

for (var loc in state.Locations) {
    var locx = parseFloat(state.Locations[loc].x*state.scale)+state.posX
    var locy =parseFloat(state.Locations[loc].y*state.scale)+state.posY;
    var txt=state.Locations[loc].text;
    var lnk=state.Locations[loc].link;
    context.beginPath();
    context.arc(locx,locy,locationSize,0,Math.PI*2,true);
    context.fillStyle = locationFill;
    context.closePath();
    context.fill();
    context.stroke();                           
}

When the state is zooming however, the scaling logic for the dots fails. The state scale for a given frame applies

x = parseFloat(activeState.myPolygons[j].myXVals[k]*activeState.scale)+activeState.posX;
y = parseFloat(activeState.myPolygons[j].myYVals[k]*activeState.scale)+activeState.posY;

When I apply this to a given location in the state with

locx = parseFloat(activeState.Locations[loc].x*activeState.scale)+activeState.posX;
locy = parseFloat(activeState.Locations[loc].y*activeState.scale)+activeState.posY;

I end up with X following pretty closely, but in Ohio's example, the Y is somewhere near Florida. Other states like California are even worse with their dots starting more "stacked" on top of each other and end up more "spread out" beside each other.

I'm trying to figure out the trig functions needed to grow and shrink the position of the XY on a location in relation to the current scale of the state, and keep it on the same path the state is traveling on through the animation (both zooming in and zooming out).

My final attempt before coming here was to get the inital XY of the location, and compare its distance to the LAST XY of the state array. I was trying to then find the angle of the line connecting those 2 points, and then use all this to scale. I still feel that I may be onto something with this approach, I just can't make it happen.

Thank you everyone for taking the time to read this, I appriciate any help you can offer

You could just look at the paper I put on your desk, the one with the equation on it. However, SVGs would be more optimal for the project, as you could easily group things together using the g tag and then could just scale the entire group.

However, since you're forced to use canvas at this point: You would have to scale up and down director, using trig given the angle of the start point to location dot and the DIFFERENCE of left or right travelled from the original distance. I will explain in more detail, with actual equations, when you allow me to give me that paper back. However, the only line you really need to modify at this point is:

locy = parseFloat(activeState.Locations[loc].y*activeState.scale)+activeState.posY;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM