Project

General

Profile

canvas_drawing_example_20150724.html

Greg Shah, 07/24/2015 06:31 PM

Download (17.8 KB)

 
1
<!DOCTYPE html>
2
<html lang="en">
3
   <head>
4
      <meta charset="utf-8" />
5
      <title>Canvas Drawing Example</title>
6
      
7
      <script>
8
         var canvas;
9
         
10
         var ctx;
11
         
12
         var pixel;
13
         var pixData;
14
         
15
         function drawPixel(ctx, x, y, color)
16
         {
17
            pixData[0] = color[0];
18
            pixData[1] = color[1];
19
            pixData[2] = color[2];
20
            
21
            
22
            ctx.putImageData(pixel, x, y);
23
         }
24
         
25
         function rawLine(ctx, x1, y1, x2, y2, color)
26
         {
27
            var dx = Math.abs(x2 - x1);
28
            var dy = Math.abs(y2 - y1);
29
            
30
            var xIncr = dx > 0 ? 1 : -1;
31
            var yIncr = dy > 0 ? 1 : -1;
32
            
33
            dy = dy * -1;
34
            
35
            var err = dx + dy;
36
            var err2;
37
            
38
            var x = x1;
39
            var y = y1;
40
            
41
            while (true)
42
            {
43
               drawPixel(ctx, x, y, color);
44
               
45
               if (x == x2 && y == y2)
46
                  break;
47
                  
48
               err2 = err * 2;
49
               
50
               if (err2 >= dy)
51
               {
52
                  err += dy;
53
                  x   += xIncr; 
54
               }
55
               
56
               if (err2 <= dx)
57
               {
58
                  err += dx;
59
                  y   += yIncr;
60
               }
61
            }
62
         }
63
         
64
         function drawLine(x1, y1, x2, y2)
65
         {
66
            ctx.beginPath();
67
            ctx.moveTo(x1, y1);
68
            ctx.lineTo(x2, y2);
69
            ctx.stroke();         
70
         }
71
         
72
         function renderClosedPath(ctx, fill)
73
         {
74
            ctx.save();
75
            ctx.clip();
76
            
77
            if (fill)
78
            {
79
               ctx.fill();
80
            }
81
            else
82
            {
83
               ctx.stroke();
84
            }
85
            
86
            ctx.restore();
87
         }
88
         
89
         // 
90
         // Draw a rounded rectangle with the given context, dimensions and fill.
91
         // 
92
         // @param    {CanvasRenderingContext2D} ctx
93
         //           The canvas 2D graphics context.
94
         // @param    {Number} x
95
         //           X coordinate of the top left of the rectangle (if a square corner was placed
96
         //           there).
97
         // @param    {Number} y
98
         //           Y coordinate of the top left of the rectangle (if a square corner was placed
99
         //           there).
100
         // @param    {Number} width
101
         //           Width of the rectangle.
102
         // @param    {Number} height
103
         //           Height of the rectangle.
104
         // @param    {Number} diameter
105
         //           Diameter of the arc that defines the rounded corner.
106
         // @param    {Boolean} fill
107
         //           <code>true</code> to fill the rectangle with the current color, otherwise just
108
         //           stroke the rectangle.
109
         // 
110
         function createRoundRect(ctx, x, y, width, height, diameter, color, fill)
111
         {
112
            var radius = diameter / 2;
113
            
114
            // arc control points
115
            var northWestX = x;
116
            var northWestY = y;
117
            var northEastX = x + width;
118
            var northEastY = y;
119
            var southWestX = x;
120
            var southWestY = y + height;
121
            var southEastX = x + width;
122
            var southEastY = y + height;
123
            
124
            // line end points
125
            var topLeftX     = northWestX + radius;
126
            var topLeftY     = northWestY;
127
            var topRightX    = northEastX - radius;
128
            var topRightY    = northEastY;
129
            var rightUpX     = northEastX;
130
            var rightUpY     = northEastY + radius;
131
            var rightDownX   = southEastX;
132
            var rightDownY   = southEastY - radius;
133
            var bottomRightX = southEastX - radius;
134
            var bottomRightY = southEastY;
135
            var bottomLeftX  = southWestX + radius;
136
            var bottomLeftY  = southWestY;
137
            var leftDownX    = southWestX;
138
            var leftDownY    = southWestY - radius;
139
            var leftUpX      = northWestX;
140
            var leftUpY      = northWestY + radius;
141
            
142
            ctx.beginPath();
143
            ctx.moveTo(topRightX, topRightY);
144
            ctx.quadraticCurveTo(northEastX, northEastY, rightUpX, rightUpY);
145
            ctx.lineTo(rightDownX, rightDownY);
146
            ctx.quadraticCurveTo(southEastX, southEastY, bottomRightX, bottomRightY);
147
            ctx.lineTo(bottomLeftX, bottomLeftY);
148
            ctx.quadraticCurveTo(southWestX, southWestY, leftDownX, leftDownY);
149
            ctx.lineTo(leftUpX, leftUpY);
150
            ctx.quadraticCurveTo(northWestX, northWestY, topLeftX, topLeftY);
151
            ctx.closePath();
152
            
153
            renderClosedPath(ctx, fill);
154
            
155
            // overdraw the anti-aliased line segments
156
            rawLine(ctx, topLeftX, topLeftY, topRightX, topRightY, color);
157
            rawLine(ctx, rightUpX, rightUpY, rightDownX, rightDownY, color);
158
            rawLine(ctx, bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, color);
159
            rawLine(ctx, leftUpX, leftUpY, leftDownX, leftDownY, color);
160
         }
