Trying to find angle between two vectors (triangle) (HTML5 Canvas) - html

I am struggling to understand why my angles are returning weird angles if anything other than a right angle is drawn. I drew a basic triangle using Canvas in HTML5.
I have the html code and js code to paste here: Please can someone tell me why only these right angles adds up to 180degrees. I have set the js code to output the angles and the sum thereof to the console... so you can see what I am talking about.
You can modify the draw function code to set the position of one of the points to make a right angle.. then you will see the 180 degrees and the angles are correct.
I searched all over the internet for an explanation and checked my formulas. Cant seem to figure this one out.
Thank you very much for any help you can offer..
--- CODE FOR HTML ---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Canvas - Triangle experiment</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="js/drawShapes.js"></script>
<style>
* { margin: 0; }
* { padding: 0; }
span.markings {
position: absolute;
}
div.drawingArea {
margin: 50px 0 0 10px;
padding: 0;
width: 500px;
height: 500px;
position: relative;
background: #ccc;
}
.coords { position: absolute; top: 0; left: 200px; }
.coords p { position: relative; }
.xcoord, .ycoord { font-weight: bold; color: red; }
#myCanvas { background: #eee; }
</style>
</head>
<body>
<div class="coords"><p>X: <span class="xcoord"></span></p><p>Y: <span class="ycoord"></span></p></div>
<div class="drawingArea">
<span class="markings A"></span>
<span class="markings B"></span>
<span class="markings C"></span>
<canvas id="myCanvas" width="410" height="410">Your browser does not have support for Canvas. You should see:</canvas>
</div>
</body>
</html>
--- CODE FOR JS ---
$(document).ready(function(){
// Just for dev purposes.. show X and Y coords when inside drawingArea
$('.drawingArea').mousemove(function(e){
$('.xcoord').html(e.pageX -10); // subtract 10 for margin left is 10
$('.ycoord').html(e.pageY -50); // subtract 40 bc margin top is 40
});
draw();
function draw()
{
// Initialize context
createContext('2d');
// Set the style properties.
context.fillStyle = '#fff';
context.strokeStyle = '#FF9900';
context.lineWidth = 5;
// Set initial positions and lengths
pts = {};
pts.AXLoc = 60;
pts.AYLoc = 40;
pts.BXLoc = 360;
pts.BYLoc = 40;
pts.CXLoc = 100;
pts.CYLoc = 340;
// Get difference between points
vector = {};
vector.Ax = Math.abs(pts.AXLoc-pts.BXLoc);
vector.Ay = Math.abs(pts.AYLoc-pts.BYLoc);
vector.Bx = Math.abs(pts.BXLoc-pts.CXLoc);
vector.By = Math.abs(pts.BYLoc-pts.CYLoc);
vector.Cx = Math.abs(pts.CXLoc-pts.AXLoc);
vector.Cy = Math.abs(pts.CYLoc-pts.AYLoc);
console.log(vector.Ax);
console.log(vector.Ay);
console.log(vector.Bx);
console.log(vector.By);
console.log(vector.Cx);
console.log(vector.Cy);
// Find the magnitude of each vector
vector.magA = Math.sqrt(Math.pow(vector.Ax, 2) + Math.pow(vector.Ay, 2));
vector.magB = Math.sqrt((Math.pow((vector.Bx), 2) + Math.pow((vector.By), 2)));
vector.magC = Math.sqrt((Math.pow((vector.Cx), 2) + Math.pow((vector.Cy), 2)));
// Cos A = (A.C) / sqrt(magnitude of A) x (magnited of C)
// This should return radian which is then converted to degrees
// Create function once code works!
vector.angleA = ((vector.Ax * vector.Cx) + (vector.Ay * vector.Cy)) / (vector.magA * vector.magC);
vector.angleA = Math.acos(vector.angleA) * (180/Math.PI);
vector.angleB = ((vector.Ax * vector.Bx) + (vector.Ay * vector.By)) / (vector.magA * vector.magB);
vector.angleB = Math.acos(vector.angleB) * (180/Math.PI);
vector.angleC = ((vector.Bx * vector.Cx) + (vector.By * vector.Cy)) / (vector.magB * vector.magC);
vector.angleC = Math.acos(vector.angleC) * (180/Math.PI);
// Output test data
console.log('angle a = ' + vector.angleA);
console.log('angle b = ' + vector.angleB);
console.log('angle c = ' + vector.angleC);
vector.allangles = vector.angleA + vector.angleB + vector.angleC;
console.log('All angles = ' +vector.allangles ); // only adds up to 180deg if right angle??!!
// Begin drawing
context.beginPath();
// Start from the top-left point.
context.moveTo(pts.AXLoc, pts.AYLoc); // give the (x,y) coordinates
context.lineTo(pts.BXLoc, pts.BYLoc);
context.lineTo(pts.CXLoc, pts.CYLoc);
//context.lineTo(pts.AXLoc, pts.AYLoc); // closes the origin point? alternate way of closing???
context.lineJoin = 'mitre';
context.closePath(); // closes the origin point? good for strokes
// Done! Now fill the shape, and draw the stroke.
// Note: your shape will not be visible until you call any of the two methods.
context.fill();
context.stroke();
context.closePath();
// Set position of markings (spans)
$('span.markings.A').css({
'top' : pts.AYLoc -30,
'left' : pts.AXLoc -5
});
$('span.markings.B').css({
'top' : pts.BYLoc -5,
'left' : pts.BXLoc +10
});
$('span.markings.C').css({
'top' : pts.CYLoc -5,
'left' : pts.CXLoc -25
});
// Write markings onto canvas (degrees and lengths)
$('span.markings.A').html('A');
$('span.markings.B').html('B');
$('span.markings.C').html('C');
}
function createContext(contextType)
{
// Get the canvas element.
var elem = document.getElementById('myCanvas');
if (!elem || !elem.getContext) {
return;
}
// Get the canvas 2d context.
context = elem.getContext(contextType);
if (!context) {
return;
}
}
});

