JavaScript - canvas - 画直角坐标系

文章目录

  • 预备知识
    • 关于 canvas 画布的原点
    • 画直线
      • 项目结构
      • HTML
      • CSS
      • JavaScript
      • 效果
    • 设置 canvas 起点偏移(平移变换)
      • JavaScript
      • 效果
    • 画空心三角形
      • JavaScript
      • 效果
    • 画实心三角形
      • JavaScript
      • 效果
  • 画直角坐标系
    • 画 x 轴、y 轴
      • JavaScript
      • 效果
    • 显示鼠标在画布中的坐标
      • JavaScript
      • 效果
    • 显示鼠标在直角坐标系中的坐标
      • JavaScript
      • 效果
  • 参考

预备知识

关于 canvas 画布的原点

在我们开始画图之前,我们需要了解一下画布栅格(canvas grid)以及坐标空间。canvas 的栅格的起点为左上角(坐标为 (0, 0))。

画直线

项目结构

JavaScript - canvas - 画直角坐标系_第1张图片

HTML

index.html 文件:

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Cartesian coordinate systemtitle>
        <link href="css/main.css" rel="stylesheet">
    head>
    <body>
        <canvas id="ccs">canvas>
        <script src="js/main.js">script>
    body>
html>

CSS

main.css 文件:

#ccs {
    box-shadow: 0px 0px 2px 1px rgba(0, 0, 0, 0.4);
}

JavaScript

main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 画直线
    ccsContext.beginPath();
    ccsContext.moveTo(0, 0); // 画笔的起点
    ccsContext.lineTo(100, 100); // 画笔的终点,第一个参数表示向左移动 100 像素,,第二个参数表示向下移动 100 像素
    ccsContext.stroke();
}

效果

JavaScript - canvas - 画直角坐标系_第2张图片

设置 canvas 起点偏移(平移变换)

画布的起点默认在左上角(坐标为 (0, 0)),我们可以使用 CanvasRenderingContext2D.translate() 方法平移画布的起点,使其在画布居中,这样比较符合直角坐标系的风格,但还是有点区别。

JavaScript

编辑 main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 使画布的起点向左移动 ccs.width / 2 像素,向下移动 ccs.height / 2 像素,使其起点位于画布中心
    ccsContext.translate(ccs.width / 2, ccs.height / 2);
    
    // 画直线
    ccsContext.beginPath();
    ccsContext.moveTo(0, 0); // 画笔的起点
    ccsContext.lineTo(100, 100); // 画笔的终点,第一个参数表示向左移动 100 像素,,第二个参数表示向下移动 100 像素
    ccsContext.stroke();
}

效果

JavaScript - canvas - 画直角坐标系_第3张图片

画空心三角形

JavaScript

编辑 main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 使画布的起点向左移动 ccs.width / 2 像素,向下移动 ccs.height / 2 像素,使其起点位于画布中心
    ccsContext.translate(ccs.width / 2, ccs.height / 2);

    // 画空心三角形
    ccsContext.beginPath();
    ccsContext.moveTo(0, 0); // 画笔的起点
    ccsContext.lineTo(50, 50); // 第二个点
    ccsContext.lineTo(0, 100); // 第三个点
    ccsContext.closePath(); // 闭合路径。你可以注释它,看看变化
    ccsContext.stroke();
}

效果

JavaScript - canvas - 画直角坐标系_第4张图片

画实心三角形

JavaScript

编辑 main.js 文件:

注意:当你调用 fill() 函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用 closePath() 函数。但是调用 stroke() 时不会自动闭合。

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 使画布的起点向左移动 ccs.width / 2 像素,向下移动 ccs.height / 2 像素,使其起点位于画布中心
    ccsContext.translate(ccs.width / 2, ccs.height / 2);

    // 画实心三角形
    ccsContext.beginPath();
    ccsContext.moveTo(0, 0); // 画笔的起点
    ccsContext.lineTo(50, 50); // 第二个点
    ccsContext.lineTo(0, 100); // 第三个点
    ccsContext.fill();
}

