canvas图片编辑

4,082 阅读3分钟

最近学习canvas,用canvas做了一个简单的图片编辑器! demo

画板功能开源 画板功能示例

先看代码

<canvas id="drawingCanvas" width="300", height="300" style="border: 1px solid"></canvas>
 <div>
   <button onclick="Undo()">撤销</button>
   <button onclick="checkRect()">正方形</button>
   <button onclick="checkCircle()">圆形</button>
   <button onclick="checkArrow()">箭头</button>
   <button onclick="checkLine()">画笔</button>
</div>
var canvas;
var context;
var startX;
var startY;
var endX;
var endY;
var pattern = 'pen';
var memory = [];
var imageSrc = 'http://7sbq8w.com1.z0.glb.clouddn.com/box.png';
var imageObj;
//imageObj = new Image();
//imageObj.src = imageSrc;
// 初始化
window.onload = function() {
    // 获取画布
    canvas = document.getElementById("drawingCanvas");
    context = canvas.getContext("2d");
		imageObj = new Image();
		imageObj.src = imageSrc;
    imageObj.onload = function () {
    	drawImage();
    }
    // 画布添加鼠标事件
    canvas.onmousedown = startDrawing;
    canvas.onmouseup = draw;
    canvas.onmouseout = draw;
    canvas.onmousemove = strokeConnection;
};

// 记录当前是否在画图
var isDrawing = false;