161
         
162
         function parseColor(spec)
163
         {
164
            if (typeof spec !== "string")
165
            {
166
               return null;
167
            }
168
            
169
            if (spec.length !== 7)
170
            {
171
               return null;
172
            }
173
            
174
            if (spec.charAt(0) !== "#")
175
            {
176
               return null;
177
            }
178
            
179
            var r = parseInt(spec.substr(1, 2), 16);
180
            var g = parseInt(spec.substr(3, 2), 16);
181
            var b = parseInt(spec.substr(5, 2), 16);
182
         
183
            if (r === NaN || g === NaN || b === NaN)
184
            {
185
               return null;
186
            }
187
            
188
            return [ r, g, b ];
189
         }
190
         
191
         function validateColor(color)
192
         {
193
            if (!(color instanceof Array))
194
            {
195
               return false;
196
            }
197
            
198
            if (color.length !== 3)
199
            {
200
               return false;
201
            }
202
            
203
            if (typeof color[0] !== "number" ||
204
                typeof color[1] !== "number" ||
205
                typeof color[2] !== "number")
206
            {
207
               return false;
208
            }
209
            
210
            if (color[0] < 0 || color[0] > 255 ||
211
                color[1] < 0 || color[1] > 255 ||
212
                color[2] < 0 || color[2] > 255)
213
            {
214
               return false;
215
            }
216
            
217
            return true;
218
         }
219
         
220
         function createColorString(color)
221
         {
222
            if (!validateColor(color))
223
            {
224
               return null;
225
            }
226
            
227
            return "rgb(" + color[0].toString() + ", "
228
                          + color[1].toString() + ", "
229
                          + color[2].toString() + ")";
230
         }
231
         
232
         function setColor(ctx, color)
233
         {
234
            var colorSpec;
235
         
236
            if (color instanceof Array)
237
            {
238
               if (validateColor(color))
239
               {
240
                  colorSpec = createColorString(color);     
241
               }
242
               else
243
               {
244
                  // error
245
               }
246
            }
247
            else
248
            {
249
               if (typeof color === "string")                  
250
               {
251
                  colorSpec = color;
252
               }
253
               else
254
               {
255
                  // error
256
               }
257
            }
258
            
259
            ctx.fillStyle   = colorSpec;
260
            ctx.strokeStyle = colorSpec;
261
         }
262
         
263
         var SCALE_FACTOR = 0.7;
264
         var MIN_COLOR_VALUE = Math.floor(1.0 / (1.0 - SCALE_FACTOR));
265
         
266
         function lightenColor(color)
267
         {
268
            if (!validateColor(color))
269
            {
270
               return null;
271
            }
272
            
273
            // handle black directly, the result is NOT very much different but this is how it is
274
            // done in Java
275
            if (color[0] == 0 && color[1] == 0 && color[2] == 0)
276
               return [ MIN_COLOR_VALUE, MIN_COLOR_VALUE, MIN_COLOR_VALUE ];
277
            
278
            // force inputs to a minimim color value if between 0 and that minimum color value
279
            // (yes, this is how Java does it)
280
            var rIn = (color[0] > 0 && color[0] < MIN_COLOR_VALUE) ? MIN_COLOR_VALUE : color[0];
281
            var gIn = (color[1] > 0 && color[1] < MIN_COLOR_VALUE) ? MIN_COLOR_VALUE : color[1];
282
            var bIn = (color[2] > 0 && color[2] < MIN_COLOR_VALUE) ? MIN_COLOR_VALUE : color[2];
283
            
284
            // scale it
285
            var r = Math.min(Math.floor(rIn / SCALE_FACTOR), 255);
286
            var g = Math.min(Math.floor(gIn / SCALE_FACTOR), 255);
287
            var b = Math.min(Math.floor(bIn / SCALE_FACTOR), 255);
288
            
289
            return [ r, g, b ];
290
         }