效果

JavaScript - canvas - 画直角坐标系_第5张图片

画直角坐标系

学会怎么画直线和三角形之后,我们就可以开始画直角坐标系!

画 x 轴、y 轴

JavaScript

编辑 main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 使画布的起点向左移动 ccs.width / 2 像素,向下移动 ccs.height / 2 像素,使其起点位于画布中心
    ccsContext.translate(ccs.width / 2, ccs.height / 2);
    
    // 画 x 正半轴
    drawLine(ccsContext, 0, 0, ccs.width / 2, 0);
    
    // 画 x 正半轴上的刻度
    const offset = 20;
    for (let i = 0; i < ccs.width / 2 / offset; i++) {
        drawLine(ccsContext, offset * i, 0, offset * i, 5);
    }
    
    // 画 x 正半轴的箭头
    strokeTriangle(ccsContext, ccs.width / 2 - 10, 5, ccs.width / 2, 0, ccs.width / 2 - 10, -5);
    
    // 画 x 负半轴
    drawLine(ccsContext, 0, 0, -ccs.width / 2, 0);
    
    // 画 x 负半轴上的刻度
    for (let i = 0; i < ccs.width / 2 / offset; i++) {
        drawLine(ccsContext, -offset * i, 0, -offset * i, 5);
    }
    
    // 画 y 正半轴
    drawLine(ccsContext, 0, 0, 0, ccs.height / 2);
    
    // 画 y 正半轴上的刻度
    for (let i = 0; i < ccs.height / 2 / offset; i++) {
        drawLine(ccsContext, 0, offset * i, 5, offset * i);
    }
    
    // 画 y 正半轴的箭头
    fillTriangle(ccsContext, 5, ccs.height / 2 - 10, 0, ccs.height / 2,  -5, ccs.height / 2 - 10);
    
    // 画 y 负半轴
    drawLine(ccsContext, 0, 0, 0, -ccs.height / 2);
    
    // 画 y 负半轴上的刻度
    for (let i = 0; i < ccs.height / 2 / offset; i++) {
        drawLine(ccsContext, 0, -offset * i, 5, -offset * i);
    }
}

function drawLine(canvasContext, x0, y0, x1, y1) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.stroke();
}

function fillTriangle(canvasContext, x0, y0, x1, y1, x2, y2) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.lineTo(x2, -y2);
    canvasContext.closePath();
    canvasContext.fill();
}

function strokeTriangle(canvasContext, x0, y0, x1, y1, x2, y2) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.lineTo(x2, -y2);
    canvasContext.closePath();
    canvasContext.stroke();
}

效果

JavaScript - canvas - 画直角坐标系_第6张图片

显示鼠标在画布中的坐标

JavaScript