You've got your angle formulas a little wrong. Here's a working fiddle: http://jsfiddle.net/manishie/AgmF4/.
Here are my corrected formulas:
vector.angleA = (Math.pow(vector.magB, 2) + Math.pow(vector.magC, 2) - Math.pow(vector.magA, 2)) / (2 * vector.magB * vector.magC);
vector.angleA = Math.acos(vector.angleA) * (180/Math.PI);
vector.angleB = (Math.pow(vector.magA, 2) + Math.pow(vector.magC, 2) - Math.pow(vector.magB, 2)) / (2 * vector.magA * vector.magC);
vector.angleB = Math.acos(vector.angleB) * (180/Math.PI);
vector.angleC = (Math.pow(vector.magA, 2) + Math.pow(vector.magB, 2) - Math.pow(vector.magC, 2)) / (2 * vector.magA * vector.magB);
vector.angleC = Math.acos(vector.angleC) * (180/Math.PI);

Related

css font-size and line-height not matching the baseline

I'm trying to do something that should be very simple but I've spent my day between failures and forums..
I would like to adjust my font in order to match my baseline. On indesign it's one click but in css it looks like the most difficult thing on earth..
Lets take a simple example with rational values.
On this image I have a baseline every 20px.
So for my <body> I do:
<style>
body {font-size:16px; line-height:20px;}
</style>
Everything works perfectly. My paragraph matchs the baseline.
But when I'm scripting my <h> that doesn't match the baseline anymore.. what am I doing wrong? That should follow my baseline, shouldn't it?
<style type="text/css">
body{font-size: 16px; line-height: 20px;}
h1{font-size: 5em; line-height: 1.25em;}
h2{font-size: 4em; line-height: 1.25em;}
h3{font-size: 3em; line-height: 1.25em;}
h4{font-size: 2em; line-height: 1.25em;}
</style>
ps: 20/16=1.25em
In my inspector, computed returns the expected values
h1{font-size: 84px; line-height: 100px;}
h2{font-size: 68px; line-height: 80px;}
h3{font-size: 52px; line-height: 60px;}
h4{font-size: 36px; line-height: 40px;}
So that should display something like this no?
It is a bit complicated - you have to measure the fonts first (as InDesign does) and calculate "line-height", the thing you called "bottom_gap" and some other stuff
I'm pretty sure we can do something in JavaScript..
You are right – but for Typography JS is used to calculate the CSS (depending on the font metrics)
Did demo the first step (measuring a font) here
https://codepen.io/sebilasse/pen/gPBQqm
It is just showing graphically what is measured [for the technical background]
This measuring is needed because every font behaves totally different in a "line".
Here is a generator which could generate such a Typo CSS:
https://codepen.io/sebilasse/pen/BdaPzN
A function to measure could be based on <canvas> and look like this :
function getMetrics(fontName, fontSize) {
// NOTE: if there is no getComputedStyle, this library won't work.
if(!document.defaultView.getComputedStyle) {
throw("ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values.");
}
if (!document.querySelector('canvas')) {
var _canvas = document.createElement('canvas');
_canvas.width = 220; _canvas.height = 220;
document.body.appendChild(_canvas);
}
// Store the old text metrics function on the Canvas2D prototype
CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
/**
* Shortcut function for getting computed CSS values
*/
var getCSSValue = function(element, property) {
return document.defaultView.getComputedStyle(element,null).getPropertyValue(property);
};
/**
* The new text metrics function
*/
CanvasRenderingContext2D.prototype.measureText = function(textstring) {
var metrics = this.measureTextWidth(textstring),
fontFamily = getCSSValue(this.canvas,"font-family"),
fontSize = getCSSValue(this.canvas,"font-size").replace("px",""),
isSpace = !(/\S/.test(textstring));
metrics.fontsize = fontSize;
// For text lead values, we meaure a multiline text container.
var leadDiv = document.createElement("div");
leadDiv.style.position = "absolute";
leadDiv.style.margin = 0;
leadDiv.style.padding = 0;
leadDiv.style.opacity = 0;
leadDiv.style.font = fontSize + "px " + fontFamily;
leadDiv.innerHTML = textstring + "<br/>" + textstring;
document.body.appendChild(leadDiv);
// Make some initial guess at the text leading (using the standard TeX ratio)
metrics.leading = 1.2 * fontSize;
// Try to get the real value from the browser
var leadDivHeight = getCSSValue(leadDiv,"height");
leadDivHeight = leadDivHeight.replace("px","");
if (leadDivHeight >= fontSize * 2) { metrics.leading = (leadDivHeight/2) | 0; }
document.body.removeChild(leadDiv);
// if we're not dealing with white space, we can compute metrics
if (!isSpace) {
// Have characters, so measure the text
var canvas = document.createElement("canvas");
var padding = 100;
canvas.width = metrics.width + padding;
canvas.height = 3*fontSize;
canvas.style.opacity = 1;
canvas.style.fontFamily = fontFamily;
canvas.style.fontSize = fontSize;
var ctx = canvas.getContext("2d");
ctx.font = fontSize + "px " + fontFamily;
var w = canvas.width,
h = canvas.height,
baseline = h/2;
// Set all canvas pixeldata values to 255, with all the content
// data being 0. This lets us scan for data[i] != 255.
ctx.fillStyle = "white";
ctx.fillRect(-1, -1, w+2, h+2);
ctx.fillStyle = "black";
ctx.fillText(textstring, padding/2, baseline);
var pixelData = ctx.getImageData(0, 0, w, h).data;
// canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
// consecutive values in the array, rather than stored as 32 bit ints.
var i = 0,
w4 = w * 4,
len = pixelData.length;
// Finding the ascent uses a normal, forward scanline
while (++i < len && pixelData[i] === 255) {}
var ascent = (i/w4)|0;
// Finding the descent uses a reverse scanline
i = len - 1;
while (--i > 0 && pixelData[i] === 255) {}
var descent = (i/w4)|0;
// find the min-x coordinate
for(i = 0; i<len && pixelData[i] === 255; ) {
i += w4;
if(i>=len) { i = (i-len) + 4; }}
var minx = ((i%w4)/4) | 0;
// find the max-x coordinate
var step = 1;
for(i = len-3; i>=0 && pixelData[i] === 255; ) {
i -= w4;
if(i<0) { i = (len - 3) - (step++)*4; }}
var maxx = ((i%w4)/4) + 1 | 0;
// set font metrics
metrics.ascent = (baseline - ascent);
metrics.descent = (descent - baseline);
metrics.bounds = { minx: minx - (padding/2),
maxx: maxx - (padding/2),
miny: 0,
maxy: descent-ascent };
metrics.height = 1+(descent - ascent);
} else {
// Only whitespace, so we can't measure the text
metrics.ascent = 0;
metrics.descent = 0;
metrics.bounds = { minx: 0,
maxx: metrics.width, // Best guess
miny: 0,
maxy: 0 };
metrics.height = 0;
}
return metrics;
};
Note that you also need a good "reset.css" to reset the browser margins and paddings.
You click "show CSS" and you can also use the generated CSS to mix multiple fonts:
If they have different base sizes, normalize the second:
var factor = CSS1baseSize / CSS2baseSize;
and now recalculate each font in CSS2 with
var size = size * factor;
See a demo in https://codepen.io/sebilasse/pen/oENGev?editors=1100
What if it comes to images?
The following demo uses two fonts with the same metrics plus an extra JS part. It is needed to calculate media elements like images for the baseline grid :
https://codepen.io/sebilasse/pen/ddopBj

Draw image on a bouncing context inside canvas

EDIT: solution some lines above.
I'm trying to have bouncing pictures with html5, canvas and some jQuery.
I've successfully made some balls bouncing, but I can't figure out how to draw pictures instead of simple 'particles'.
I've tried in different ways, but actually I think I'missing something.
I post the whole html so you can just copy/paste it easily.
Under my try there is a commented section with working bouncing balls.
Thank you so much!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Explode Demo</title>
<link rel="stylesheet" href="styles.css" />
<meta name="viewport" content="width=320 initial-scale=1.0, user-scalable=no" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<style type="text/css">
* {
margin: 0; padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
canvas {
display: block;
background: whiteSmoke;
width: 100%;
height: 100%;
}
#presentation{
position: fixed;
background: rgba(0,0,0,0.7);
width: 100%;
height: 70px;
box-shadow: 7px 7px 13px rgba(0,0,0,0.3);
color: white;
font-family:futura;
font-size: 30px;
padding-left: 50px;
padding-top: 10px;
}
</style>
</head>
<body>
<div id="presentation">Bouncing Baaaaalls!</div>
<canvas id="output" ></canvas>
<script type="text/javascript">
(function() {
window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame||function(f){window.setTimeout(f,40/60)
}}});
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
function Particle() {
var W = canvas.width = window.innerWidth;
var H = canvas.height = window.innerHeight;
this.radius = 20;
this.x = parseInt(Math.random() * W);
this.y = H;
this.color = 'rgb(' +
parseInt(Math.random() * 255) + ',' +
parseInt(Math.random() * 255) + ',' +
parseInt(Math.random() * 255) + ')';
if (this.x > W/2 ){
this.vx = Math.random() * (-15 - -5) + -5;
}else{
this.vx = Math.random() * (15 - 5) + 5;
}
this.vy = Math.random() * (-32 - -25) + -25;
this.draw = function() {
var img = new Image();
img.src = 'troll1.png';
ctx.beginPath();
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.closePath();
ctx.beginPath();
ctx.arc(this.x, this.y, 20, 0, 6.28, false);
ctx.clip();
ctx.stroke();
ctx.closePath();
ctx.drawImage(img, 0, 0);
// WORKING PARTICLES STARTS HERE
// ctx.fillStyle = this.color;
// ctx.beginPath();
// ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
// ctx.fill();
// ctx.closePath();
// WORKING PARTICLES ENDS HERE
};
}
var particle_count = 20;
var particles = [];
// Now lets quickly create our particle
// objects and store them in particles array
for (var i = 0; i < particle_count; i++) {
var particle = new Particle();
particles.push(particle);
}
function renderFrame() {
requestAnimationFrame(renderFrame);
// Clearing screen to prevent trails
var W = canvas.width = window.innerWidth;
var H = canvas.height = window.innerHeight;
ctx.clearRect(0, 0, W, H);
particles.forEach(function(particle) {
// The particles simply go upwards
// It MUST come down, so lets apply gravity
particle.vy += 1;
// Adding velocity to x and y axis
particle.x += particle.vx;
particle.y += particle.vy;
// We're almost done! All we need to do now
// is to reposition the particles as soon
// as they move off the canvas.
// We'll also need to re-set the velocities
if (
// off the right side
particle.x + particle.radius > W ||
// off the left side
particle.x - particle.radius < 0 ||
// off the bottom
particle.y + particle.radius > H
) {
// If any of the above conditions are met
// then we need to re-position the particles
// on the base :)
// If we do not re-set the velocities then
// the particles will stick to base :D
// Velocity X
particle.x = parseInt(Math.random() * W);
particle.y = H;
if (particle.x > W/2 ){
particle.vx = Math.random() * (-15 - -5) + -5;
}else{
particle.vx = Math.random() * (15 - 5) + 5;
}
particle.vy = Math.random() * (-32 - -28) + -28;
}
particle.draw();
});
}
$(document).ready(function(){
renderFrame();
});
</script>
</body>
</html>
EDIT WITH SOLUTION:
First, thanks to markE
I edited the code as he said, the problem was actually about timing (and understanding what I was doing). His answer really helped me a lot.
The image was not moving because I didn't told it to do that actually ( with ctx.drawImage(img, this.x, this.y)).
NOTE: For debugging canvas rendering with chrome take a look at HTML5 canvas inspector?
So here is the working (and ultra commented, thanks for the lesson markE) code for bouncing troll faces (put a troll1.png picture in the same folder):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Explode Demo</title>
<!-- <link rel="stylesheet" href="styles.css" /> --> <meta name="viewport" content="width=320 initial-scale=1.0, user-scalable=no" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<style type="text/css">
* {
margin: 0; padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
canvas {
display: block;
background: whiteSmoke;
width: 100%;
height: 100%;
}
#presentation{
position: fixed;
background: rgba(0,0,0,0.7);
width: 100%;
height: 70px;
box-shadow: 7px 7px 13px rgba(0,0,0,0.3);
color: white;
font-family:futura;
font-size: 30px;
padding-left: 50px;
padding-top: 10px;
}
</style>
</head>
<body>
<div id="presentation">Bouncing Baaaaalls!</div>
<canvas id="output" ></canvas>
<script type="text/javascript">
(function() {
//define the animation refresh (frame rendering) with built-in browser timing
window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame||function(f){window.setTimeout(f,40/60)
}}});
//define some variables: canvas, context, img to put inside the context and an array of bouncing objects
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
//IMPORTANT: Wait for the picture to be loaded!
img.onload = function(){
alert('troll1 is LOADED.');
beginAnimation();
};
//yes, the src goes after
img.src = 'troll1.png';
//how many bouncing objects?
var particle_count = 4;
var particles = [];
var particle;
function Particle() {
//define properties of a bouncing object, such as where it start, how fast it goes
var W = canvas.width = window.innerWidth;
var H = canvas.height = window.innerHeight;
this.radius = 20;
this.x = parseInt(Math.random() * W);
this.y = H;
//Uncomment for coloured particles:
// this.color = 'rgb(' +
// parseInt(Math.random() * 255) + ',' +
// parseInt(Math.random() * 255) + ',' +
// parseInt(Math.random() * 255) + ')';
//end coloured particles
if (this.x > W/2 ){
this.vx = Math.random() * (-15 - -5) + -5;
}else{
this.vx = Math.random() * (15 - 5) + 5;
}
this.vy = Math.random() * (-32 - -25) + -25;
//we will call this function to actually draw the bouncing object at EVERY FRAME
this.draw = function() {
// Bouncing pictures were not bouncing because there were no this.x this.y . Shame on me.
ctx.drawImage(img,this.x,this.y);
// WORKING PARTICLES STARTS HERE
// ctx.fillStyle = this.color;
// ctx.beginPath();
// ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
// ctx.fill();
// ctx.closePath();
//WORKING PARTICLES ENDS HERE
};
}
function renderFrame() {
//RENDER THE PARTICLEEEEEEES!
requestAnimationFrame(renderFrame);
// Clearing screen to prevent trails
var W = canvas.width = window.innerWidth;
var H = canvas.height = window.innerHeight;
ctx.clearRect(0, 0, W, H);
particles.forEach(function(particle) {
// The bouncing objects simply go upwards
// It MUST come down, so lets apply gravity
particle.vy += 1;
// Adding velocity to x and y axis
particle.x += particle.vx;
particle.y += particle.vy;
// We're almost done! All we need to do now
// is to reposition the bouncing objects as soon
// as they move off the canvas.
// We'll also need to re-set the velocities
if (
// off the right side
particle.x + particle.radius > W ||
// off the left side
particle.x - particle.radius < 0 ||
// off the bottom
particle.y + particle.radius > H
) {
// If any of the above conditions are met
// then we need to re-position the bouncing objects
// on the base :)
// If we do not re-set the velocities then
// the bouncing objects will stick to base :D
// Velocity X
particle.x = parseInt(Math.random() * W);
particle.y = H;
if (particle.x > W/2 ){
particle.vx = Math.random() * (-15 - -5) + -5;
}else{
particle.vx = Math.random() * (15 - 5) + 5;
}
particle.vy = Math.random() * (-32 - -28) + -28;
}
particle.draw();
});
}
function beginAnimation(){
//create the particles and start to render them
for (var i = 0; i < particle_count; i++) {
particle = new Particle();
particles.push(particle);
}
//BOUNCE MOFOS!
renderFrame();
}
</script>
You're not waiting for troll1.png to load before trying to draw it.
Var img=new Image();
img.onload=function(){
beginMyAnimation();
}
img.src=”troll1”;
alert(“troll1 is not loaded yet.”);
function beginAnimation(){
….
ctx.drawImage(img,0,0);
….
}
The order of execution is like this:
First var img=new Image().
Javascript creates a new image object and puts a reference in img.
Second img.onload….
Javascript doesn’t execute this code yet. It just takes note that onload must be executed after troll1.jpg has successfully been loaded into the new image object.
Third img.src=”something.jpg”.
Javascript immediately begins loading troll1.jpg.
Since loading will take a while, Javascript also continues executing any code that follows.
Fourth alert(“Image is not loaded yet.”);
Javascript displays this alert message. Note that troll1.jpg has not been loaded yet. Therefore, any code that tried to use the image now would fail—no image yet!
Fifth img.onload.
Javascript has finally fully loaded troll1.jpg so it executes the onload function.
Sixth beginMyAnimation()
Javascript will finally execute beginAnimation(). At this point any code that tries to use the image will succeed. You can do ctx.drawImage(img,0,0) will succeed now.
So rearrange all your setup code inside in beginAnimation(). Finally, put renderFrame() last in beginAnimation().

