Introduction
This is just a tip for those who are sick and tired of struggling with the "string-like" Anti-Bots and who don't want users of their Web pages to experience the same feelings.
Background
The idea of using a kind of a slider control to rotate an image (in order to prove you are human) is not new. I got it from one of my students in 2008 and he had found it somewhere in the net.
But the implementation was based on jQuery or some other JavaScript library. Hmmm, learning jQuery requires some effort. I don't like wonders taken out of black box, so I stopped thinking of the trick.
Now, that HTML5 gives us new possibilities, I thought I should implement the idea in a form of a control. Later, I plan to make a real ASP.NET Custom Control with embedded JavaScript. So far, the code is pure HTML and JavaScript, but it does the job.
Using the Code
The most difficult part of solution is:
- How to scale an arbitrary image and place it at the desired location of a canvas
- How to rotate image at the desired angle
- How to track this angle value, and use it as result of a test
The following fragment solves a part of the problem:
hdc.clearRect(0, 0, canvasWidth, canvasHeight);
hdc.save();
hdc.translate(cx, cy);
hdc.rotate(-angle);
hdc.translate(-cx, -cy);
hdc.drawImage(img, posX, posY, img.width, img.height);
hdc.restore();
The hdc
variable is a device context of a canvas
-element. It is declared at the global scope and created in onload
event handler, as recommended by the HTML5 tutorials.
var canvasImg = document.getElementById("canvasImg");
hdc = canvasImg.getContext("2d");
The important details of the context manipulations are:
- Saving current state of the context
- Translating (shifting) the context at the desired location (
cx
, cy
) within the canvas. - Rotating is trivial, but the sign of the angle should be negative (if you want the rotation direction to coincide with the Math counter-clockwise-positive-rule)
- Translating the context back (
-cx
, -cy
) - Drawing the image at the position (
posX
, posY
) that must be properly calculated beforehand - Restoring the context to its initial state
Scaling the image and position calculation is done once (after image load is finished). The left-top corner of the image should be biased relative to the center of the canvas. The value of this shift must be half the size of the image.
posX = cx - img.width / 2;
posY = cy - img.height / 2;
The following function shows all the details of scaling and positioning calculations. This function is called in response to the image onload
event (img.onload = Scale;
).
function Scale()
{
canvasWidth = canvasImg.width;
canvasHeight = canvasImg.height;
var
dx = img.width - canvasWidth,
dy = img.height - canvasHeight;
if (dx >= 0 || dy >= 0)
{
var d = 0.75 * (dx > dy ? canvasWidth / img.width : canvasHeight / img.height);
img.width *= d;
img.height *= d;
}
cx = canvasWidth / 2;
cy = canvasHeight / 2;
posX = cx - img.width / 2;
posY = cy - img.height / 2;
factor = 2 * Math.abs(startAngle) / slider.offsetWidth;
Draw();
}
Points of Interest
I did not know that HTML5 has a control with a slider functionality, so a hand-made slider is created here. If you find the whole control useful, then maybe it's worth replacing the slider with a standard one.