编辑 main.js 文件,显示鼠标在画布中的坐标(见第 58 ~ 89 行):

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const ccs = document.querySelector("#ccs");
    // 设置画布的宽高
    ccs.width = 400;
    ccs.height = 400;
    // console.dir(ccs);

    const ccsContext = ccs.getContext("2d");
    
    // 使画布的起点向左移动 ccs.width / 2 像素,向下移动 ccs.height / 2 像素,使其起点位于画布中心
    ccsContext.translate(ccs.width / 2, ccs.height / 2);
    
    // 画 x 正半轴
    drawLine(ccsContext, 0, 0, ccs.width / 2, 0);
    
    // 画 x 正半轴上的刻度
    const offset = 20;
    for (let i = 0; i < ccs.width / 2 / offset; i++) {
        drawLine(ccsContext, offset * i, 0, offset * i, 5);
    }
    
    // 画 x 正半轴的箭头
    strokeTriangle(ccsContext, ccs.width / 2 - 10, 5, ccs.width / 2, 0, ccs.width / 2 - 10, -5);
    
    // 画 x 负半轴
    drawLine(ccsContext, 0, 0, -ccs.width / 2, 0);
    
    // 画 x 负半轴上的刻度
    for (let i = 0; i < ccs.width / 2 / offset; i++) {
        drawLine(ccsContext, -offset * i, 0, -offset * i, 5);
    }
    
    // 画 y 正半轴
    drawLine(ccsContext, 0, 0, 0, ccs.height / 2);
    
    // 画 y 正半轴上的刻度
    for (let i = 0; i < ccs.height / 2 / offset; i++) {
        drawLine(ccsContext, 0, offset * i, 5, offset * i);
    }
    
    // 画 y 正半轴的箭头
    fillTriangle(ccsContext, 5, ccs.height / 2 - 10, 0, ccs.height / 2,  -5, ccs.height / 2 - 10);
    
    // 画 y 负半轴
    drawLine(ccsContext, 0, 0, 0, -ccs.height / 2);
    
    // 画 y 负半轴上的刻度
    for (let i = 0; i < ccs.height / 2 / offset; i++) {
        drawLine(ccsContext, 0, -offset * i, 5, -offset * i);
    }
    
    
    // 创建一个元素,用于显示鼠标在画布中的坐标
    const span = document.createElement("span");
    span.innerText = new Date().toString();
    span.style.setProperty("display", "none");
    span.style.setProperty("box-shadow", "0px 0px 2px 1px rgba(0, 0, 0, 0.4)");
    span.style.setProperty("padding", "0px 5px");
    span.style.setProperty("position", "fixed");
    
    document.body.appendChild(span);
    
    // 添加鼠标在画布中移动的监听事件
    ccs.addEventListener("mousemove", (event) => {
        // console.log(event);
        console.log(event.layerX, event.layerY);
        
        // console.dir(span);
        span.innerText = event.layerX + ", " + event.layerY; // 鼠标在画布中的坐标
        span.style.setProperty("display", "inline");
    });
    
    // 添加鼠标在 document 中移动的监听事件
    document.onmousemove = (event) => {
        console.log(event);
        console.log(event.clientX, event.clientY);
        
        if (event.toElement !== ccs) {
            span.style.setProperty("display", "none");
        } else {
            span.style.setProperty("top", (event.clientY + 10) + "px");
            span.style.setProperty("left", (event.clientX + 10) + "px");
        }
    }
}

function drawLine(canvasContext, x0, y0, x1, y1) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.stroke();
}

function fillTriangle(canvasContext, x0, y0, x1, y1, x2, y2) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.lineTo(x2, -y2);
    canvasContext.closePath();
    canvasContext.fill();
}

function strokeTriangle(canvasContext, x0, y0, x1, y1, x2, y2) {
    canvasContext.beginPath();
    canvasContext.moveTo(x0, -y0);
    canvasContext.lineTo(x1, -y1);
    canvasContext.lineTo(x2, -y2);
    canvasContext.closePath();
    canvasContext.stroke();
}

效果

从以上效果可以看到,显示的坐标是鼠标在画布中的坐标,而不是鼠标在直角坐标系中的坐标:

显示鼠标在直角坐标系中的坐标

JavaScript

编辑 main.js 文件,显示鼠标在直角坐标系中的坐标(见第 16 行):

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    // 其他保持不变...
    
    // 添加鼠标在画布中移动的监听事件
    ccs.addEventListener("mousemove", (event) => {
        // console.log(event);
        console.log(event.layerX, event.layerY);
        
        // console.dir(span);
        // span.innerText = event.layerX + ", " + event.layerY; // 鼠标在画布中的坐标
        span.innerText = `${event.layerX - ccs.width / 2}, ${-(event.layerY - ccs.height / 2)}`; // 鼠标在直角坐标系中的坐标
        span.style.setProperty("display", "inline");
    });
    
    // 其他保持不变...
}

// 其他保持不变...

效果

参考

Web technology for developers > Web APIs > Canvas API > Canvas tutorial > Drawing shapes with canvas

Web technology for developers > Web APIs > CanvasRenderingContext2D > CanvasRenderingContext2D.translate()

你可能感兴趣的:(HTML,&,CSS,&,JavaScript,javascript,css,直角坐标系,canvas)