JS Canvas - 无法绘制流畅的线条

JS Canvas - Can't draw smooth lines

我一直试图在我的 canvas 上画出流畅的线条,但我没有成功。我试过将 lineCap 设置为圆形,使用 quadratic 而不是 lineTo,但没有任何效果。

在鼠标移动的同时,将鼠标的位置记录到一个数组中,然后遍历数组绘制线条。

**JavaScript**
// GLOBAL VARIABLES - CONTAINER/CANVAS
var wContainer  = document.getElementById('Whiteboard_Container');
var layerGrid   = document.getElementById('LayerGrid');
var lgctx       = layerGrid.getContext('2d');
var layer1      = document.getElementById('Layer1');
var l1ctx       = layer1.getContext('2d');

// GLOBAL VARIABLES - MOUSE/TOUCH
var cursorPos   = { top: 0, left: 0, x: 0, y: 0 };
var cursorType  = 'mouse';  // USED TO CONTROL MOUSE/TOUCH
var dragging    = false;    // SETS/STOPS DRAGGING CANVAS
var dragLock    = false;    // USED TO PREVENT ACCIDENTAL DRAGGING (esp. touch)

// GLOBAL VARIABLES - DRAWING
var layer   = 'LAYER1';
var vectors = [];

// MOUSE EVENT LISTENERS
layer1.addEventListener('mousedown', mouseDown);
layer1.addEventListener('mouseup', mouseUp);

// TOUCH EVENT LISTENERS
layer1.addEventListener('touchstart', touchDown);
layer1.addEventListener('touchend', touchUp);

// INITIALISER
function init()
{
    resize();

    // SET PEN STYLE
    lgctx.strokeStyle = 'rgba(230, 230, 230, 1)';
    lgctx.lineWidth   = 1;
    lgctx.lineCap     = 'round';
    lgctx.lineJoin    = 'round';

    // SET PEN STYLE
    l1ctx.strokeStyle = 'rgba(0, 0, 0, 1)';
    l1ctx.lineWidth   = 10;
    l1ctx.lineCap     = 'round';
    l1ctx.lineJoin    = 'round';
}
init();

// AUTO SIZE THE CANVAS
function resize()
{
    // SET THE CANVAS SIZE
    layerGrid.width     = window.innerWidth * 4;
    layerGrid.height    = window.innerHeight * 4;
    layer1.height       = window.innerHeight * 4;
    layer1.width        = window.innerWidth * 4;
    layer1.height       = window.innerHeight * 4;
}

// MOUSE DOWN
function mouseDown(e)
{
    // Set cursor type
    cursorType = 'mouse';

    // EMPTY CURRENT VECTOR LIST
    vectors = [];
    vectors.length = 0;

    if(e.button == 0) // LEFT MOUSE CLICK
    {
        layer1.addEventListener('mousemove', setVectors);
    }
    else if(e.button == 2 && dragLock == false) // RIGHT MOUSE CLICK
    {
        dragging = true;
        layer1.addEventListener('mousemove', canvasDrag);

        // SET START SCROLL POSITION
        cursorPos.top  = wContainer.scrollTop;
        cursorPos.left = wContainer.scrollLeft;

        // SET CURSOR POSITION
        cursorPos.x = e.clientX;
        cursorPos.y = e.clientY;
    }
}

// MOUSE UP
function mouseUp(e)
{
    // PREVENT DRAGGING & REMOVE GRID
    dragging = false;
    layer1.removeEventListener('mousemove', canvasDrag);
    layer1.removeEventListener('mousemove', setVectors);
    removeGrid();
}