Why I am geting "Uncaught RangeError: Maximum call stack size exceeded" error?

I implemented a small script to test requestAnimationFrame and it generates
"Uncaught RangeError: Maximum call stack size exceeded" error ..This is the code
<html>
<head>
<meta charset="utf-8">
<script>
window.onload=function(){
if (document.createElement("canvas").getContext){
//alert("browser supports canvas");
//console.log(document.getElementById("canvas").getContext);
canvas = document.getElementById("canvas");
shape = new shapes();
shape.drawball(canvas,5,"red");
}
};
function shapes(){
this.drawtriangle = function(canvas){
triangles = new triangle(0,0,0,200,200,200);
triangles.draw(canvas.getContext('2d'));
}
this.drawball = function(canvas,radius,color) {
ball = new Ball(radius,color);
ball.drawBall(canvas.getContext('2d'),canvas);
}
}
function coordinates(x1,y1){
this.x = x1;
this.y = y1;
}
function angle(angle){
this.angle = angle;
}
function Ball(radius,color){
this.origin = new coordinates(100,100);
this.radius = (radius === "undefined" ) ? 5 : radius;
this.color = (color === "undefined") ? red : color;
this.rotation = 0;
this.index = 0;
this.angles = new angle(0);
this.speed = 10;
}
Ball.prototype.drawBall = function (context,canvas){
context.fillStyle = this.color;
context.strokeStyle = "blue";
context.rotate(this.rotation);
context.beginPath();
context.arc(this.origin.x,this.origin.y,this.radius,0,(Math.PI*2),true);
context.closePath();
context.fill();
context.stroke(); this.animate(context,canvas);
}
Ball.prototype.animate = function (context,canvas){
var that = this;console.log(".......");
var time = new Date().getTime() * 0.002;
this.origin.x = Math.sin( time ) * 96 + 128;
this.origin.y = Math.cos( time * 0.9 ) * 96 + 128;
//context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + this.speed;
this.origin.y = this.origin.y + this.speed;
this.angles.angle = this.angles.angle + this.speed;
window.webkitrequestAnimationFrame(that.drawBall(context,canvas));
}
</script>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000px" height="1000px">
Your browser dows bot suppoet canvas
</canvas>
</body>
</html>
I got another code from the net and this works fine although it has recursion..
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>RequestAnimationFrame.js example</title>
</head>
<body>
<script >/**
* Provides requestAnimationFrame in a cross browser way.
* http://paulirish.com/2011/requestanimationframe-for-smart-animating/
*/
if ( !window.requestAnimationFrame ) {
window.requestAnimationFrame = ( function() {
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || // comment out if FF4 is slow (it caps framerate at ~30fps: https://bugzilla.mozilla.org/show_bug.cgi?id=630127)
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
window.setTimeout( callback, 1000 / 60 );
};
} )();
}</script>
<script>
var canvas, context;
init();
animate();
function init() {
canvas = document.createElement( 'canvas' );
canvas.width = 256;
canvas.height = 256;
context = canvas.getContext( '2d' );
document.body.appendChild( canvas );
}
function animate() {
requestAnimationFrame( animate );
draw();
}
function draw() {
var time = new Date().getTime() * 0.002;
var x = Math.sin( time ) * 96 + 128;
var y = Math.cos( time * 0.9 ) * 96 + 128;
context.fillStyle = 'rgb(245,245,245)';
context.fillRect( 0, 0, 255, 255 );
context.fillStyle = 'rgb(255,0,0)';
context.beginPath();
context.arc( x, y, 10, 0, Math.PI * 2, true );
context.closePath();
context.fill();
}
</script>
<div style="width:256px">
view source<br /><br/>
requestAnimationFrame() allows modern browsers to stop drawing graphics when a tab or window is not visible. Improving overall performance and batteries on mobile devices.<br /><br />
RequestAnimationFrame.js emulates the basic usage for old browsers.
</div>
</body>
</html>
What may be causing this error and how do I fix it ?
Ball.animate calls Ball.drawBall and vice versa. So, before either finishes executing, they call each other until the "call stack" size exceeds its limit (causing the error).
Instead of
ball.drawBall(canvas.getContext('2d'),canvas);
try
setInterval(function () { ball.animate(canvas.getContext('2d'),canvas); }, 1000/60);
and remove
this.animate(context,canvas);
from Ball.prototype.drawBall
There are many, many problems with your code, but that's the one you asked about.
I've explained the simple errors. My annotations are in /* */ comments. There are also formatting and style errors, which I've omitted.
<html>
<head>
<meta charset="utf-8">
<script>
window.onload=function(){
if (document.createElement("canvas").getContext){
//alert("browser supports canvas");
//console.log(document.getElementById("canvas").getContext);
canvas = document.getElementById("canvas");
shape = new shapes();
/* drawball should be called by a run() function, or similar */
shape.drawball(canvas,5,"red");
}
};
/**
* Is this supposed to be a singleton, or perhaps a module?
* otherwise, you should use prototype to define these methods
*/
function shapes(){
this.drawtriangle = function(canvas){
/* triangles isn't defined in this file? */
triangles = new triangle(0,0,0,200,200,200);
/* I'd store a reference to context as a property of the function (class)
* aka a "static variable" */
triangles.draw(canvas.getContext('2d'));
}
this.drawball = function(canvas,radius,color) {
ball = new Ball(radius,color);
/* same here */
ball.drawBall(canvas.getContext('2d'),canvas);
}
}
/**
* this is reasonable, but don't pluralize a class name unless you mean it
* I'd maybe call it "Point" or "Vec2d"
*/
function coordinates(x1,y1){
this.x = x1;
this.y = y1;
}
/* so you'd use this object as angle.angle?? Just store it as a scalar.*/
function angle(angle){
this.angle = angle;
}
/* This is correct, I'm sure it's also copy/pasted */
function Ball(radius,color){
this.origin = new coordinates(100,100);
this.radius = (radius === "undefined" ) ? 5 : radius;
this.color = (color === "undefined") ? red : color;
this.rotation = 0;
this.index = 0;
this.angles = new angle(0);
this.speed = 10;
}
/**
* Ball.drawBall()? I'd use Ball.draw()
* I'd again store context in the function object, and you don't need to
* pass canvas
*/
Ball.prototype.drawBall = function (context,canvas){
context.fillStyle = this.color;
context.strokeStyle = "blue";
context.rotate(this.rotation);
context.beginPath();
context.arc(this.origin.x,this.origin.y,this.radius,0,(Math.PI*2),true);
context.closePath();
context.fill();
context.stroke();
/* There is no reason to have your whole animation call here,
* there should only be code to _draw_ the _ball_ (like the function says) */
this.animate(context,canvas);
}
/* This makes sense for a singleton Ball, but in this case animate should be
* on a containing object. The Ball doesn't animate anything, the App does */
Ball.prototype.animate = function (context,canvas){
/* I can't explain this. I'm 99.999% sure it's not what you want. */
var that = this;console.log(".......");
var time = new Date().getTime() * 0.002;
this.origin.x = Math.sin( time ) * 96 + 128;
this.origin.y = Math.cos( time * 0.9 ) * 96 + 128;
//context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + this.speed;
this.origin.y = this.origin.y + this.speed;
this.angles.angle = this.angles.angle + this.speed;
/* you complete your cycle here (animate calls drawBall and vice versa) */
window.webkitrequestAnimationFrame(that.drawBall(context,canvas));
}
</script>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000px" height="1000px">
Your browser dows bot suppoet canvas
</canvas>
</body>
</html>