// 开始画图
function startDrawing(e) {
    isDrawing = true;
    context.strokeStyle = 'red';
    // 把画笔移动到鼠标位置
    startX = e.pageX - canvas.offsetLeft;
    startY = e.pageY - canvas.offsetTop;
    if (pattern == 'arrow') {
        beginPoint.x = startX;
        beginPoint.y = startY;
    } else if (pattern == 'pen') {
        memory.push({lineArray: [], pattern: pattern});
    } else {
        context.moveTo(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
    }
}

function drawImage() {
    if (imageObj.width / imageObj.height > canvas.width / canvas.height) {
        context.drawImage(imageObj, 0, 0, canvas.width, imageObj.height * canvas.width / imageObj.width);
    } else {
        context.drawImage(imageObj, 0, 0, imageObj.width * canvas.height / canvas.height, canvas.height);
    }
}

//画图
function draw(e) {
    if (isDrawing == true && pattern != 'pen') {
        endX = e.pageX - canvas.offsetLeft;
        endY = e.pageY - canvas.offsetTop;
        if (pattern == 'rect') {
            drawRect();
        }
        if (pattern == 'circle') {
            drawArc();
        }
        if (pattern == 'arrow') {
            drawArrow();
        }
        memory.push({startX: startX, startY: startY, endX: endX, endY: endY, pattern: pattern});
        isDrawing = false;
    }

    if (isDrawing == true && pattern == 'pen') {
        isDrawing = false;
    }
}

function strokeConnection(e) {
    if (isDrawing == true && pattern == 'pen') {
        endX = e.pageX - canvas.offsetLeft;
        endY = e.pageY - canvas.offsetTop;
        drawLine();
        memory[memory.length - 1].lineArray.push({startX: startX, startY: startY, endX: endX, endY: endY});
        startX = endX;
        startY = endY;
    }
}

function drawLine(data) {
    context.beginPath();
    if (data) {
        context.moveTo(data.startX, data.startY);
        context.lineTo(data.endX, data.endY);
    } else {
        context.moveTo(startX, startY);
        context.lineTo(endX, endY);
    }
    context.closePath();
    context.stroke();
    context.restore();
}

function drawArrow(data) {
    if (data) {
        beginPoint.x = data.startX;
        beginPoint.y = data.startY;
        stopPoint.x = data.endX;
        stopPoint.y = data.endY;
        Plot.arrowCoord(beginPoint, stopPoint);
        Plot.sideCoord();
        Plot.drawArrow();
    } else {
        stopPoint.x = endX;
        stopPoint.y = endY;
        Plot.arrowCoord(beginPoint, stopPoint);
        Plot.sideCoord();
        Plot.drawArrow();
    }
}

function drawArc(data) {
    if (data) {
        var radiusX = (data.endX - data.startX)/2;
        var radiusY = (data.endY - data.startY)/2;
        ParamEllipse(data.startX + radiusX, data.startY + radiusY, Math.abs(radiusX), Math.abs(radiusY));
    } else {
        var radiusX = (endX - startX)/2;
        var radiusY = (endY - startY)/2;
        ParamEllipse(startX + radiusX, startY + radiusY, Math.abs(radiusX), Math.abs(radiusY));
    }
}

function drawRect(data) {
    context.beginPath();
    if (data) {
        context.strokeRect(data.startX, data.startY, data.endX - data.startX, data.endY - data.startY);
    } else {
        context.strokeRect(startX, startY, endX - startX, endY - startY);
    }
    context.closePath();
    context.stroke();
}

// 清除画布
function clearCanvas() {
    context.clearRect(0, 0, canvas.width, canvas.height);
}

// 保存画布
function saveCanvas() {
    alert(canvas.toDataURL());
    //return canvas.toDataURL();
}

//撤销
function Undo() {
    clearCanvas();
    drawImage();
    var stroke = memory.pop();
    if (stroke) {
        redraw();
    }
}

//重画
function redraw() {
    memory.forEach(function (item) {
        if (item.pattern == 'rect') {
            drawRect(item);
        }
        if (item.pattern == 'circle') {
            drawArc(item);
        }
        if (item.pattern == 'arrow') {
            drawArrow(item);
        }
        if (item.pattern == 'pen') {
            item.lineArray.forEach(function (it) {
                drawLine(it);
            })
        }
    })
}

//切换画矩形
function checkRect() {
    pattern = 'rect';
}

//切换画圆形
function checkCircle() {
    pattern = 'circle';
}

//切换画箭头
function checkArrow() {
    pattern = 'arrow';
}

//切换画线
function checkLine() {
    pattern = 'pen';
}

//椭圆
function ParamEllipse(x, y, a, b) {
    var step = (a > b) ? 1 / a : 1 / b;
    context.beginPath();
    context.moveTo(x + a, y);
    for (var i = 0; i < 2 * Math.PI; i +=step) {
        context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
    }
    context.closePath();
    context.stroke();
};


//箭头
var beginPoint = {};
var stopPoint = {};
var polygonVertex = [];
var CONST = {
    edgeLen: 50,
    angle: 25
};
//封装的作图对象
var Plot = {

    angle: "",

    //在CONST中定义的edgeLen以及angle参数
    //短距离画箭头的时候会出现箭头头部过大,修改:
    dynArrowSize: function() {
        var x = stopPoint.x - beginPoint.x,
            y = stopPoint.y - beginPoint.y,
            length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        if (length < 250) {
            CONST.edgeLen = CONST.edgeLen/2;
            CONST.angle = CONST.angle/2;
        }
        else if(length<500){
            CONST.edgeLen=CONST.edgeLen*length/500;
            CONST.angle=CONST.angle*length/500;
        }
    },

    //getRadian 返回以起点与X轴之间的夹角角度值
    getRadian: function(beginPoint, stopPoint) {
        Plot.angle = Math.atan2(stopPoint.y - beginPoint.y, stopPoint.x - beginPoint.x) / Math.PI * 180;
        paraDef(50,25);
        Plot.dynArrowSize();
    },

    //获得箭头底边两个点
    arrowCoord: function(beginPoint, stopPoint) {
        polygonVertex[0] = beginPoint.x;
        polygonVertex[1] = beginPoint.y;
        polygonVertex[6] = stopPoint.x;
        polygonVertex[7] = stopPoint.y;
        Plot.getRadian(beginPoint, stopPoint);
        polygonVertex[8] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle + CONST.angle));
        polygonVertex[9] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle + CONST.angle));
        polygonVertex[4] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle - CONST.angle));
        polygonVertex[5] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle - CONST.angle));
    },

    //获取另两个底边侧面点
    sideCoord: function() {
        var midpoint = {};
        // midpoint.x = polygonVertex[6] - (CONST.edgeLen * Math.cos(Plot.angle * Math.PI / 180));
        // midpoint.y = polygonVertex[7] - (CONST.edgeLen * Math.sin(Plot.angle * Math.PI / 180));
        midpoint.x=(polygonVertex[4]+polygonVertex[8])/2;
        midpoint.y=(polygonVertex[5]+polygonVertex[9])/2;
        polygonVertex[2] = (polygonVertex[4] + midpoint.x) / 2;
        polygonVertex[3] = (polygonVertex[5] + midpoint.y) / 2;
        polygonVertex[10] = (polygonVertex[8] + midpoint.x) / 2;
        polygonVertex[11] = (polygonVertex[9] + midpoint.y) / 2;
    },

    //画箭头
    drawArrow: function() {
        context.beginPath();
        context.fillStyle = 'red';
        context.moveTo(polygonVertex[0], polygonVertex[1]);
        context.lineTo(polygonVertex[2], polygonVertex[3]);
        context.lineTo(polygonVertex[4], polygonVertex[5]);
        context.lineTo(polygonVertex[6], polygonVertex[7]);
        context.lineTo(polygonVertex[8], polygonVertex[9]);
        context.lineTo(polygonVertex[10], polygonVertex[11]);
        context.closePath();
        context.fill();
    }
};

//自定义参数
function paraDef(edgeLen, angle) {
    CONST.edgeLen = edgeLen;
    CONST.angle = angle;
}

图片编辑器的主要步骤: 1.创建画布; 2.监听鼠标事件获取位置坐标; 3.画线,画正方形,画圆,画箭头的基础方法; 4.撤销 5.保存

这里主要说一下撤销的实现,其实就是创建一个数组,保存每次编辑的坐标路径,当点击撤销时,清空画布,pop出数组的最新记录,然后根据记忆的数组路径重新画图。