如何使用JavaScript在画布上绘图?

Mar*_*oma 20 javascript canvas

如何在画布元素上自由绘制(使用我的鼠标/手指),就像你可以用铅笔画画一样?

关于这个问题

有很多问题需要在画布上实现免费手绘:

所以我认为提出一个参考问题是一个好主意,每个答案都是社区维基,并且包含对一个JavaScript库/纯JavaScript如何在画布上绘画的解释.

答案的结构

答案应该是社区维基并使用以下模板:

## [Name of library](Link to project page)
### Simple example
    A basic, complete example. That means it has to contain HTML 
    and JavaScript. You can start with this:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Simple example</title>
        <script type='text/javascript' src='http://cdnjs.com/[your library]'></script>
        <style type='text/css'>
            #sheet {
                border:1px solid black;
            }
        </style>
        <script type='text/javascript'>
            window.onload=function(){
                // TODO: Adjust
            }
        </script>
      </head>
      <body>
        <canvas id="sheet" width="400" height="400"></canvas>
      </body>
    </html>

    If possible, this example should work with both, mouse and touch events.

[JSFiddle](Link to code on jsfiddle.net)

This solution works with:

<!-- Please test it the following way: Write "Hello World"
  Problems that you test this way are:
   * Does it work at all?
   * Are lines separated?
   * Does it get slow when you write too much?
-->

* Desktop computers:
  * [Browser + Version list]
* Touch devices:
  * [Browser + Version list] on [Device name]

### Import / Export
Some explanations how to import / export user drawn images.

### Line smoothing
Explanations about how to manipulate the line the user draws. 
This can include:
  * Bézier curves
  * Controlling thickness of lines
Run Code Online (Sandbox Code Playgroud)

Mar*_*oma 9

Fabric.js

<!DOCTYPE html>
<html>
  <head>
    <title>Simple example</title>
    <script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js'></script>
    <style type='text/css'>
        #sheet {
            border:1px solid black;
        }
    </style>
    <script type='text/javascript'>
        window.onload=function(){
            var canvas = new fabric.Canvas('sheet');
            canvas.isDrawingMode = true;
            canvas.freeDrawingBrush.width = 5;
            canvas.freeDrawingBrush.color = "#ff0000";
        }
    </script>
  </head>
  <body>
    <canvas id="sheet" width="400" height="400"></canvas>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

JSFiddle - 演示

  • 线的宽度可以用canvas.freeDrawingBrush.width.
  • 线条的颜色可以用canvas.freeDrawingBrush.color.

此解决方案适用于:

  • 台式电脑:
    • Chrome 33
    • Firefox 28
  • 触控设备:
    • Nexus 4上的Chrome 34
    • Nexus 4上的Opera 20
    • Nexus 4上的Firefox 28

进出口

只能通过序列化完整的画布来实现,请参阅教程

线条平滑

是自动完成的,似乎无法停用它.


Mar*_*oma 8

简单的JavaScript

简单的例子

<!DOCTYPE html>
<html>
  <head>
    <title>Simple example</title>
    <style type='text/css'>
        #sheet {
            border:1px solid black;
        }
    </style>
  </head>
  <body>
    <canvas id="sheet" width="400" height="400"></canvas>
    <script type='text/javascript'>
/*jslint browser:true */
"use strict";
var context = document.getElementById('sheet').getContext("2d");
var canvas = document.getElementById('sheet');
context = canvas.getContext("2d");
context.strokeStyle = "#ff0000";
context.lineJoin = "round";
context.lineWidth = 5;

var clickX = [];
var clickY = [];
var clickDrag = [];
var paint;

/**
 * Add information where the user clicked at.
 * @param {number} x
 * @param {number} y
 * @return {boolean} dragging
 */
function addClick(x, y, dragging) {
    clickX.push(x);
    clickY.push(y);
    clickDrag.push(dragging);
}

/**
 * Redraw the complete canvas.
 */
function redraw() {
    // Clears the canvas
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);

    for (var i = 0; i < clickX.length; i += 1) {
        if (!clickDrag[i] && i == 0) {
            context.beginPath();
            context.moveTo(clickX[i], clickY[i]);
            context.stroke();
        } else if (!clickDrag[i] && i > 0) {
            context.closePath();

            context.beginPath();
            context.moveTo(clickX[i], clickY[i]);
            context.stroke();
        } else {
            context.lineTo(clickX[i], clickY[i]);
            context.stroke();
        }
    }
}

/**
 * Draw the newly added point.
 * @return {void}
 */
function drawNew() {
    var i = clickX.length - 1
    if (!clickDrag[i]) {
        if (clickX.length == 0) {
            context.beginPath();
            context.moveTo(clickX[i], clickY[i]);
            context.stroke();
        } else {
            context.closePath();

            context.beginPath();
            context.moveTo(clickX[i], clickY[i]);
            context.stroke();
        }
    } else {
        context.lineTo(clickX[i], clickY[i]);
        context.stroke();
    }
}

function mouseDownEventHandler(e) {
    paint = true;
    var x = e.pageX - canvas.offsetLeft;
    var y = e.pageY - canvas.offsetTop;
    if (paint) {
        addClick(x, y, false);
        drawNew();
    }
}

function touchstartEventHandler(e) {
    paint = true;
    if (paint) {
        addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, false);
        drawNew();
    }
}

function mouseUpEventHandler(e) {
    context.closePath();
    paint = false;
}

function mouseMoveEventHandler(e) {
    var x = e.pageX - canvas.offsetLeft;
    var y = e.pageY - canvas.offsetTop;
    if (paint) {
        addClick(x, y, true);
        drawNew();
    }
}

function touchMoveEventHandler(e) {
    if (paint) {
        addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, true);
        drawNew();
    }
}

function setUpHandler(isMouseandNotTouch, detectEvent) {
    removeRaceHandlers();
    if (isMouseandNotTouch) {
        canvas.addEventListener('mouseup', mouseUpEventHandler);
        canvas.addEventListener('mousemove', mouseMoveEventHandler);
        canvas.addEventListener('mousedown', mouseDownEventHandler);
        mouseDownEventHandler(detectEvent);
    } else {
        canvas.addEventListener('touchstart', touchstartEventHandler);
        canvas.addEventListener('touchmove', touchMoveEventHandler);
        canvas.addEventListener('touchend', mouseUpEventHandler);
        touchstartEventHandler(detectEvent);
    }
}

function mouseWins(e) {
    setUpHandler(true, e);
}

function touchWins(e) {
    setUpHandler(false, e);
}

function removeRaceHandlers() {
    canvas.removeEventListener('mousedown', mouseWins);
    canvas.removeEventListener('touchstart', touchWins);
}

canvas.addEventListener('mousedown', mouseWins);
canvas.addEventListener('touchstart', touchWins);
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

的jsfiddle

  • 线的宽度可以用context.lineWidth.
  • 线条的颜色可以用strokeStyle.

此解决方案适用于:

  • 台式电脑:
    • Chrome 33
    • Firefox 28
  • 触控设备:
    • Nexus 4上的Firefox 28

它不起作用

  • 触控设备:
    • Nexus 4上的Chrome 34/Opera 20(见问题)

进出口

导入和导出图像可以通过导入/导出完成clickX,clickYclickDrag.

线条平滑

最终可以通过替换来实现lineTo()bezierCurveTo()