Canvas drawings, like lines, are blurry

I have a <div style="border:1px solid border;" /> and canvas, which is drawn using:
context.lineWidth = 1;
context.strokeStyle = "gray";
The drawing looks quite blurry (lineWidth less than one creates even worse picture), and nothing near to the div's border. Is it possible to get the same quality of drawing as HTML using canvas?
var ctx = document.getElementById("canvas").getContext("2d");
ctx.lineWidth = 1;
ctx.moveTo(2, 2);
ctx.lineTo(98, 2);
ctx.lineTo(98, 98);
ctx.lineTo(2, 98);
ctx.lineTo(2, 2);
ctx.stroke();
div {
border: 1px solid black;
width: 100px;
height: 100px;
}
canvas, div {background-color: #F5F5F5;}
canvas {border: 1px solid white;display: block;}
<table>
<tr><td>Line on canvas:</td><td>1px border:</td></tr>
<tr><td><canvas id="canvas" width="100" height="100"/></td><td><div> </div></td></tr>
</table>
I found that setting the canvas size in CSS caused my images to be displayed in a blurry manner.
Try this:
<canvas id="preview" width="640" height="260"></canvas>
as per my post: HTML Blurry Canvas Images
When drawing lines in canvas, you actually need to straddle the pixels. It was a bizarre choice in the API in my opinion, but easy to work with:
Instead of this:
context.moveTo(10, 0);
context.lineTo(10, 30);
Do this:
context.moveTo(10.5, 0);
context.lineTo(10.5, 30);
Dive into HTML5's canvas chapter talks about this nicely
Even easier fix is to just use this:
context = canvas.context2d;
context.translate(0.5, 0.5);
From here on out your coordinates should be adjusted by that 0.5 pixel.
I use a retina display and I found a solution that worked for me here.
Small recap :
First you need to set the size of your canvas twice as large as you want it, for example :
canvas = document.getElementById('myCanvas');
canvas.width = 200;
canvas.height = 200;
Then using CSS you set it to the desired size :
canvas.style.width = "100px";
canvas.style.height = "100px";
And finally you scale the drawing context by 2 :
const dpi = window.devicePixelRatio;
canvas.getContext('2d').scale(dpi, dpi);
The Mozilla website has example code for how to apply the correct resolution in a canvas:
https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Set display size (css pixels).
var size = 200;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
// Set actual size in memory (scaled to account for extra pixel density).
var scale = window.devicePixelRatio; // Change to 1 on retina screens to see blurry canvas.
canvas.width = size * scale;
canvas.height = size * scale;
// Normalize coordinate system to use css pixels.
ctx.scale(scale, scale);
ctx.fillStyle = "#bada55";
ctx.fillRect(10, 10, 300, 300);
ctx.fillStyle = "#ffffff";
ctx.font = '18px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
var x = size / 2;
var y = size / 2;
var textString = "I love MDN";
ctx.fillText(textString, x, y);
<canvas id="canvas"></canvas>
Lines are blurred because the canvas virtual size is zoomed to its HTML element actual size. To overcome this issue you need to adjust canvas virtual size before drawing:
function Draw () {
var e, surface;
e = document.getElementById ("surface");
/* Begin size adjusting. */
e.width = e.offsetWidth;
e.height = e.offsetHeight;
/* End size adjusting. */
surface = e.getContext ("2d");
surface.strokeRect (10, 10, 20, 20);
}
window.onload = Draw ()
<!DOCTYPE html>
<html>
<head>
<title>Canvas size adjusting demo</title>
</head>
<body>
<canvas id="surface"></canvas>
</body>
</html>
HTML:
Ok, I've figured this out once and for all. You need to do two things:
place any lines on 0.5 px. Refer to this, which provides a great explanation:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors#A_lineWidth_example
There are essentially two heights and two widths associated with the canvas. There is the canvas height and width and then there is the css style height and width of the element. These need to be in sync.
To do this, you need to calculate the css height and width as:
var myCanvasEl = document.getElementById('myCanvas');
var ctx = myCanvasEl.getContext('2d');
myCanvasEl.style.height = myCanvasEl.height / window.devicePixelRatio + "px";
myCanvasEl.style.width = myCanvasEl.width / window.devicePixelRatio + "px";
where myCanvasEl.style.height and myCanvasEl.style.widthis the css styling height and width of the element, while myCanvasEl.height and myCanvasEl.width is the height and width of the canvas.
OLD ANSWER (superseded by above):
This is the best solution I've found in 2020. Notice I've multiplied the devicePixelRatio by 2:
var size = 100;
var scale = window.devicePixelRatio*2;
context.width = size * scale;
cartesian_001El.style.height = cartesian_001El.height / window.devicePixelRatio + "px";
cartesian_001El.style.height = cartesian_001El.height / window.devicePixelRatio + "px";
context.height = size * scale;
context.scale(scale, scale);
Something else that nobody talked about here when images are scaled (which was my issue) is imageSmoothingEnabled.
The imageSmoothingEnabled property of the CanvasRenderingContext2D interface, part of the Canvas API, determines whether scaled images are smoothed (true, default) or not (false). On getting the imageSmoothingEnabled property, the last value it was set to is returned.
This property is useful for games and other apps that use pixel art. When enlarging images, the default resizing algorithm will blur the pixels. Set this property to false to retain the pixels' sharpness.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled
To disable it, simply set the properity to false:
ctx.imageSmoothingEnabled = false;
canvas.width=canvas.clientWidth
canvas.height=canvas.clientHeight
To avoid this issue in animation I would like to share a small demo.
Basically I am checking increment values each time & jumping in a set of 1px by removing float values.
HTML:
<canvas id="canvas" width="600" height="600"></canvas>
CSS:
html, body{
height: 100%;
}
body{
font-family: monaco, Consolas,"Lucida Console", monospace;
background: #000;
}
canvas{
position: fixed;
top: 0;
left: 0;
transform: translateZ(0);
}
JS:
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
ctx.translate(0.5, 0.5);
var i = 0;
var iInc = 0.005;
var range = 0.5;
raf = window.requestAnimationFrame(draw);
function draw() {
var animInc = EasingFunctions.easeInQuad(i) * 250;
ctx.clearRect(0, 0, 600, 600);
ctx.save();
ctx.beginPath();
ctx.strokeStyle = '#fff';
var rectInc = 10 + animInc;
// Avoid Half Pixel
rectIncFloat = rectInc % 1; // Getting decimal value.
rectInc = rectInc - rectIncFloat; // Removing decimal.
// console.log(rectInc);
ctx.rect(rectInc, rectInc, 130, 60);
ctx.stroke();
ctx.closePath();
ctx.font = "14px arial";
ctx.fillStyle = '#fff';
ctx.textAlign = 'center';
ctx.fillText("MAIN BUTTON", 65.5 + rectInc, 35.5 + rectInc);
i += iInc;
if (i >= 1) {
iInc = -iInc;
}
if (i <= 0) {
iInc = Math.abs(iInc);
}
raf = window.requestAnimationFrame(draw);
}
// Easing
EasingFunctions = {
// no easing, no acceleration
linear: function(t) {
return t
},
// accelerating from zero velocity
easeInQuad: function(t) {
return t * t
},
// decelerating to zero velocity
easeOutQuad: function(t) {
return t * (2 - t)
},
// acceleration until halfway, then deceleration
easeInOutQuad: function(t) {
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t
},
// accelerating from zero velocity
easeInCubic: function(t) {
return t * t * t
},
// decelerating to zero velocity
easeOutCubic: function(t) {
return (--t) * t * t + 1
},
// acceleration until halfway, then deceleration
easeInOutCubic: function(t) {
return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
},
// accelerating from zero velocity
easeInQuart: function(t) {
return t * t * t * t
},
// decelerating to zero velocity
easeOutQuart: function(t) {
return 1 - (--t) * t * t * t
},
// acceleration until halfway, then deceleration
easeInOutQuart: function(t) {
return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t
},
// accelerating from zero velocity
easeInQuint: function(t) {
return t * t * t * t * t
},
// decelerating to zero velocity
easeOutQuint: function(t) {
return 1 + (--t) * t * t * t * t
},
// acceleration until halfway, then deceleration
easeInOutQuint: function(t) {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
}
}
A related issue could be that you're setting the <canvas>'s height and width from CSS or other sources. I'm guessing it scales the canvas and associated drawings. Setting the <canvas> size using the height and width property (either from the HTML tag or a JS script) resolved the error for me.
Here is my solution: set width and height for canvas
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Also set in css, so it will not overflow from its parent
canvas {
width: 100%
height: 100%
}
Although LittleJoe's solution worked perfect on desktop it didn't work on mobile because on iphone 11 pro for example the dpi is 3 so I had to set width/height based on dpi. At the end it worked:
let width = 100, height = 100;
const dpi = window.devicePixelRatio;
canvas = document.getElementById('myCanvas');
canvas.width = width * dpi;
canvas.height = height * dpi;
canvas.style.width = width + "px";
canvas.style.height = width + "px";
canvas.getContext('2d').scale(dpi, dpi);
in order to get rid of the blurryness you need to set the size of the canvas in two manners:
first withcanvas.width = yourwidthhere;
and canvas.height = yourheighthere;
second by setting the css attribute either by js or a stylesheet
HTML:
<canvas class="canvas_hangman"></canvas>
JS:
function setUpCanvas() {
canvas = document.getElementsByClassName("canvas_hangman")[0];
ctx = canvas.getContext('2d');
ctx.translate(0.5, 0.5);
// Set display size (vw/vh).
var sizeWidth = 80 * window.innerWidth / 100,
sizeHeight = 100 * window.innerHeight / 100 || 766;
// console.log(sizeWidth, sizeHeight);
// Setting the canvas height and width to be responsive
canvas.width = sizeWidth;
canvas.height = sizeHeight;
canvas.style.width = sizeWidth;
canvas.style.height = sizeHeight;
}
window.onload = setUpCanvas();
This perfectly sets up your HTML canvas to draw on, and in a responsive manner too :)