291
         
292
         function darkenColor(color)
293
         {
294
            if (!validateColor(color))
295
            {
296
               return null;
297
            }
298
            
299
            var r = Math.max(0, Math.floor(color[0] * SCALE_FACTOR));
300
            var g = Math.max(0, Math.floor(color[1] * SCALE_FACTOR));
301
            var b = Math.max(0, Math.floor(color[2] * SCALE_FACTOR));
302
            
303
            return [ r, g, b ];
304
         }
305
         
306
         function create3DRect(ctx, x, y, width, height, fill, raised)
307
         {
308
            // save off our colors
309
            var oriFillColor   = ctx.fillStyle;
310
            var oriStrokeColor = ctx.strokeStyle;
311
            
312
            // both colors should always be assigned the same value on entry to any drawing op
313
            // so it should be safe to use just the fill color
314
            var color = parseColor(oriFillColor);
315
            
316
            if (color === null)
317
            {
318
               return;
319
            }
320
            
321
            var darker  = darkenColor(color);
322
            var lighter = lightenColor(color);
323
            
324
            // save off our state so that we can clear our clipping region on exit
325
            ctx.save();
326
            
327
            // the Java implementation draws 1 pixel larger in each dimension
328
            width  = width + 1;
329
            height = height + 1;
330
            
331
            // setup our clipping region, this is needed because the line width (1 pixel wide) is
332
            // drawn half on one side of hte path and half on the other side; this makes paths a
333
            // half pixel bigger than the caller would expect; by defining the same path as a
334
            // clipping region, the half pixel width that is outside the path is never output
335
            ctx.beginPath();
336
            ctx.rect(x, y, width, height);
337
            ctx.clip();
338
            
339
            var current = raised ? lighter : darker;
340
            
341
            if (fill)
342
               ctx.fillStyle = createColorString(current);
343
               
344
            createRect(ctx, x, y, width, height, current, fill);
345
            
346
            // now begin a new path for the rectangle itself
347
            //ctx.beginPath();
348
            
349
            // create the path
350
            //ctx.rect(x, y, width, height);
351
            
352
            // set the color and fill or stroke as needed
353
            //if (fill)                                       
354
            //{
355
            //   ctx.fillStyle = createColorString(raised ? lighter : darker);
356
            //   ctx.fill();
357
            //}
358
            //else
359
            //{
360
            //   ctx.strokeStyle = createColorString(raised ? lighter : darker);
361
            //   ctx.stroke();
362
            //}
363
            
364
            // draw the bottom and right sides in the contrasting color
365
            //ctx.strokeStyle = createColorString(raised ? darker : lighter);
366
            current = raised ? darker : lighter;
367
            //drawLine(x + 1, y + height, x + width, y + height);
368
            //drawLine(x + width, y, x + width, y + height - 1);
369
            rawLine(ctx, x + 1, y + height, x + width, y + height, current);
370
            rawLine(ctx, x + width, y, x + width, y + height - 1, current);
371
            
372
            // clear the clipping region
373
            ctx.restore();
374
            
375
            // reset our colors
376
            ctx.fillStyle   = oriFillColor;
377
            ctx.strokeStyle = oriStrokeColor;
378
         }                         
379
         
380
         function createRect(ctx, x, y, width, height, color, fill)
381
         {
382
            // use vector operations for the interior of the rectangle
383
            if (fill)
384
            {
385
               ctx.beginPath();
386
               ctx.rect(x, y, width, height);
387
               renderClosedPath(ctx, fill);
388
            }
389
            
390
            // now overdraw the stroked portion
391
            rawLine(ctx, x, y, x + width, y, color);
392
            rawLine(ctx, x, y + 1, x, y + height, color);
393
            rawLine(ctx, x + width, y + 1, x + width, y + height, color);
394
            rawLine(ctx, x + 1, y + height, x + width - 1, y + height, color);
395
         }                         
396
         
397
         window.onload=function()
398
         {
399
            canvas = document.getElementById("bogus");
400
            ctx = canvas.getContext('2d');
401
            
402
            canvas.top = 150;
403
            canvas.left = 150;
404
            canvas.width = 600;
405
            canvas.height = 600;
406
            
407
            // canvas.style.backgroundColor = "#EEEEEE";
408
            // canvas.style.backgroundColor = "rbga(238, 238, 238, 1.0)";
409
            
410
            pixel   = ctx.createImageData(1, 1);
411
            pixData = pixel.data;
412
            pixData[3] = 0xFF; // force alpha to 100% opaque
413
            
414
            ctx.globalCompositeOperation = "source-over";
415
            ctx.lineWidth = 1;
416
            ctx.imageSmoothingEnabled = false;
417
            ctx.translate(0.5, 0.5);
418
            
419
            var color;
420
            
421
            // draw background
422
            setColor(ctx, "#EEEEEE");
423
            color = parseColor(ctx.strokeStyle);
424
            createRect(ctx, 0, 0, canvas.width, canvas.height, color, true);
425
            
426
            // reset black as default color
427
            setColor(ctx, "#000000");
428
            color = parseColor(ctx.strokeStyle);
429
            
430
            var t;
431
            
432
            // draw Y axis tick marks
433
            for (t = 0; t < canvas.height; t++)
434
            {
435
               // major every 10 pixels
436
               if ((t % 10) == 0)
437
               {
438
                  rawLine(ctx, 0, t, 5, t, color);
439
               }
440
               // minor every 2 pixels
441
               if ((t % 2) == 0)
442
               {
443
                  rawLine(ctx, 0, t, 2, t, color);
444
               }
445
            }
446
            
447
            // draw X axis tick marks (can't start at 0 because the Y ticks are there)
448
            for (t = 10; t < canvas.width; t++)
449
            {
450
               // major every 10 pixels
451
               if ((t % 10) == 0)
452
               {
453
                  rawLine(ctx, t, 0, t, 5, color);
454
               }
455
               // minor every 2 pixels
456
               if ((t % 2) == 0)
457
               {
458
                  rawLine(ctx, t, 0, t, 2, color);
459
               }
460
            }
461
            
462
            rawLine(ctx, 10, 10, 590, 30, color);
463
         
464
            createRect(ctx, 40, 40, 55, 30, color, false);
465
            createRect(ctx, 150, 40, 55, 30, color, true);
466
            
467
            createRoundRect(ctx, 40, 90, 55, 30, 5, color, false);
468
            createRoundRect(ctx, 40, 130, 55, 30, 5, color, true);
469
            createRoundRect(ctx, 110, 90, 55, 30, 8, color, false);
470
            createRoundRect(ctx, 110, 130, 55, 30, 8, color, true);
471
            createRoundRect(ctx, 180, 90, 55, 30, 10, color, false);
472
            createRoundRect(ctx, 180, 130, 55, 30, 10, color, true);
473
            createRoundRect(ctx, 250, 90, 55, 30, 15, color, false);
474
            createRoundRect(ctx, 250, 130, 55, 30, 15, color, true);
475
            createRoundRect(ctx, 320, 90, 55, 30, 20, color, false);
476
            createRoundRect(ctx, 320, 130, 55, 30, 20, color, true);
477
            createRoundRect(ctx, 390, 90, 55, 30, 25, color, false);
478
            createRoundRect(ctx, 390, 130, 55, 30, 25, color, true);
479
            createRoundRect(ctx, 460, 90, 55, 30, 30, color, false);
480
            createRoundRect(ctx, 460, 130, 55, 30, 30, color, true);
481
            
482
            setColor(ctx, "#00FF00");
483
            color = parseColor(ctx.strokeStyle);
484
            create3DRect(ctx, 40, 175, 75, 30, false, true);
485
            create3DRect(ctx, 130, 175, 150, 60, false, true);
486
            create3DRect(ctx, 295, 175, 75, 30, true, true);
487
            create3DRect(ctx, 395, 175, 150, 60, true, true);
488
            create3DRect(ctx, 40, 250, 75, 30, false, false);
489
            create3DRect(ctx, 130, 250, 150, 60, false, false);
490
            create3DRect(ctx, 295, 250, 75, 30, true, false);
491
            create3DRect(ctx, 395, 250, 150, 60, true, false);
492
            setColor(ctx, "#000000");
493
            color = parseColor(ctx.strokeStyle);
494
            create3DRect(ctx, 40, 325, 75, 30, false, false);
495
            create3DRect(ctx, 130, 325, 150, 60, false, false);
496
            create3DRect(ctx, 295, 325, 75, 30, true, false);
497
            create3DRect(ctx, 395, 325, 150, 60, true, false);
498
         };
499
      </script>
500
   </head>
501
   <body>
502
      <canvas id="bogus" style="position:'absolute';">No HTML5 support in your browser!</canvas>
503
   </body>
504
</html>