Commit df8b7559 authored by O'Reilly Media, Inc's avatar O'Reilly Media, Inc
Browse files

Initial commit

parents
## Example files for the title:
# Canvas Pocket Reference, by David Flanagan
[![Canvas Pocket Reference, by David Flanagan](http://akamaicovers.oreilly.com/images/9781449396800/cat.gif)](https://www.safaribooksonline.com/library/view/title/9781449398941//)
The following applies to example files from material published by O’Reilly Media, Inc. Content from other publishers may include different rules of usage. Please refer to any additional usage rights explained in the actual example files or refer to the publisher’s website.
O'Reilly books are here to help you get your job done. In general, you may use the code in O'Reilly books in your programs and documentation. You do not need to contact us for permission unless you're reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from our books does not require permission. Answering a question by citing our books and quoting example code does not require permission. On the other hand, selling or distributing a CD-ROM of examples from O'Reilly books does require permission. Incorporating a significant amount of example code from our books into your product's documentation does require permission.
We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN.
If you think your use of code examples falls outside fair use or the permission given here, feel free to contact us at <permissions@oreilly.com>.
Please note that the examples are not production code and have not been carefully testing. They are provided "as-is" and come with no warranty of any kind.
These files are the examples from the book Canvas Pocket Reference
(ISBN: 978-1-449-39680-0) by David Flanagan. The examples are copyright
2011 by O'Reilly Media. You can use them under the terms of O'Reilly's
code reuse policy:
This book is here to help you get your job done. In general, you
may use the code in this book in your programs and
documentation. You do not need to contact us for permission unless
you're reproducing a significant portion of the code. For example,
writing a program that uses several chunks of code from this book
does not require permission. Selling or distributing a CD-ROM of
examples from O'Reilly books does require permission. Answering a
question by citing this book and quoting example code does not
require permission. Incorporating a significant amount of example
code from this book into your product's documentation does require
permission.
We appreciate, but do not require, attribution. An attribution
usually includes the title, author, publisher, and ISBN. For
example: "From _Canvas Pocket Reference_, by David
Flanagan. Copyright 2011 O'Reilly Media, Inc., 978-1-449-39680-0".
If you feel your use of code examples falls outside fair use of
the permission given above, feel free to contact us at
permissions@oreilly.com.
The numbered examples in the book are in files whose names begin with
"example". There are a few places in the book where useful chunks of
code appear without example numbers. Those chunks are in files whose
name begins with "page".
I hope these examples are helpful to you!
David Flanagan
// Define a regular polygon with n sides, centered at (x,y)
// with radius r. The vertices are equally spaced along the
// circumference of a circle. Put the first vertex straight
// up or at the specified angle. Rotate clockwise, unless
// the last argument is true.
function polygon(c,n,x,y,r,angle,counterclockwise) {
angle = angle || 0;
counterclockwise = counterclockwise || false;
// Compute vertex position and begin a subpath there
c.moveTo(x + r*Math.sin(angle),
y - r*Math.cos(angle));
var delta = 2*Math.PI/n; // Angle between vertices
for(var i = 1; i < n; i++) { // For remaining vertices
// Compute angle of this vertex
angle += counterclockwise?-delta:delta;
// Compute position of vertex and add a line to it
c.lineTo(x + r*Math.sin(angle),
y - r*Math.cos(angle));
}
c.closePath(); // Connect last vertex back to the first
}
// Start a new path and add polygon subpaths
c.beginPath();
polygon(c, 3, 75, 100, 50); // Triangle
polygon(c, 4, 225, 100, 50, Math.PI/4); // Square
polygon(c, 5, 375, 100, 50); // Pentagon
polygon(c, 6, 525, 100, 50, Math.PI/6); // Hexagon
// Add a small counterclockwise square inside the hexagon
polygon(c, 4, 525, 100, 20, Math.PI/4, true);
// Set properties that control how the graphics will look
c.fillStyle = "#ccc"; // Light-gray interiors
c.strokeStyle = "#008"; // outlined with dark-blue lines
c.lineWidth = 5; // five pixels wide.
// Now draw all the polygons (each in its own subpath)
c.fill(); // Fill the shapes
c.stroke(); // And stroke their outlines
// Returns true if the specified mouse event is over a
// non-transparent pixel.
function hitpaint(context, event) {
// Convert mouse event coordinates to canvas coordinates
var canvas = context.canvas;
var bb = canvas.getBoundingClientRect();
var x=(event.clientX-bb.left)*(canvas.width/bb.width);
var y=(event.clientY-bb.top)*(canvas.height/bb.height);
// Get the pixel (or pixels if multiple device pixels
// map to 1 CSS pixel) at those coordinates
var pixels = c.getImageData(x,y,1,1);
// If any pixels have a non-zero alpha, return true
for(var i = 3; i < pixels.data.length; i+=4) {
if (pixels.data[i] !== 0) return true;
}
// Otherwise it was a miss.
return false;
}
/*
* Find all elements of CSS class "sparkline", parse their
* content as a series of numbers, and replace it with a
* graphical representation.
*
* Define sparklines with markup like this:
* <span class="sparkline">3 5 7 6 6 9 11 15</span>
*
* Style sparklines with CSS like this:
* .sparkline { background-color: #ddd; color: red; }
*
* - Sparkline color is from the computed style of the CSS
* color property.
* - Sparklines are transparent, so the normal background
* color shows through.
* - Sparkline height is from the data-height attribute if
* defined or from the computed style for the font-size
* otherwise.
* - Sparkline width is from the data-width attribute if it
* is defined or the number of data points times data-dx
* if that is defined or the number of data points times
* the height divided by 6
* - The minimum and maximum values of the y axis are taken
* from the data-ymin and data-ymax attributes if they
* are defined, and otherwise come from the minimum and
* maximum values of the data.
*/
// Run this code when the document first loads
window.addEventListener("load", function() {
// Find all elements of class "sparkline"
var elts = document.getElementsByClassName("sparkline");
// Loop through those elements
main: for(var e = 0; e < elts.length; e++) {
var elt = elts[e];
// Get content of the element and convert to an
// array of numbers. If the conversion fails, skip
// this element.
var content = elt.textContent || elt.innerText;
// Trim leading and trailing whitespace
var content = content.replace(/^\s+|\s+$/g, "");
// Remove comments
var text = content.replace(/#.*$/gm, "");
// Convert newlines, etc., to spaces
text = text.replace(/[\n\r\t\v\f]/g, " ");
// Split numbers on commas or spaces
var data = text.split(/\s+|\s*,\s*/);
// For each split piece of the string
for(var i = 0; i < data.length; i++) {
data[i] = Number(data[i]); // Convert to number
if (isNaN(data[i])) // On failure
continue main; // skip this elt.
}
// Now compute the color, width, height, and y axis
// bounds of the sparkline from the data, from data-
// attributes of the element, and from the computed
// style of the element
var style = getComputedStyle(elt, null);
var color = style.color;
var height =
parseInt(elt.getAttribute("data-height")) ||
parseInt(style.fontSize) || 20;
var datadx = parseInt(elt.getAttribute("data-dx"));
var width =
parseInt(elt.getAttribute("data-width")) ||
data.length*(datadx || height/6);
var ymin =
parseInt(elt.getAttribute("data-ymin")) ||
Math.min.apply(Math, data);
var ymax =
parseInt(elt.getAttribute("data-ymax")) ||
Math.max.apply(Math, data);
if (ymin >= ymax) ymax = ymin + 1;
// Create the canvas element
var canvas = document.createElement("canvas");
canvas.width = width; // Set canvas dimensions
canvas.height = height;
// Use the element content as a tooltip
canvas.title = content;
elt.innerHTML = ""; // Erase existing content
elt.appendChild(canvas); // Insert canvas into elt
// Now plot the points in the canvas
var context = canvas.getContext('2d');
for(var i = 0; i < data.length; i++) {
// Transform (i,data[i]) to canvas coordinates
var x = width*i/data.length;
var y = (ymax-data[i])*height/(ymax-ymin);
// Draw a line to (x,y). Note that the first
// call to lineTo() does a moveTo() instead.
context.lineTo(x,y);
}
context.strokeStyle = color; // Specify color
context.stroke(); // and draw it
}
}, false); // last argument to addEventListener()
// Revert to the last saved graphics state,
// without popping the stack of saved states.
CanvasRenderingContext2D.prototype.revert = function() {
this.restore(); // Restore the old graphics state
this.save(); // Save it again so we can go back to it
return this; // Allow method chaining
};
// Set the graphics attributes specified by the properties
// of the object o. Or, if no argument is passed, return
// the current attributes as an object. Note that this does
// not handle the transformation or clipping region.
CanvasRenderingContext2D.prototype.attrs = function(o) {
if (o) {
for(var a in o) // For each property in o
this[a] = o[a]; // Set it as an attribute
return this; // Enable method chaining
}
else return {
fillStyle: this.fillStyle,
font: this.font,
globalAlpha: this.globalAlpha,
globalCompositeOperation:
this.globalCompositeOperation,
lineCap: this.lineCap,
lineJoin: this.lineJoin,
lineWidth: this.lineWidth,
miterLimit: this.miterLimit,
textAlign: this.textAlign,
textBaseline: this.textBaseline,
shadowBlur: this.shadowBlur,
shadowColor: this.shadowColor,
shadowOffsetX: this.shadowOffsetX,
shadowOffsetY: this.shadowOffsetY,
strokeStyle: this.strokeStyle
};
};
var deg = Math.PI/180; // For converting degrees to radians
// Draw a level-n Koch Snowflake fractal in the context c,
// with lower-left corner at (x,y) and side length len.
function snowflake(c, n, x, y, len) {
c.save(); // Save current transformation
c.translate(x,y); // Translate to starting point
c.moveTo(0,0); // Begin a new subpath there
leg(n); // Draw the first leg of the fractal
c.rotate(-120*deg); // Rotate 120 degrees anticlockwise
leg(n); // Draw the second leg
c.rotate(-120*deg); // Rotate again.
leg(n); // Draw the final leg
c.closePath(); // Close the subpath
c.restore(); // Restore original transformation
// Draw a single leg of a level-n Koch snowflake.
// This function leaves the current point at the end of
// the leg it has drawn and translates the coordinate
// system so the current point is (0,0). This means you
// can easily call rotate() after drawing a leg.
function leg(n) {
c.save(); // Save current transform
if (n == 0) { // Non-recursive case:
c.lineTo(len, 0); // Just a horizontal line
}
else { // Recursive case: _ _
// draw 4 sub-legs like: \/
c.scale(1/3,1/3); // Sub-legs are 1/3rd size
leg(n-1); // Draw the first sub-leg
c.rotate(60*deg); // Turn 60 degrees clockwise
leg(n-1); // Draw the second sub-leg
c.rotate(-120*deg); // Rotate 120 degrees back
leg(n-1); // Third sub-leg
c.rotate(60*deg); // Back to original heading
leg(n-1); // Final sub-leg
}
c.restore(); // Restore the transform
c.translate(len, 0); // Translate to end of leg
}
}
// Draw snowflake fractals of level 0 through 4
snowflake(c,0,25,125,125); // Equilateral triangle
snowflake(c,1,175,125,125); // A 6-sided star
snowflake(c,2,325,125,125); // Kind of a snowflake
snowflake(c,3,475,125,125); // More snowflake-like
snowflake(c,4,625,125,125); // This looks really fractal!
c.stroke(); // Stroke this complicated path
// A utility function to convert from degrees to radians
function rads(x) { return Math.PI*x/180; }
// Draw a circle. Scale and rotate if you want an ellipse
// instead. There is no current point, so this draws just
// the circle with no straight line from the current point
// to the start of the circle.
c.beginPath();
c.arc(75,100,50, // Center at (75,100), radius 50
0,rads(360),false); // Go clockwise from 0 to 360°
// Draw a wedge. Angles are measured clockwise from the
// positive x axis. Note that arc() adds a line from the
// current point to the arc start.
c.moveTo(200, 100); // Start at center of the circle
c.arc(200, 100, 50, // Circle center and radius
rads(-60), rads(0), // start at -60° and go to 0°
false); // false means clockwise
c.closePath(); // Go back to the circle center
// Same wedge, opposite direction
c.moveTo(325, 100);
c.arc(325, 100, 50, rads(-60), rads(0), true);
c.closePath();
// Use arcTo() for rounded corners. Here we draw a square
// with upper-left at (400,50) and various corner radii.
c.moveTo(450, 50); // Begin in middle of the top
c.arcTo(500,50,500,150,30); // Top and upper-right corner
c.arcTo(500,150,400,150,20); // Right and lower-right corner
c.arcTo(400,150,400,50,10); // Bottom and lower-left corner
c.arcTo(400,50,500,50,0); // Left and upper-left corner
c.closePath(); // The rest of the top edge
// Quadratic Bezier curve: one control point
c.moveTo(75, 250); // Begin at (75,250)
c.quadraticCurveTo(100,200, 175, 250); // Curve to (175,250)
c.fillRect(100-3,200-3,6,6); // Mark the control point
// Cubic Bezier curve: two control points
c.moveTo(200, 250); // Start point
c.bezierCurveTo(220,220,280,280,300,250); // To (300,250)
c.fillRect(220-3,220-3,6,6); // Mark control points
c.fillRect(280-3,280-3,6,6);
// Define some graphics attributes and draw the curves
c.fillStyle = "#aaa"; // Gray fills
c.lineWidth = 5; // 5-pixel black (by default) lines
c.fill(); // Fill the curves
c.stroke(); // Stroke their outlines
// Define some drawing attributes
c.font = "bold 60pt sans-serif"; // Big font
c.lineWidth = 2; // Narrow lines
c.strokeStyle = "#000"; // Black lines
// Outline a rectangle and some text
c.strokeRect(175, 25, 50, 325); // Vertical stripe
c.strokeText("<canvas>", 15, 330); // Text outline
// Define a complex path with an interior that is outside.
polygon(c,3,200,225,200); // Large triangle
polygon(c,3,200,225,100,0,true); // Small reverse triangle
// Make that path the clipping region.
c.clip();
// Stroke the path with a 5 pixel line,
// entirely inside the clipping region.
c.lineWidth = 10; // Half of this line will be clipped away
c.stroke();
// Fill the parts of the rectangle and text
// that are inside the clipping region
c.fillStyle = "#aaa" // Light gray
c.fillRect(175, 25, 50, 325); // Fill vertical stripe
c.fillStyle = "#888" // Darker gray
c.fillText("<canvas>", 15, 330); // Fill text
// Define a subtle shadow
c.shadowColor = "rgba(100,100,100,.4)"; // Transparent gray
c.shadowOffsetX = c.shadowOffsetY = 3; // Slight offset
c.shadowBlur = 5; // Soften edges
// Draw some text and a blue box using that shadow
c.lineWidth = 10;
c.strokeStyle = "blue";
c.strokeRect(100, 100, 300, 200); // Draw a box
c.font = "Bold 36pt Helvetica";
c.fillText("Hello World", 115, 225); // Draw some text
// Define a less subtle shadow. Larger offset makes items
// "float" higher. Note how the shadow overlaps the box.
c.shadowOffsetX = c.shadowOffsetY = 20; // Large offsets
c.shadowBlur = 10; // Softer edges
c.fillStyle = "red"; // Draw a solid red rectangle
c.fillRect(50,25,200,65); // that floats above the blue box
// Draw a line in the upper left
c.moveTo(5,5);
c.lineTo(45,45);
c.lineWidth = 8;
c.lineCap = "round";
c.stroke();
// Define a transformation
c.translate(50,100);
c.rotate(-45*Math.PI/180); // Straighten out the line
c.scale(10,10); // Scale so we can see the pixels
// Use drawImage() to copy the line
c.drawImage(c.canvas, // Copy from canvas to itself
0, 0, 50, 50, // untransformed source rectangle
0, 0, 50, 50); // transformed destination
// Smear the pixels of the rectangle to the right, producing
// a sort of motion blur as if objects are moving from right
// to left. n must be 2 or larger. Larger values produce
// bigger smears. The rectangle is specified in the default
// coordinate system.
function smear(c, n, x, y, w, h) {
// Get the ImageData object that represents the
// rectangle of pixels to smear
var pixels = c.getImageData(x,y,w,h);
// This smear is done in-place and requires only the
// source ImageData. Some image processing algorithms
// require an additional ImageData to store transformed
// pixel values. If we needed an output buffer, we could
// create a new ImageData with the same dimensions like
// this: var output_pixels = c.createImageData(pixels);
// These dimensions may be different than the w and h
// arguments: there may be more than one device pixel
// per CSS pixel.
var width = pixels.width, height = pixels.height;
// This is the byte array that holds the raw pixel data,
// left-to-right and top-to-bottom. Each pixel occupies
// 4 consecutive bytes in R,G,B,A order.
var data = pixels.data;
// Each pixel after the first in each row is smeared by
// replacing it with 1/nth of its own value plus m/nths
// of the previous pixel's value
var m = n-1;
for(var row = 0; row < height; row++) { // For each row
// Compute offset of the second pixel of the row
var i = row*width*4 + 4;
// For each pixel in the row, starting at the second
for(var col = 1; col < width; col++, i += 4) {
data[i] = (data[i]+data[i-4]*m)/n; // Red
data[i+1] = (data[i+1]+data[i-3]*m)/n; // Green
data[i+2] = (data[i+2]+data[i-2]*m)/n; // Blue
data[i+3] = (data[i+3]+data[i-1]*m)/n; // Alpha
}
}
// Now copy the smeared image data back to the canvas
c.putImageData(pixels, x, y);
}
// Returns true if the mouse event is over the current path
// in the specified CanvasRenderingContext2D object.
function hitpath(context, event) {
var canvas, bb, x, y;
// Get <canvas> element from the context object
canvas = context.canvas;
// Get canvas size and position
bb = canvas.getBoundingClientRect();
// Convert mouse event coordinates to canvas coordinates
x = (event.clientX-bb.left) * (canvas.width/bb.width);
y = (event.clientY-bb.top) * (canvas.height/bb.height);
// Call isPointInPath with these transformed coordinates
return context.isPointInPath(x,y);
}
// Shear transform:
// x' = x + kx*y;
// y' = y + ky*x;
function shear(c, kx, ky) {
c.transform(1, ky, kx, 1, 0, 0);
}
// Rotate theta radians clockwise around (x,y).
// This can also be accomplished with a translate,
// rotate, translate back sequence of transformations.
function rotateAbout(c, theta, x, y) {
var ct = Math.cos(theta), st = Math.sin(theta);
c.transform(ct, -st, st, ct,
-x*ct-y*st+x, x*st-y*ct+y);
}
<body>
This is a red square:
<canvas id="square" width=10 height=10></canvas>.
This is a blue circle:
<canvas id="circle" width=10 height=10></canvas>.
<script>
// Get first canvas element and its context
var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// Draw something in the canvas
context.fillStyle = "#f00"; // Set color to red
context.fillRect(0,0,10,10); // Fill a small square
// Get second canvas and its context
canvas = document.getElementById("circle");
context = canvas.getContext("2d");
// Begin a path and add a circle to it
context.beginPath();
context.arc(5, 5, 5, 0, 2*Math.PI, true);
context.fillStyle = "#00f"; // Set blue fill
context.fill(); // Fill the path
</script>
</body>
logo.png

1.91 KB

Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment