html5的canvas是一種立即模式繪圖系統。當想canvas之中繪制內容時,瀏覽器立刻就會將這些內容畫到canvas之中,然后立刻忘掉剛才所畫的東西。如果要實現的是一個繪畫應用程序的話,那么立即模式繪圖系統就很合適,不過,如果要實現的是那種可以讓用戶創建並操作繪圖對象的畫圖應用程序,那么最好還是講可以編輯並繪制的那些圖形對象保存到一份列表中。
程序清單,使用多邊形對象來實現畫圖應用程序
html代碼:
1 <html> 2 <head> 3 <title>Polygon Objects</title> 4 5 <style> 6 body { 7 background: #eeeeee; 8 } 9 10 #controls { 11 position: absolute; 12 left: 25px; 13 top: 25px; 14 } 15 16 #canvas { 17 background: #ffffff; 18 cursor: crosshair; 19 margin-left: 10px; 20 margin-top: 10px; 21 -webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5); 22 -moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5); 23 box-shadow: 4px 4px 8px rgba(0,0,0,0.5); 24 } 25 26 </style> 27 </head> 28 29 <body> 30 <canvas id='canvas' width='850' height='600'> 31 Canvas not supported 32 </canvas> 33 34 <div id='controls'> 35 Stroke color: <select id='strokeStyleSelect'> 36 <option value='red'>red</option> 37 <option value='green'>green</option> 38 <option value='blue'>blue</option> 39 <option value='orange'>orange</option> 40 <option value='cornflowerblue' selected>cornflowerblue</option> 41 <option value='goldenrod'>goldenrod</option> 42 <option value='navy'>navy</option> 43 <option value='purple'>purple</option> 44 </select> 45 46 Fill color: <select id='fillStyleSelect'> 47 <option value='rgba(255,0,0,0.5)'>semi-transparent red</option> 48 <option value='green'>green</option> 49 <option value='rgba(0,0,255,0.5)'>semi-transparent blue</option> 50 <option value='orange'>orange</option> 51 <option value='rgba(100,140,230,0.5)'>semi-transparent cornflowerblue</option> 52 <option value='goldenrod' selected>goldenrod</option> 53 <option value='navy'>navy</option> 54 <option value='purple'>purple</option> 55 </select> 56 57 Sides: <select id='sidesSelect'> 58 <option value=4 select>4</option> 59 <option value=6>6</option> 60 <option value=8>8</option> 61 <option value=10>10</option> 62 <option value=12>12</option> 63 <option value=20>20</option> 64 </select> 65 66 67 Start angle: <select id='startAngleSelect'> 68 <option value=0 select>0</option> 69 <option value=22.5>22.5</option> 70 <option value=45>45</option> 71 <option value=67.5>67.5</option> 72 <option value=90>90</option> 73 </select> 74 75 Fill <input id='fillCheckbox' type='checkbox' checked/> 76 <input id='eraseAllButton' type='button' value='Erase all'/> 77 </div> 78 79 <script src = '../../shared/js/polygon.js'></script> 80 <script src = 'example.js'></script> 81 </body> 82 </html>
example.js代碼:
1 var canvas = document.getElementById('canvas'), 2 context = canvas.getContext('2d'), 3 eraseAllButton = document.getElementById('eraseAllButton'), 4 strokeStyleSelect = document.getElementById('strokeStyleSelect'), 5 startAngleSelect = document.getElementById('startAngleSelect'), 6 7 fillStyleSelect = document.getElementById('fillStyleSelect'), 8 fillCheckbox = document.getElementById('fillCheckbox'), 9 10 sidesSelect = document.getElementById('sidesSelect'), 11 12 drawingSurfaceImageData, 13 14 mousedown = {}, 15 rubberbandRect = {}, 16 17 dragging = false, 18 draggingOffsetX, 19 draggingOffsetY, 20 21 sides = 8, 22 startAngle = 0, 23 24 guidewires = true, 25 26 polygons = [], 27 tmpPolygon = new Polygon(0,0,1,4,0,'blue','red',false), 28 29 Point = function (x, y) { 30 this.x = x; 31 this.y = y; 32 }; 33 34 35 // Functions..................................................... 36 37 function drawGrid(color, stepx, stepy) { 38 context.save() 39 40 context.shadowColor = undefined; 41 context.shadowOffsetX = 0; 42 context.shadowOffsetY = 0; 43 44 context.strokeStyle = color; 45 context.fillStyle = '#ffffff'; 46 context.lineWidth = 0.5; 47 context.fillRect(0, 0, context.canvas.width, context.canvas.height); 48 49 for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) { 50 context.beginPath(); 51 context.moveTo(i, 0); 52 context.lineTo(i, context.canvas.height); 53 context.stroke(); 54 } 55 56 for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) { 57 context.beginPath(); 58 context.moveTo(0, i); 59 context.lineTo(context.canvas.width, i); 60 context.stroke(); 61 } 62 63 context.restore(); 64 } 65 66 function windowToCanvas(x, y) { 67 var bbox = canvas.getBoundingClientRect(); 68 return { x: x - bbox.left * (canvas.width / bbox.width), 69 y: y - bbox.top * (canvas.height / bbox.height) 70 }; 71 } 72 73 // Save and restore drawing surface.............................. 74 75 function saveDrawingSurface() { 76 drawingSurfaceImageData = context.getImageData(0, 0, 77 canvas.width, 78 canvas.height); 79 } 80 81 function restoreDrawingSurface() { 82 context.putImageData(drawingSurfaceImageData, 0, 0); 83 } 84 85 // Rubberbands................................................... 86 87 function updateRubberbandRectangle(loc) { 88 rubberbandRect.width = Math.abs(loc.x - mousedown.x); 89 rubberbandRect.height = Math.abs(loc.y - mousedown.y); 90 91 if (loc.x > mousedown.x) rubberbandRect.left = mousedown.x; 92 else rubberbandRect.left = loc.x; 93 94 if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y; 95 else rubberbandRect.top = loc.y; 96 } 97 98 function drawRubberbandShape(loc, sides, startAngle) { 99 var polygon = new Polygon(mousedown.x, mousedown.y, 100 rubberbandRect.width, 101 parseInt(sidesSelect.value), 102 (Math.PI / 180) * parseInt(startAngleSelect.value), 103 context.strokeStyle, 104 context.fillStyle, 105 fillCheckbox.checked); 106 107 context.beginPath(); 108 polygon.createPath(context); 109 polygon.stroke(context); 110 111 if (fillCheckbox.checked) { 112 polygon.fill(context); 113 } 114 115 if (!dragging) { 116 polygons.push(polygon); 117 } 118 } 119 120 function updateRubberband(loc, sides, startAngle) { 121 updateRubberbandRectangle(loc); 122 drawRubberbandShape(loc, sides, startAngle); 123 } 124 125 // Guidewires.................................................... 126 127 function drawHorizontalLine (y) { 128 context.beginPath(); 129 context.moveTo(0,y+0.5); 130 context.lineTo(context.canvas.width,y+0.5); 131 context.stroke(); 132 } 133 134 function drawVerticalLine (x) { 135 context.beginPath(); 136 context.moveTo(x+0.5,0); 137 context.lineTo(x+0.5,context.canvas.height); 138 context.stroke(); 139 } 140 141 function drawGuidewires(x, y) { 142 context.save(); 143 context.strokeStyle = 'rgba(0,0,230,0.4)'; 144 context.lineWidth = 0.5; 145 drawVerticalLine(x); 146 drawHorizontalLine(y); 147 context.restore(); 148 } 149 150 function drawPolygons() { 151 polygons.forEach( function (polygon) { 152 polygon.stroke(context); 153 if (polygon.filled) { 154 polygon.fill(context); 155 } 156 }); 157 } 158 159 function startDragging(loc) { 160 saveDrawingSurface(); 161 mousedown.x = loc.x; 162 mousedown.y = loc.y; 163 } 164 165 // Event handlers................................................ 166 167 canvas.onmousedown = function (e) { 168 var loc = windowToCanvas(e.clientX, e.clientY); 169 170 e.preventDefault(); // prevent cursor change 171 172 startDragging(loc); 173 dragging = true; 174 }; 175 176 canvas.onmousemove = function (e) { 177 var loc = windowToCanvas(e.clientX, e.clientYe); 178 179 e.preventDefault(); // prevent selections 180 181 if (dragging) { 182 restoreDrawingSurface(); 183 updateRubberband(loc, sides, startAngle); 184 185 if (guidewires) { 186 drawGuidewires(mousedown.x, mousedown.y); 187 } 188 } 189 }; 190 191 canvas.onmouseup = function (e) { 192 var loc = windowToCanvas(e.clientX, e.clientY); 193 194 dragging = false; 195 restoreDrawingSurface(); 196 updateRubberband(loc); 197 }; 198 199 eraseAllButton.onclick = function (e) { 200 context.clearRect(0, 0, canvas.width, canvas.height); 201 drawGrid('lightgray', 10, 10); 202 saveDrawingSurface(); 203 }; 204 205 strokeStyleSelect.onchange = function (e) { 206 context.strokeStyle = strokeStyleSelect.value; 207 }; 208 209 fillStyleSelect.onchange = function (e) { 210 context.fillStyle = fillStyleSelect.value; 211 }; 212 213 // Initialization................................................ 214 215 context.strokeStyle = strokeStyleSelect.value; 216 context.fillStyle = fillStyleSelect.value; 217 218 context.shadowOffsetX = 2; 219 context.shadowOffsetY = 2; 220 context.shadowBlur = 4; 221 222 drawGrid('lightgray', 10, 10);
應用程序在用戶按下並拖動鼠標時,會調用drawRubberbandshape函數,以創建多邊形。此函數先創建一個表示多邊形的對象,然后調用該對象的createPath()方法,最后對創建出的路徑進行描邊,必要時還會填充。
如果用戶在拖動之后將鼠標松開,那么drawRubberbandShape()函數就會將當前這個多邊形加入到應用程序所維護的那份多邊形對象列表之中。
本小節中所實現的多邊形對象包含下列方法:
points[] getPoints()
void createPath(context)
void stroke(context)
void fill(context)
void move(x,y)