本文源码转载于:
项目文件预览 - CanvasStudy - GitCode
效果展示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>拖拽画直线</title> </head> <style> body { background: #eeeeee; } #controls { position: absolute; left: 25px; top: 25px; } #canvas { background: #ffffff; cursor: pointer; margin-left: 10px; margin-top: 10px; box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5); } input[type='button'] { background: cornflowerblue; cursor: pointer; border-radius: 10px; } </style> <body> <canvas id="canvas" width="600" height="400"></canvas> <div id="controls"> Stroke Color:<select id="strokeStyleSelect" style="margin-right: 20px"> <option value="red">red</option> <option value="green">green</option> <option value="blue">blue</option> <option value="orange">orange</option> </select> Guidewires(坐标辅助线): <input type="checkbox" id="guidewireCheckbox" checked style="margin-right: 20px"> 矩形选框 <input type="checkbox" id="RectangleCheckbox" style="margin-right: 20px"> <input type="button" id="eraseAllButton" value="清空画布"> </div> <p>设置笔触颜色,是否添加辅助线,是否使用矩形选框,然后再画布上绘画。如有需要,使用"清除画布"按钮</p> </body> <script> let canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), eraseAllButton = document.getElementById('eraseAllButton'), guidewireCheckbox = document.getElementById('guidewireCheckbox'), strokeStyleSelect = document.getElementById('strokeStyleSelect'), RectangleCheckbox = document.getElementById('RectangleCheckbox'), drawingSurfaceImageData, mousedown = {}, rubberbandRect = {}, dragging = false, guidewires = guidewireCheckbox.checked, rectangle = RectangleCheckbox.checked; //Function…… /** * 画网格 * @param color * @param stepX * @param stepY */ function drawGrid(color, stepX, stepY) { context.save(); context.lineWidth = 0.5; context.strokeStyle = color; for (var i = stepX + 0.5; i < canvas.width; i += stepX) { context.beginPath(); context.moveTo(i, 0 + 0.5); context.lineTo(i, canvas.height + 0.5) context.stroke(); } for (var i = stepY + 0.5; i < canvas.height; i += stepY) { context.beginPath(); context.moveTo(0 + 0.5, i); context.lineTo(canvas.width, i); context.stroke(); } context.restore(); } /** * 坐标转化为canvas坐标 * @param x * @param y */ function windowToCanvas(x, y) { //返回元素的大小以及位置 var bbox = canvas.getBoundingClientRect(); return {x: x - bbox.left * (canvas.width / bbox.width), y: y - bbox.top * (canvas.height / bbox.height)}; } //保存和恢复绘图面板 function saveDrawingSurface() { drawingSurfaceImageData = context.getImageData(0, 0, canvas.width, canvas.height); } function restoreDrawingSurface() { context.putImageData(drawingSurfaceImageData, 0, 0); } //Rubber bands /** * 更新橡皮筋矩形 * @param loc */ function updateRubberbandRectangle(loc) { rubberbandRect.width = Math.abs(loc.x - mousedown.x); rubberbandRect.height = Math.abs(loc.y - mousedown.y); //从左往右拉,和从右往左拉的两种情况。主要是判断左边的位置 //因为从左往右拉的时候,左边x坐标不变 //从右往左拉的时候,左边线的x坐标需要跟着鼠标移动 if (loc.x > mousedown.x) rubberbandRect.left = mousedown.x; else rubberbandRect.left = loc.x; if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y; else rubberbandRect.top = loc.y; context.save(); context.beginPath(); context.rect(rubberbandRect.left, rubberbandRect.top, rubberbandRect.width, rubberbandRect.height); context.stroke(); context.restore(); } /** * 画可以看得见的线 * @param loc */ function drawRubberbandShape(loc) { context.beginPath(); context.moveTo(mousedown.x, mousedown.y); context.lineTo(loc.x, loc.y); context.stroke(); } /** * 更新橡皮筋 * @param loc */ function updateRubberband(loc) { //如果判断需要画矩形,就执行画矩形方法 if (rectangle) { updateRubberbandRectangle(loc); } //执行画直线的方法,这里没加if是为了让读者容易理解矩形的绘制方法,因为"draw矩形"是基于"draw直线"的 drawRubberbandShape(loc); } //Guidewires辅助线 /** * 画水平辅助线,占整个canvas宽度 * @param y */ function drawHorizontalLine(y) { context.beginPath(); context.moveTo(0, y + 0.5); context.lineTo(canvas.width, y + 0.5); context.stroke(); } /** * 画垂直辅助线,占整个canvas高度 * @param x */ function drawVerticalLine(x) { context.beginPath(); context.moveTo(x + 0.5, 0); context.lineTo(x + 0.5, context.canvas.height); context.stroke(); } /** * 画辅助线,并设置属性 * @param x * @param y */ function drawGuidewires(x, y) { context.save(); context.strokeStyle = 'rgba(0,0,230,0.4)'; context.lineWidth = 0.5; drawVerticalLine(x); drawHorizontalLine(y); context.restore(); } //事件 /** * 鼠标按下的时候,记录坐标,并设置为拖拽状态 * @param e */ canvas.onmousedown = function (e) { var loc = windowToCanvas(e.clientX, e.clientY); e.preventDefault(); saveDrawingSurface(); mousedown.x = loc.x; mousedown.y = loc.y; dragging = true; }; /** * (鼠标按下之后)鼠标移动的时候 * 判断拖拽中:更新当前连线的位置 * 判断辅助线显示:添加辅助线 * @param e */ canvas.onmousemove = function (e) { var loc; if (dragging) { e.preventDefault(); loc = windowToCanvas(e.clientX, e.clientY); restoreDrawingSurface(); updateRubberband(loc); if (guidewires) { drawGuidewires(loc.x, loc.y); } } }; /** * (拖拽完成后)当鼠标松开时,重新获取本点坐标,清除之前的"跟随鼠标移动的线",更新连线,取消拖拽状态 * @param e */ canvas.onmouseup = function (e) { loc = windowToCanvas(e.clientX, e.clientY); restoreDrawingSurface(); updateRubberband(loc); dragging = false; }; //控制器的事件 /** * 清除所有画布图像 * @param ev */ eraseAllButton.onclick = function (ev) { context.clearRect(0, 0, canvas.width, canvas.height); drawGrid('lightgray', 10, 10); saveDrawingSurface(); }; strokeStyleSelect.onchange = function (ev) { context.strokeStyle = strokeStyleSelect.value; }; guidewireCheckbox.onchange = function (ev) { guidewires = guidewireCheckbox.checked; }; RectangleCheckbox.onchange = function (ev) { rectangle = RectangleCheckbox.checked; }; //init context.strokeStyle = strokeStyleSelect.value; drawGrid('lightgray', 10, 10); </script> </html>