// TOUCH DOWN
function touchDown(e)
{
    e.preventDefault();

    // Set cursor type
    cursorType = 'touch';

    // EMPTY CURRENT VECTOR LIST
    vectors = [];
    vectors.length = 0;

    if(e.touches.length == 1)       // SINGLE TOUCH
    {
        layer1.addEventListener('touchmove', setVectors);
    }
    else if(e.touches.length > 1 && dragLock == false)  // MULTI-TOUCH
    {
        dragging = true;
        layer1.addEventListener('touchmove', canvasDrag);

        // SET START SCROLL POSITION
        cursorPos.top  = wContainer.scrollTop;
        cursorPos.left = wContainer.scrollLeft;

        // SET CURSOR POSITION
        cursorPos.x = e.touches[0].clientX;
        cursorPos.y = e.touches[0].clientY;
    }
}

// TOUCH UP
function touchUp(e)
{
    // PREVENT DRAGGING & REMOVE GRID
    dragging = false;
    layer1.removeEventListener('mousemove', canvasDrag);
    layer1.removeEventListener('touchmove', setVectors);
    removeGrid();
}

// HANDLE MOVEMENT
function canvasDrag(e)
{
    if(dragging == true && cursorType == 'mouse')   // MOUSE DRAG
    {
        // DRAW GRID AS GUIDANCE
        drawGrid();

        const dx = e.clientX - cursorPos.x;
        const dy = e.clientY - cursorPos.y;

        wContainer.scrollTop  = cursorPos.top - dy;
        wContainer.scrollLeft = cursorPos.left - dx;
    }
    else if(dragging == true && cursorType == 'touch' && e.touches.length > 1) // TOUCH DRAG
    {
        // DRAW GRID AS GUIDANCE
        drawGrid();

        const dx = e.touches[0].clientX - cursorPos.x;
        const dy = e.touches[0].clientY - cursorPos.y;

        wContainer.scrollTop  = cursorPos.top - dy;
        wContainer.scrollLeft = cursorPos.left - dx;
    }
}

// SET VECTORS
function setVectors(e)
{
    if(cursorType == 'mouse') // MOUSE DRAW
    {
        // Set the drawing vectors
        vectors.push({x:e.clientX + wContainer.scrollLeft, y:e.clientY + wContainer.scrollTop});
    }
    else if(cursorType == 'touch' && e.touches.length == 1) // ONLY ALLOW IF SINGLE TOUCH
    {
        // Set the drawing vectors
        vectors.push({x:e.touches[0].clientX + wContainer.scrollLeft, y:e.touches[0].clientY + wContainer.scrollTop});
    }

    // Draw the path, based on the vectors array
    drawVectors();
}

// DRAW VECTORS
function drawVectors()
{
    l1ctx.beginPath();
    //l1ctx.clearRect(0, 0, layer1.width, layer1.height);
    for(var i = 0; i < vectors.length - 2; i++)
    {
        var xc = (vectors[i].x + vectors[i + 1].x) / 2;
        var yc = (vectors[i].y + vectors[i + 1].y) / 2;
        l1ctx.quadraticCurveTo(vectors[i].x, vectors[i].y, xc, yc);
        //l1ctx.lineTo(vectors[i].x, vectors[i].y);
    }
    l1ctx.stroke();
}

// DRAW GRID
function drawGrid()
{
    lgctx.beginPath();
    for(var i = 0; i < layer1.width; i = i + 100)
    {
        lgctx.moveTo(i, 0); 
        lgctx.lineTo(i, layer1.height);
    }
    lgctx.moveTo(0, 0);
    for(var i = 0; i < layer1.height; i = i + 100)
    {
        lgctx.moveTo(0, i); 
        lgctx.lineTo(layer1.width, i);
    }
    lgctx.stroke();
    lgctx.closePath();
}

// REMOVE GRID
function removeGrid()
{
    lgctx.clearRect(0, 0, layerGrid.width, layerGrid.height);
}

第一行是它的绘制方式,第二行是我试图让它出现的方式: Line comparison

这是相当大的代码块,因此很难给出准确的解决方案,但归根结底,您需要 anti-aliasing。尝试找到其中包含该术语的一些函数或属性。如果没有,则必须手动实施。

你永远不会清除你的 l1ctx 上下文。
所以你总是在以前的线上画新线并取消抗锯齿。

只需在drawVectors的开头添加一个clearRect调用即可。