Html5 animation

i was searching how to flip pages of e-book with html5 and i get this example
but i was wonder if the book have 300 page i will do with the same way???
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link type="text/css" href="main.css" rel="stylesheet" media="screen" />
</head>
<body>
<div id="book">
<canvas id="pageflip-canvas"></canvas>
<div id="pages">
<section>
<div>
<h2>History</h2>
<p>Canvas was initially introduced by Apple for use inside their own Mac OS X WebKit component, powering applications like Dashboard widgets and the Safari browser. Later, it was adopted by Gecko browsers and Opera and standardized by the WHATWG on new proposed specifications for next generation web technologies.</p>
</div>
</section>
<section>
<div>
<h2>Usage</h2>
<p>Canvas consists of a drawable region defined in HTML code with height and width attributes. JavaScript code may access the area through a full set of drawing functions similar to other common 2D APIs, thus allowing for dynamically generated graphics. Some anticipated uses of canvas include building graphs, animations, games, and image composition.</p>
</div>
</section>
<section>
<div>
<h2>Reactions</h2>
<p>At the time of its introduction the canvas element was met with mixed reactions from the web standards community. There have been arguments against Apple's decision to create a new proprietary element instead of supporting the SVG standard. There are other concerns about syntax e.g. the absence of a namespace.</p>
</div>
</section>
<section>
<div>
<h2>Support</h2>
<p>The element is currently supported by the latest versions of Mozilla Firefox, Google Chrome, Safari, and Opera. It is not natively implemented by Internet Explorer (IE) as of version 8, though support is in development for Internet Explorer 9; however, many of the Canvas element's features can be supported in IE, for example by using Google or Mozilla plugins, JavaScript libraries and either Adobe Flash or IE's proprietary VML.</p>
</div>
</section>
</div>
</div>
<script type="text/javascript" src="pageflip.js"></script>
</body>
<html>
here the jquery
(function() {
// Dimensions of the whole book
var BOOK_WIDTH = 830;
var BOOK_HEIGHT = 260;
// Dimensions of one page in the book
var PAGE_WIDTH = 400;
var PAGE_HEIGHT = 250;
// Vertical spacing between the top edge of the book and the papers
var PAGE_Y = ( BOOK_HEIGHT - PAGE_HEIGHT ) / 2;
// The canvas size equals to the book dimensions + this padding
var CANVAS_PADDING = 60;
var page = 0;
var canvas = document.getElementById( "pageflip-canvas" );
var context = canvas.getContext( "2d" );
var mouse = { x: 0, y: 0 };
var flips = [];
var book = document.getElementById( "book" );
// List of all the page elements in the DOM
var pages = book.getElementsByTagName( "section" );
// Organize the depth of our pages and create the flip definitions
for( var i = 0, len = pages.length; i < len; i++ ) {
pages[i].style.zIndex = len - i;
flips.push( {
// Current progress of the flip (left -1 to right +1)
progress: 1,
// The target value towards which progress is always moving
target: 1,
// The page DOM element related to this flip
page: pages[i],
// True while the page is being dragged
dragging: false
} );
}
// Resize the canvas to match the book size
canvas.width = BOOK_WIDTH + ( CANVAS_PADDING * 2 );
canvas.height = BOOK_HEIGHT + ( CANVAS_PADDING * 2 );
// Offset the canvas so that it's padding is evenly spread around the book
canvas.style.top = -CANVAS_PADDING + "px";
canvas.style.left = -CANVAS_PADDING + "px";
// Render the page flip 60 times a second
setInterval( render, 1000 / 60 );
document.addEventListener( "mousemove", mouseMoveHandler, false );
document.addEventListener( "mousedown", mouseDownHandler, false );
document.addEventListener( "mouseup", mouseUpHandler, false );
function mouseMoveHandler( event ) {
// Offset mouse position so that the top of the book spine is 0,0
mouse.x = event.clientX - book.offsetLeft - ( BOOK_WIDTH / 2 );
mouse.y = event.clientY - book.offsetTop;
}
function mouseDownHandler( event ) {
// Make sure the mouse pointer is inside of the book
if (Math.abs(mouse.x) < PAGE_WIDTH) {
if (mouse.x < 0 && page - 1 >= 0) {
// We are on the left side, drag the previous page
flips[page - 1].dragging = true;
}
else if (mouse.x > 0 && page + 1 < flips.length) {
// We are on the right side, drag the current page
flips[page].dragging = true;
}
}
// Prevents the text selection
event.preventDefault();
}
function mouseUpHandler( event ) {
for( var i = 0; i < flips.length; i++ ) {
// If this flip was being dragged, animate to its destination
if( flips[i].dragging ) {
// Figure out which page we should navigate to
if( mouse.x < 0 ) {
flips[i].target = -1;
page = Math.min( page + 1, flips.length );
}
else {
flips[i].target = 1;
page = Math.max( page - 1, 0 );
}
}
flips[i].dragging = false;
}
}
function render() {
// Reset all pixels in the canvas
context.clearRect( 0, 0, canvas.width, canvas.height );
for( var i = 0, len = flips.length; i < len; i++ ) {
var flip = flips[i];
if( flip.dragging ) {
flip.target = Math.max( Math.min( mouse.x / PAGE_WIDTH, 1 ), -1 );
}
// Ease progress towards the target value
flip.progress += ( flip.target - flip.progress ) * 0.2;
// If the flip is being dragged or is somewhere in the middle of the book, render it
if( flip.dragging || Math.abs( flip.progress ) < 0.997 ) {
drawFlip( flip );
}
}
}
function drawFlip( flip ) {
// Strength of the fold is strongest in the middle of the book
var strength = 1 - Math.abs( flip.progress );
// Width of the folded paper
var foldWidth = ( PAGE_WIDTH * 0.5 ) * ( 1 - flip.progress );
// X position of the folded paper
var foldX = PAGE_WIDTH * flip.progress + foldWidth;
// How far the page should outdent vertically due to perspective
var verticalOutdent = 20 * strength;
// The maximum width of the left and right side shadows
var paperShadowWidth = ( PAGE_WIDTH * 0.5 ) * Math.max( Math.min( 1 - flip.progress, 0.5 ), 0 );
var rightShadowWidth = ( PAGE_WIDTH * 0.5 ) * Math.max( Math.min( strength, 0.5 ), 0 );
var leftShadowWidth = ( PAGE_WIDTH * 0.5 ) * Math.max( Math.min( strength, 0.5 ), 0 );
// Change page element width to match the x position of the fold
flip.page.style.width = Math.max(foldX, 0) + "px";
context.save();
context.translate( CANVAS_PADDING + ( BOOK_WIDTH / 2 ), PAGE_Y + CANVAS_PADDING );
// Draw a sharp shadow on the left side of the page
context.strokeStyle = 'rgba(0,0,0,'+(0.05 * strength)+')';
context.lineWidth = 30 * strength;
context.beginPath();
context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5);
context.lineTo(foldX - foldWidth, PAGE_HEIGHT + (verticalOutdent * 0.5));
context.stroke();
// Right side drop shadow
var rightShadowGradient = context.createLinearGradient(foldX, 0, foldX + rightShadowWidth, 0);
rightShadowGradient.addColorStop(0, 'rgba(0,0,0,'+(strength*0.2)+')');
rightShadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.0)');
context.fillStyle = rightShadowGradient;
context.beginPath();
context.moveTo(foldX, 0);
context.lineTo(foldX + rightShadowWidth, 0);
context.lineTo(foldX + rightShadowWidth, PAGE_HEIGHT);
context.lineTo(foldX, PAGE_HEIGHT);
context.fill();
// Left side drop shadow
var leftShadowGradient = context.createLinearGradient(foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0);
leftShadowGradient.addColorStop(0, 'rgba(0,0,0,0.0)');
leftShadowGradient.addColorStop(1, 'rgba(0,0,0,'+(strength*0.15)+')');
context.fillStyle = leftShadowGradient;
context.beginPath();
context.moveTo(foldX - foldWidth - leftShadowWidth, 0);
context.lineTo(foldX - foldWidth, 0);
context.lineTo(foldX - foldWidth, PAGE_HEIGHT);
context.lineTo(foldX - foldWidth - leftShadowWidth, PAGE_HEIGHT);
context.fill();
// Gradient applied to the folded paper (highlights & shadows)
var foldGradient = context.createLinearGradient(foldX - paperShadowWidth, 0, foldX, 0);
foldGradient.addColorStop(0.35, '#fafafa');
foldGradient.addColorStop(0.73, '#eeeeee');
foldGradient.addColorStop(0.9, '#fafafa');
foldGradient.addColorStop(1.0, '#e2e2e2');
context.fillStyle = foldGradient;
context.strokeStyle = 'rgba(0,0,0,0.06)';
context.lineWidth = 0.5;
// Draw the folded piece of paper
context.beginPath();
context.moveTo(foldX, 0);
context.lineTo(foldX, PAGE_HEIGHT);
context.quadraticCurveTo(foldX, PAGE_HEIGHT + (verticalOutdent * 2), foldX - foldWidth, PAGE_HEIGHT + verticalOutdent);
context.lineTo(foldX - foldWidth, -verticalOutdent);
context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0);
context.fill();
context.stroke();
context.restore();
}
})();
the css
body, h2, p {
margin: 0;
padding: 0;
}
body {
background-color: #444;
color: #333;
font-family: Helvetica, sans-serif;
}
#book {
background: url("book.png") no-repeat;
position: absolute;
width: 830px;
height: 260px;
left: 50%;
top: 50%;
margin-left: -400px;
margin-top: -125px;
}
#pages section {
background: url("paper.png") no-repeat;
display: block;
width: 400px;
height: 250px;
position: absolute;
left: 415px;
top: 5px;
overflow: hidden;
}
#pages section>div {
display: block;
width: 400px;
height: 250px;
font-size: 12px;
}
#pages section p,
#pages section h2 {
padding: 3px 35px;
line-height: 1.4em;
text-align: justify;
}
#pages section h2{
margin: 15px 0 10px;
}
#pageflip-canvas {
position: absolute;
z-index: 100;
}
You can load the pages dynamically through AJAX in the event handler, this way the user is not forced to download the whole document.