canvas实现涂鸦功能

    xiaoxiao2021-04-17  47

    <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Magnifying Glass</title> <style> body { background: #dddddd; } option { padding-right: 5px; } .floatingControls { position: absolute; left: 105px; top: 70px; width: 300px; padding: 20px; border: thin solid rgba(0, 0, 0, 0.3); background: rgba(0, 0, 200, 0.1); color: blue; font: 14px Arial; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px; box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px; display: none; } .floatingControls p { margin-top: 0px; margin-bottom: 20px; } #iconCanvas { background: #eeeeef; border: thin solid #aaaaaa; cursor: pointer; -webkit-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -moz-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; } #drawingCanvas { background: #ffffff; border: thin solid #aaaaaa; cursor: crosshair; margin-left: 10px; -webkit-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -moz-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -webkit-border-radius: 12px; -moz-border-radius: 12px; border-radius: 12px; } #controls { padding-top: 5px; padding-bottom: 10px; padding-left: 100px; } select { margin-right: 15px; } @media all and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait) { #controls { padding-top: 10px; padding-bottom: 10px; } #drawingCanvas { background: #ffffff; border: thin solid #aaaaaa; cursor: crosshair; margin-left: 10px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 2px 2px 4px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 2px 2px 4px; box-shadow: rgba(0, 0, 0, 0.2) 2px 2px 4px; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; } } @media all and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape) { #controls { padding-top: 20px; padding-bottom: 10px; padding-left: 85px; } #drawingCanvas { background: #ffffff; cursor: crosshair; margin-left: 5px; -webkit-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -moz-box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 4px; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; } #snapshotImageElement { position: absolute; left: 85px; top: 30px; margin: 20px; display: none; } } #snapshotButton { margin-left: 150px; } #snapshotInstructions { position: absolute; left: 300px; top: 0px; margin-top: 17px; display: none; } #keyboard { position: absolute; left: 25px; top: 0px; width: 1000px; height: 0px; background: rgba(129, 129, 138, 0.4); -webkit-box-shadow: rgba(0, 0, 0, 0.2) 3px 3px 4px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 3px 3px 4px; box-shadow: rgba(0, 0, 0, 0.2) 3px 3px 4px; } </style> </head> <body> <div id='controls'> Stroke: <select id='strokeStyleSelect'> <option value='red'>red</option> <option value='green'>green</option> <option value='blue'>blue</option> <option value='orange'>orange</option> <option value='cornflowerblue' selected>cornflowerblue</option> <option value='goldenrod'>goldenrod</option> <option value='white'>white</option> <option value='purple'>purple</option> </select> Fill: <select id='fillStyleSelect'> <option value='red'>red</option> <option value='rgba(255,0,0,0.5)'>semi-transparent red</option> <option value='green'>green</option> <option value='rgba(0,255,0,0.5)'>semi-transparent green</option> <option value='blue'>blue</option> <option value='rgba(0,0,255,0.5)'>semi-transparent blue</option> <option value='orange'>orange</option> <option value='rgba(255,165,0,0.5)'>semi-transparent orange</option> <option value='cornflowerblue'>cornflowerblue</option> <option value='rgba(100,140,230,0.5)'>semi-transparent cornflowerblue</option> <option value='goldenrod'>goldenrod</option> <option value='rgba(218,165,32,0.5)' selected>semi-transparent goldenrod</option> <option value='white'>white</option> <option value='rgba(255,255,255,0.5)'>semi-transparent white</option> <option value='purple'>purple</option> <option value='rgba(255,0,255,0.5)'>semi-transparent purple</option> </select> Line width: <select id='lineWidthSelect'> <option value='1.0'>1.0</option> <option value='2.0'>2.0</option> <option value='3.0'>3.0</option> <option value='4.0'>4.0</option> <option value='5.0'>5.0</option> <option value='6.0'>6.0</option> </select> <input id='eraseAllButton' type='button' value='Erase all' /> <input id='snapshotButton' type='button' value='Take snapshot' /> <span id='snapshotInstructions'>Drag the image to your desktop (or Right-click to save to disk)</span> </div> <img id='snapshotImageElement' alt='' /> <!-- What's up with the need for alt='', Opera?!? --> <canvas id='iconCanvas' width='75' height='670'> Canvas not supported </canvas> <canvas id='drawingCanvas' width='915' height='670'> Canvas not supported </canvas> <div id='keyboard'></div> <div id='curveInstructions' class='floatingControls'> <p> The yellow circle is a control point for the curve. Drag that circle around to change the shape of the curve. </p> <input id='curveInstructionsOkayButton' type='button' value='Okay' /> <input id='curveInstructionsNoMoreButton' type='button' value='Do not show this again' /> </div> <script src='keyboard.js'></script> <script> var iconCanvas = document.getElementById('iconCanvas'), drawingCanvas = document.getElementById('drawingCanvas'), drawingContext = drawingCanvas.getContext('2d'), backgroundContext = document.createElement('canvas').getContext('2d'), iconContext = iconCanvas.getContext('2d'), strokeStyleSelect = document.getElementById('strokeStyleSelect'), fillStyleSelect = document.getElementById('fillStyleSelect'), lineWidthSelect = document.getElementById('lineWidthSelect'), eraseAllButton = document.getElementById('eraseAllButton'), snapshotButton = document.getElementById('snapshotButton'), controls = document.getElementById('controls'), curveInstructions = document.getElementById('curveInstructions'), curveInstructionsOkayButton = document.getElementById('curveInstructionsOkayButton'), curveInstructionsNoMoreButton = document.getElementById('curveInstructionsNoMoreButton'), showCurveInstructions = true, drawingSurfaceImageData, rubberbandW, rubberbandH, rubberbandUlhc = {}, dragging = false, mousedown = {}, lastRect = {}, lastX, lastY, controlPoint = {}, editingCurve = false, draggingControlPoint = false, curveStart = {}, curveEnd = {}, doFill = false, selectedRect = null, selectedFunction, editingText = false, currentText, CONTROL_POINT_RADIUS = 20, CONTROL_POINT_FILL_STYLE = 'rgba(255,255,0,0.5)', CONTROL_POINT_STROKE_STYLE = 'rgba(0, 0, 255, 0.8)', RUBBERBAND_LINE_WIDTH = 1, RUBBERBAND_STROKE_STYLE = 'green', GRID_HORIZONTAL_SPACING = 10, GRID_VERTICAL_SPACING = 10, GRID_LINE_COLOR = 'rgb(0, 0, 200)', ERASER_ICON_GRID_COLOR = 'rgb(0, 0, 200)', ERASER_ICON_CIRCLE_COLOR = 'rgba(100, 140, 200, 0.5)', ERASER_ICON_RADIUS = 20, SLINKY_LINE_WIDTH = 1, SLINKY_SHADOW_STYLE = 'rgba(0,0,0,0.2)', SLINKY_SHADOW_OFFSET = -5, SLINKY_SHADOW_BLUR = 20, SLINKY_RADIUS = 60, ERASER_LINE_WIDTH = 1, ERASER_SHADOW_STYLE = 'blue', ERASER_STROKE_STYLE = 'rgba(0,0,255,0.6)', ERASER_SHADOW_OFFSET = -5, ERASER_SHADOW_BLUR = 20, ERASER_RADIUS = 40, SHADOW_COLOR = 'rgba(0,0,0,0.7)', ICON_BACKGROUND_STYLE = '#eeeeee', ICON_BORDER_STROKE_STYLE = 'rgba(100, 140, 230, 0.5)', ICON_STROKE_STYLE = 'rgb(100, 140, 230)', ICON_FILL_STYLE = '#dddddd', TEXT_ICON_FILL_STYLE = 'rgba(100, 140, 230, 0.5)', TEXT_ICON_TEXT = 'T', CIRCLE_ICON_RADIUS = 20, ICON_RECTANGLES = [{ x: 13.5, y: 18.5, w: 48, h: 48 }, { x: 13.5, y: 78.5, w: 48, h: 48 }, { x: 13.5, y: 138.5, w: 48, h: 48 }, { x: 13.5, y: 198.5, w: 48, h: 48 }, { x: 13.5, y: 258.5, w: 48, h: 48 }, { x: 13.5, y: 318.5, w: 48, h: 48 }, { x: 13.5, y: 378.5, w: 48, h: 48 }, { x: 13.5, y: 438.5, w: 48, h: 48 }, { x: 13.5, y: 508.5, w: 48, h: 48 } ], LINE_ICON = 0, RECTANGLE_ICON = 1, CIRCLE_ICON = 2, OPEN_PATH_ICON = 3, CLOSED_PATH_ICON = 4, CURVE_ICON = 5, TEXT_ICON = 6, SLINKY_ICON = 7, ERASER_ICON = 8; // 绘制表格.......................................................... function drawGrid(context, color, stepx, stepy) { context.save() context.strokeStyle = color; context.fillStyle = '#ffffff'; context.lineWidth = 0.5; context.fillRect(0, 0, context.canvas.width, context.canvas.height); context.globalAlpha = 0.1; //绘制竖线 context.beginPath(); for(var i = stepx + 0.5; i < context.canvas.width; i += stepx) { context.moveTo(i, 0); context.lineTo(i, context.canvas.height); } context.stroke(); //绘制横线 context.beginPath(); for(var i = stepy + 0.5; i < context.canvas.height; i += stepy) { context.moveTo(0, i); context.lineTo(context.canvas.width, i); } context.stroke(); context.restore(); } // Icons......................................................... //第一个icon,画斜线 function drawLineIcon(rect) { iconContext.beginPath(); iconContext.moveTo(rect.x + 5, rect.y + 5); iconContext.lineTo(rect.x + rect.w - 5, rect.y + rect.h - 5); iconContext.stroke(); } //第二个icon,画矩形 function drawRectIcon(rect) { iconContext.strokeRect(rect.x + 5, rect.y + 5, rect.w - 10, rect.h - 10); } //第三个icon,画圆 function drawCircleIcon(rect) { iconContext.beginPath(); iconContext.arc(rect.x + rect.w / 2, rect.y + rect.h / 2, CIRCLE_ICON_RADIUS, 0, Math.PI * 2, false); iconContext.stroke(); } //第四个icon,画没有闭合的曲线 function drawOpenPathIcon(rect) { iconContext.beginPath(); drawOpenPathIconLines(rect); iconContext.stroke(); } //第五个icon,画闭合曲线 function drawClosepxathIcon(rect) { //fillIconLowerRight(rect); iconContext.beginPath(); drawOpenPathIconLines(rect); iconContext.closePath(); iconContext.stroke(); } //第六个icon,画曲线 function drawCurveIcon(rect) { //fillIconLowerRight(rect); iconContext.beginPath(); iconContext.beginPath(); iconContext.moveTo(rect.x + rect.w - 10, rect.y + 5); iconContext.quadraticCurveTo(rect.x - 10, rect.y, rect.x + rect.w - 10, rect.y + rect.h - 5); iconContext.stroke(); } //第七个icon,画文字 function drawTextIcon(rect) { var text = TEXT_ICON_TEXT; //fillIconLowerRight(rect); iconContext.fillStyle = TEXT_ICON_FILL_STYLE; iconContext.fillText(text, rect.x + rect.w / 2, rect.y + rect.h / 2 + 5); iconContext.strokeText(text, rect.x + rect.w / 2, rect.y + rect.h / 2 + 5); } //第八个icon,画管道 function drawSlinkyIcon(rect) { var x, y; //fillIconLowerRight(rect); iconContext.save(); iconContext.strokeStyle = 'rgba(100, 140, 230, 0.6)'; for(var i = -2; i < rect.w / 3 + 2; i += 1.5) { if(i < rect.w / 6) x = rect.x + rect.w / 3 + i + rect.w / 8; else x = rect.x + rect.w / 3 + (rect.w / 3 - i) + rect.w / 8; y = rect.y + rect.w / 3 + i; iconContext.beginPath(); iconContext.arc(x, y, 12, 0, Math.PI * 2, false); iconContext.stroke(); } iconContext.restore(); } //第九个icon,画带有网格的圆 function drawEraserIcon(rect) { var rect = ICON_RECTANGLES[ERASER_ICON]; iconContext.save(); iconContext.beginPath(); iconContext.arc(rect.x + rect.w / 2, rect.y + rect.h / 2, ERASER_ICON_RADIUS, 0, Math.PI * 2, false); iconContext.strokeStyle = ERASER_ICON_CIRCLE_COLOR; iconContext.stroke(); // clip() 方法从原始画布中剪切任意形状和尺寸,带网格的区域被绘制在剪切圆所在区域 //一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域), //您也可以在使用 clip() 方法前通过使用 save() //方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法) iconContext.clip(); drawGrid(iconContext, ERASER_ICON_GRID_COLOR, 5, 5); iconContext.restore(); } function drawIcon(rect) { iconContext.save(); iconContext.strokeStyle = ICON_BORDER_STROKE_STYLE; //绘制外围矩形边框 iconContext.strokeRect(rect.x, rect.y, rect.w, rect.h); iconContext.strokeStyle = ICON_STROKE_STYLE; //根据设定的坐标 绘制对应的icon if(rect.y === ICON_RECTANGLES[LINE_ICON].y) drawLineIcon(rect); else if(rect.y === ICON_RECTANGLES[RECTANGLE_ICON].y) drawRectIcon(rect); else if(rect.y === ICON_RECTANGLES[CIRCLE_ICON].y) drawCircleIcon(rect); else if(rect.y === ICON_RECTANGLES[OPEN_PATH_ICON].y) drawOpenPathIcon(rect); else if(rect.y === ICON_RECTANGLES[CLOSED_PATH_ICON].y) drawClosepxathIcon(rect, 20); else if(rect.y === ICON_RECTANGLES[TEXT_ICON].y) drawTextIcon(rect); else if(rect.y === ICON_RECTANGLES[CURVE_ICON].y) drawCurveIcon(rect); else if(rect.y === ICON_RECTANGLES[ERASER_ICON].y) drawEraserIcon(rect); else if(rect.y === ICON_RECTANGLES[SLINKY_ICON].y) drawSlinkyIcon(rect); iconContext.restore(); } function drawIcons() { //清空画布 iconContext.clearRect(0, 0, iconCanvas.width, iconCanvas.height); //遍历ICON_RECTANGLES数组,并且绘制每个icon ICON_RECTANGLES.forEach(function(rect) { iconContext.save(); if(selectedRect === rect) setSelectedIconShadow(); else setIconShadow(); iconContext.fillStyle = ICON_BACKGROUND_STYLE; //绘制已填色的矩形,默认的填充颜色是黑色,填充颜色由fillStyle指定,可以是颜色或者渐变 //绘制放置icon的外层矩形 iconContext.fillRect(rect.x, rect.y, rect.w, rect.h); iconContext.restore(); drawIcon(rect); }); } // function drawOpenPathIconLines(rect) { iconContext.lineTo(rect.x + 13, rect.y + 19); iconContext.lineTo(rect.x + 15, rect.y + 17); iconContext.lineTo(rect.x + 25, rect.y + 12); iconContext.lineTo(rect.x + 35, rect.y + 13); iconContext.lineTo(rect.x + 38, rect.y + 15); iconContext.lineTo(rect.x + 40, rect.y + 17); iconContext.lineTo(rect.x + 39, rect.y + 23); iconContext.lineTo(rect.x + 36, rect.y + 25); iconContext.lineTo(rect.x + 32, rect.y + 27); iconContext.lineTo(rect.x + 28, rect.y + 29); iconContext.lineTo(rect.x + 26, rect.y + 31); iconContext.lineTo(rect.x + 24, rect.y + 33); iconContext.lineTo(rect.x + 22, rect.y + 35); iconContext.lineTo(rect.x + 20, rect.y + 37); iconContext.lineTo(rect.x + 18, rect.y + 39); iconContext.lineTo(rect.x + 16, rect.y + 39); iconContext.lineTo(rect.x + 13, rect.y + 36); iconContext.lineTo(rect.x + 11, rect.y + 34); } function fillIconLowerRight(rect) { iconContext.beginPath(); iconContext.moveTo(rect.x + rect.w, rect.y); iconContext.lineTo(rect.x + rect.w, rect.y + rect.h); iconContext.lineTo(rect.x, rect.y + rect.h); iconContext.closePath(); iconContext.fill(); } function isPointInIconLowerRight(rect, x, y) { iconContext.beginPath(); iconContext.moveTo(rect.x + rect.w, rect.y); iconContext.lineTo(rect.x + rect.w, rect.y + rect.h); iconContext.lineTo(rect.x, rect.y + rect.h); return iconContext.isPointInPath(x, y); } function getIconFunction(rect, loc) { var action; if(rect.y === ICON_RECTANGLES[LINE_ICON].y) action = 'line'; else if(rect.y === ICON_RECTANGLES[RECTANGLE_ICON].y) action = 'rectangle'; else if(rect.y === ICON_RECTANGLES[CIRCLE_ICON].y) action = 'circle'; else if(rect.y === ICON_RECTANGLES[OPEN_PATH_ICON].y) action = 'path'; else if(rect.y === ICON_RECTANGLES[CLOSED_PATH_ICON].y) action = 'pathClosed'; else if(rect.y === ICON_RECTANGLES[CURVE_ICON].y) action = 'curve'; else if(rect.y === ICON_RECTANGLES[TEXT_ICON].y) action = 'text'; else if(rect.y === ICON_RECTANGLES[SLINKY_ICON].y) action = 'slinky'; else if(rect.y === ICON_RECTANGLES[ERASER_ICON].y) action = 'erase'; if(action === 'rectangle' || action === 'circle' || action === 'pathClosed' || action === 'text' || action === 'curve' || action === 'slinky') { doFill = isPointInIconLowerRight(rect, loc.x, loc.y); } return action; } function setIconShadow() { iconContext.shadowColor = SHADOW_COLOR; iconContext.shadowOffsetX = 1; iconContext.shadowOffsetY = 1; iconContext.shadowBlur = 2; } function setSelectedIconShadow() { iconContext.shadowColor = SHADOW_COLOR; iconContext.shadowOffsetX = 4; iconContext.shadowOffsetY = 4; iconContext.shadowBlur = 5; } function selectIcon(rect) { selectedRect = rect; drawIcons(); } // Saving/Restoring the drawing surface.......................... function saveDrawingSurface() { //getImageData(x,y,width,height) 返回ImageData对象,该对象拷贝了画布指定矩形区域的像素数据 //ImageData对象中的每个像素存放了像素的RGBA,对ImageData操作后可以通过 putImageData()将图像数据拷贝回画布 drawingSurfaceImageData = drawingContext.getImageData(0, 0, drawingCanvas.width, drawingCanvas.height); } function restoreDrawingSurface() { drawingContext.putImageData(drawingSurfaceImageData, 0, 0); } // Rubberbands................................................... function updateRubberbandRectangle(loc) { rubberbandW = Math.abs(loc.x - mousedown.x); rubberbandH = Math.abs(loc.y - mousedown.y); if(loc.x > mousedown.x) rubberbandUlhc.x = mousedown.x; else rubberbandUlhc.x = loc.x; if(loc.y > mousedown.y) rubberbandUlhc.y = mousedown.y; else rubberbandUlhc.y = loc.y; } function drawRubberbandRectangle() { drawingContext.strokeRect(rubberbandUlhc.x, rubberbandUlhc.y, rubberbandW, rubberbandH); } function drawRubberbandLine(loc) { drawingContext.beginPath(); drawingContext.moveTo(mousedown.x, mousedown.y); drawingContext.lineTo(loc.x, loc.y); drawingContext.stroke(); } function drawRubberbandCircle(loc) { var angle = Math.atan(rubberbandH / rubberbandW); var radius = rubberbandH / Math.sin(angle); if(mousedown.y === loc.y) { radius = Math.abs(loc.x - mousedown.x); } drawingContext.beginPath(); drawingContext.arc(mousedown.x, mousedown.y, radius, 0, Math.PI * 2, false); drawingContext.stroke(); } function drawRubberband(loc) { drawingContext.save(); drawingContext.strokeStyle = RUBBERBAND_STROKE_STYLE; drawingContext.lineWidth = RUBBERBAND_LINE_WIDTH; if(selectedFunction === 'rectangle') { drawRubberbandRectangle(); } else if(selectedFunction === 'line' || selectedFunction === 'curve') { drawRubberbandLine(loc); } else if(selectedFunction === 'circle') { drawRubberbandCircle(loc); } drawingContext.restore(); } // Eraser........................................................ function setPathForEraser() { drawingContext.beginPath(); drawingContext.moveTo(lastX, lastY); drawingContext.arc(lastX, lastY, ERASER_RADIUS + ERASER_LINE_WIDTH, 0, Math.PI * 2, false); } function setSlinkyAttributes() { drawingContext.lineWidth = lineWidthSelect.value; drawingContext.shadowColor = strokeStyleSelect.value; drawingContext.shadowOffsetX = SLINKY_SHADOW_OFFSET; drawingContext.shadowOffsetY = SLINKY_SHADOW_OFFSET; drawingContext.shadowBlur = SLINKY_SHADOW_BLUR; drawingContext.strokeStyle = strokeStyleSelect.value; } function setEraserAttributes() { drawingContext.lineWidth = ERASER_LINE_WIDTH; drawingContext.shadowColor = ERASER_SHADOW_STYLE; drawingContext.shadowOffsetX = ERASER_SHADOW_OFFSET; drawingContext.shadowOffsetY = ERASER_SHADOW_OFFSET; drawingContext.shadowBlur = ERASER_SHADOW_BLUR; drawingContext.strokeStyle = ERASER_STROKE_STYLE; } function eraseLast() { var x = lastX - ERASER_RADIUS - ERASER_LINE_WIDTH, y = lastY - ERASER_RADIUS - ERASER_LINE_WIDTH, w = ERASER_RADIUS * 2 + ERASER_LINE_WIDTH * 2, h = w, cw = drawingContext.canvas.width, ch = drawingContext.canvas.height; drawingContext.save(); setPathForEraser(); drawingContext.clip(); if(x + w > cw) w = cw - x; if(y + h > ch) h = ch - y; if(x < 0) { x = 0; } if(y < 0) { y = 0; } drawingContext.drawImage( backgroundContext.canvas, x, y, w, h, x, y, w, h); drawingContext.restore(); } function drawEraser(loc) { drawingContext.save(); setEraserAttributes(); drawingContext.beginPath(); drawingContext.arc(loc.x, loc.y, ERASER_RADIUS, 0, Math.PI * 2, false); drawingContext.clip(); drawingContext.stroke(); drawingContext.restore(); } function drawSlinky(loc) { drawingContext.save(); setSlinkyAttributes(); drawingContext.beginPath(); drawingContext.arc(loc.x, loc.y, ERASER_RADIUS, 0, Math.PI * 2, false); drawingContext.clip(); drawingContext.strokeStyle = strokeStyleSelect.value; drawingContext.stroke(); if(doFill) { drawingContext.shadowColor = undefined; drawingContext.shadowOffsetX = 0; drawingContext.globalAlpha = 0.2; drawingContext.fill(); } drawingContext.restore(); } // Finish drawing lines, circles, and rectangles................. function finishDrawingLine(loc) { drawingContext.beginPath(); drawingContext.moveTo(mousedown.x, mousedown.y); drawingContext.lineTo(loc.x, loc.y); drawingContext.stroke(); } function finishDrawingCircle(loc) { var angle = Math.atan(rubberbandH / rubberbandW), radius = rubberbandH / Math.sin(angle); if(mousedown.y === loc.y) { radius = Math.abs(loc.x - mousedown.x); } drawingContext.beginPath(); drawingContext.arc(mousedown.x, mousedown.y, radius, 0, Math.PI * 2, false); if(doFill) { drawingContext.fill(); } drawingContext.stroke(); } function finishDrawingRectangle() { if(rubberbandW > 0 && rubberbandH > 0) { if(doFill) { drawingContext.fillRect(rubberbandUlhc.x, rubberbandUlhc.y, rubberbandW, rubberbandH) } drawingContext.strokeRect(rubberbandUlhc.x, rubberbandUlhc.y, rubberbandW, rubberbandH); } } // Drawing curves................................................ function drawControlPoint() { drawingContext.save(); drawingContext.strokeStyle = CONTROL_POINT_STROKE_STYLE; drawingContext.fillStyle = CONTROL_POINT_FILL_STYLE; drawingContext.lineWidth = 1.0; drawingContext.beginPath(); drawingContext.arc(controlPoint.x, controlPoint.y, CONTROL_POINT_RADIUS, 0, Math.PI * 2, false); drawingContext.stroke(); drawingContext.fill(); drawingContext.restore(); } function startEditingCurve(loc) { if(loc.x != mousedown.x || loc.y != mousedown.y) { drawingCanvas.style.cursor = 'pointer'; curveStart.x = mousedown.x; curveStart.y = mousedown.y; curveEnd.x = loc.x; curveEnd.y = loc.y; controlPoint.x = (curveStart.x + curveEnd.x) / 2; controlPoint.y = (curveStart.y + curveEnd.y) / 2; drawControlPoint(); editingCurve = true; if(showCurveInstructions) curveInstructions.style.display = 'inline'; } } function drawCurve() { drawingContext.beginPath(); drawingContext.moveTo(curveStart.x, curveStart.y); drawingContext.quadraticCurveTo(controlPoint.x, controlPoint.y, curveEnd.x, curveEnd.y); drawingContext.stroke(); } function finishDrawingCurve() { drawingCanvas.style.cursor = 'crosshair'; restoreDrawingSurface(); drawCurve(); if(doFill) { drawingContext.fill(); } } // Guidewires.................................................... function drawHorizontalLine(y) { drawingContext.beginPath(); drawingContext.moveTo(0, y + 0.5); drawingContext.lineTo(drawingCanvas.width, y + 0.5); drawingContext.stroke(); } function drawVerticalLine(x) { drawingContext.beginPath(); drawingContext.moveTo(x + 0.5, 0); drawingContext.lineTo(x + 0.5, drawingCanvas.height); drawingContext.stroke(); } function drawGuidewires(x, y) { drawingContext.save(); drawingContext.strokeStyle = 'rgba(0,0,230,0.4)'; drawingContext.lineWidth = 0.5; drawVerticalLine(x); drawHorizontalLine(y); drawingContext.restore(); } // Event handling functions...................................... function windowToCanvas(canvas, x, y) { //getBoundingClientRect() 返回canvas所在的矩形对象,包含left,top,right,bottom,分别表示元素各边与页面上边和左边的距离 var bbox = canvas.getBoundingClientRect(); return { x: x - bbox.left * (canvas.width / bbox.width), y: y - bbox.top * (canvas.height / bbox.height) }; } function mouseDownOrTouchStartInControlCanvas(loc) { if(editingText) { editingText = false; eraseTextCursor(); hideKeyboard(); } else if(editingCurve) { editingCurve = false; restoreDrawingSurface(); } ICON_RECTANGLES.forEach(function(rect) { iconContext.beginPath(); iconContext.rect(rect.x, rect.y, rect.w, rect.h); if(iconContext.isPointInPath(loc.x, loc.y)) { selectIcon(rect, loc); selectedFunction = getIconFunction(rect, loc); if(selectedFunction === 'text') { drawingCanvas.style.cursor = 'text'; } else { drawingCanvas.style.cursor = 'crosshair'; } } }); }; // Key event handlers............................................ function backspace() { restoreDrawingSurface(); currentText = currentText.slice(0, -1); eraseTextCursor(); }; function enter() { finishDrawingText(); mousedown.y += drawingContext.measureText('W').width; saveDrawingSurface(); startDrawingText(); }; function insert(key) { currentText += key; restoreDrawingSurface(); drawCurrentText(); drawTextCursor(); }; document.onkeydown = function(e) { if(e.ctrlKey || e.metaKey || e.altKey) return; if(e.keyCode === 8) { // backspace e.preventDefault(); backspace(); } else if(e.keyCode === 13) { // enter e.preventDefault(); enter(); } } document.onkeypress = function(e) { var key = String.fromCharCode(e.which); if(e.ctrlKey || e.metaKey || e.altKey) return; if(editingText && e.keyCode !== 8) { e.preventDefault(); insert(key); } } function eraseTextCursor() { restoreDrawingSurface(); drawCurrentText(); } function drawCurrentText() { if(doFill) drawingContext.fillText(currentText, mousedown.x, mousedown.y); drawingContext.strokeText(currentText, mousedown.x, mousedown.y); } function drawTextCursor() { var widthMetric = drawingContext.measureText(currentText), heightMetric = drawingContext.measureText('W'), cursorLoc = { x: mousedown.x + widthMetric.width, y: mousedown.y - heightMetric.width + 5 }; drawingContext.beginPath(); drawingContext.moveTo(cursorLoc.x, cursorLoc.y); drawingContext.lineTo(cursorLoc.x, cursorLoc.y + heightMetric.width - 12); drawingContext.stroke(); } function startDrawingText() { editingText = true; currentText = ''; drawTextCursor(); showKeyboard(); } function finishDrawingText() { restoreDrawingSurface(); drawCurrentText(); } function mouseDownOrTouchStartInDrawingCanvas(loc) { dragging = true; if(editingText) { finishDrawingText(); } else if(editingCurve) { if(drawingContext.isPointInPath(loc.x, loc.y)) { draggingControlPoint = true; } else { restoreDrawingSurface(); } editingCurve = false; } if(!draggingControlPoint) { saveDrawingSurface(); mousedown.x = loc.x; mousedown.y = loc.y; if(selectedFunction === 'path' || selectedFunction === 'pathClosed') { drawingContext.beginPath(); drawingContext.moveTo(loc.x, loc.y); } else if(selectedFunction === 'text') { startDrawingText(); } else { editingText = false; } lastX = loc.x; lastY = loc.y; } } function moveControlPoint(loc) { controlPoint.x = loc.x; controlPoint.y = loc.y; } function mouseMoveOrTouchMoveInDrawingCanvas(loc) { if(draggingControlPoint) { restoreDrawingSurface(); moveControlPoint(loc); drawingContext.save(); drawingContext.strokeStyle = RUBBERBAND_STROKE_STYLE; drawingContext.lineWidth = RUBBERBAND_LINE_WIDTH; drawCurve(); drawControlPoint(); drawingContext.restore(); } else if(dragging) { if(selectedFunction === 'erase') { eraseLast(); drawEraser(loc); } else if(selectedFunction === 'slinky') { drawSlinky(loc); } else if(selectedFunction === 'path' || selectedFunction === 'pathClosed') { drawingContext.lineTo(loc.x, loc.y); drawingContext.stroke(); } else { // For lines, circles, rectangles, and curves, draw rubberbands restoreDrawingSurface(); updateRubberbandRectangle(loc); drawRubberband(loc); } lastX = loc.x; lastY = loc.y; lastRect.w = rubberbandW; lastRect.h = rubberbandH; } if(dragging || draggingControlPoint) { if(selectedFunction === 'line' || selectedFunction === 'rectangle' || selectedFunction === 'circle') { drawGuidewires(loc.x, loc.y); } } }; function enpxath(loc) { drawingContext.lineTo(loc.x, loc.y); drawingContext.stroke(); if(selectedFunction === 'pathClosed') { drawingContext.closePath(); if(doFill) { drawingContext.fill(); } drawingContext.stroke(); } } function mouseUpOrTouchEndInDrawingCanvas(loc) { if(selectedFunction !== 'erase' && selectedFunction !== 'slinky') { restoreDrawingSurface(); } if(draggingControlPoint) { moveControlPoint(loc); finishDrawingCurve(); draggingControlPoint = false; } else if(dragging) { if(selectedFunction === 'erase') { eraseLast(); } else if(selectedFunction === 'path' || selectedFunction === 'pathClosed') { enpxath(loc); } else { if(selectedFunction === 'line') finishDrawingLine(loc); else if(selectedFunction === 'rectangle') finishDrawingRectangle(); else if(selectedFunction === 'circle') finishDrawingCircle(loc); else if(selectedFunction === 'curve') startEditingCurve(loc); } } dragging = false; }; // Control canvas event handlers................................. iconCanvas.onmousedown = function(e) { var x = e.x || e.clientX, y = e.y || e.clientY, loc = windowToCanvas(iconCanvas, x, y); e.preventDefault(); mouseDownOrTouchStartInControlCanvas(loc); } iconCanvas.addEventListener('touchstart', function(e) { if(e.touches.length === 1) { e.preventDefault(); mouseDownOrTouchStartInControlCanvas( windowToCanvas(iconCanvas, e.touches[0].clientX, e.touches[0].clientY)); } }); // Drawing canvas event handlers................................. drawingCanvas.onmousedown = function(e) { console.log('onmousedown:'+' x='+e.x+' y='+e.y) var x = e.x || e.clientX, y = e.y || e.clientY; e.preventDefault(); mouseDownOrTouchStartInDrawingCanvas( windowToCanvas(drawingCanvas, x, y)); } drawingCanvas.ontouchstart = function(e) { console.log('ontouchstart:'+' x='+e.x+' y='+e.y) if(e.touches.length === 1) { e.preventDefault(); mouseDownOrTouchStartInDrawingCanvas( windowToCanvas(drawingCanvas, e.touches[0].clientX, e.touches[0].clientY)); } } drawingCanvas.ontouchmove = function(e) { console.log('ontouchmove:'+' x='+e.x+' y='+e.y) if(e.touches.length === 1) { mouseMoveOrTouchMoveInDrawingCanvas( windowToCanvas(drawingCanvas, e.touches[0].clientX, e.touches[0].clientY)); } } drawingCanvas.ontouchend = function(e) { var loc; console.log('ontouchend:'+' x='+e.x+' y='+e.y) if(e.changedTouches.length === 1) { loc = windowToCanvas(drawingCanvas, e.changedTouches[0].clientX, e.changedTouches[0].clientY); mouseUpOrTouchEndInDrawingCanvas(loc); } } drawingCanvas.onmousemove = function(e) { //console.log('onmousemove:'+' x='+e.x+' y='+e.y) var x = e.x || e.clientX, y = e.y || e.clientY, loc = windowToCanvas(drawingCanvas, x, y); e.preventDefault(); mouseMoveOrTouchMoveInDrawingCanvas(loc); } drawingCanvas.onmouseup = function(e) { console.log('onmouseup:'+' x='+e.x+' y='+e.y) var x = e.x || e.clientX, y = e.y || e.clientY, loc = windowToCanvas(drawingCanvas, x, y); e.preventDefault(); mouseUpOrTouchEndInDrawingCanvas(loc); } // Control event handlers........................................ //边框样式选择器 strokeStyleSelect.onchange = function(e) { drawingContext.strokeStyle = strokeStyleSelect.value; }; //填充样式选择器 fillStyleSelect.onchange = function(e) { drawingContext.fillStyle = fillStyleSelect.value; }; //线宽选择器 lineWidthSelect.onchange = function(e) { drawingContext.lineWidth = lineWidthSelect.value; /* var c = drawingContext.canvas, sw = c.width, sh = c.height, dw = sw * lineWidthSelect.value, dh = sh * lineWidthSelect.value; drawingContext.scale(lineWidthSelect.value, lineWidthSelect.value); drawingContext.drawImage(c, 0, 0); */ }; //清空当前画布 并且或者网格 eraseAllButton.onclick = function(e) { drawingContext.clearRect(0, 0, drawingCanvas.width, drawingCanvas.height); drawGrid(drawingContext, GRID_LINE_COLOR, 10, 10); saveDrawingSurface(); rubberbandW = rubberbandH = 0; }; curveInstructionsOkayButton.onclick = function(e) { curveInstructions.style.display = 'none'; }; curveInstructionsNoMoreButton.onclick = function(e) { curveInstructions.style.display = 'none'; showCurveInstructions = false; }; //保存当前绘制的画面,即快照抓取 snapshotButton.onclick = function(e) { var dataUrl; if(snapshotButton.value === 'Take snapshot') { dataUrl = drawingCanvas.toDataURL(); snapshotImageElement.src = dataUrl; snapshotImageElement.style.display = 'inline'; snapshotInstructions.style.display = 'inline'; drawingCanvas.style.display = 'none'; iconCanvas.style.display = 'none'; controls.style.display = 'none'; snapshotButton.value = 'Back to Paint'; } else { snapshotButton.value = 'Take snapshot'; drawingCanvas.style.display = 'inline'; iconCanvas.style.display = 'inline'; controls.style.display = 'inline'; snapshotImageElement.style.display = 'none'; snapshotInstructions.style.display = 'none'; } }; function drawBackground() { backgroundContext.canvas.width = drawingContext.canvas.width; backgroundContext.canvas.height = drawingContext.canvas.height; drawGrid(backgroundContext, GRID_LINE_COLOR, 10, 10); } // 初始化相关变量................................................ iconContext.strokeStyle = ICON_STROKE_STYLE; iconContext.fillStyle = ICON_FILL_STYLE; iconContext.font = '48px Palatino'; iconContext.textAlign = 'center'; iconContext.textBaseline = 'middle'; drawingContext.font = '48px Palatino'; drawingContext.textBaseline = 'bottom'; drawingContext.strokeStyle = strokeStyleSelect.value; drawingContext.fillStyle = fillStyleSelect.value; drawingContext.lineWidth = lineWidthSelect.value; selectedRect = ICON_RECTANGLES[SLINKY_ICON]; selectedFunction = 'slinky'; //通过e.preventDefault()阻止此dom的默认滚动事件, //touchmove事件,当手指在屏幕上滑动的时候连续的触发,在这个发生期间,调用preventDefault()可以阻止滚动 document.body.addEventListener('touchmove', function(e) { e.preventDefault(); }, false); //绘制左侧icon工具栏 drawIcons(); drawGrid(drawingContext, GRID_LINE_COLOR, 10, 10); </script> </body> </html> 演示地址:http://sandbox.runjs.cn/show/guaoa3vd

    转载请注明原文地址: https://ju.6miu.com/read-673875.html

    最新回复(0)