Canvas

基础

HTML5新标签 canvas

  • 获取元素 const canvas = document.getElementById("canvas");

  • 获得上下文对象 const ctx = canvas.getContext("2d");

绘制折线图

        ctx.beginPath();//开始一个新的路径
        ctx.moveTo(100,100);//把绘图的 起始点 移动到该坐标
        ctx.lineTo(200,200);//从上一个点到该坐标画一条线
        ctx.lineTo(300,100);//从上一个点到该坐标画一条线
        // ctx.closePath();//闭合路径,连接到起始点
        ctx.stroke();//绘制当前定义的路径
        // ctx.fill();//填充当前路径 不连接起点的话自动帮我们闭合

绘制矩形

画矩形边框

步骤

  • ctx.rect(x,y,width,height) ---> x,y 左上角坐标

  • ctx.stroke() 绘制路径

或者使用复合写法

  • ctx.strokeRect(x,y,width,height)

填充矩形

  • ctx.rect(x,y,w,h)

  • ctx.fill()

或者使用复合写法

  • ctx.fillRect(x,y,w,h)

橡皮擦

  • ctx.clearRect(x,y,w,h)

  • 把位于矩形区域内所有的绘制删除,设置为透明

贝塞尔曲线

二次贝塞尔曲线

  • ctx.quadraticCurveTo(cp1x,cp1y,x,y)

  • cp1x,cp1y:控制点坐标

  • x,y结束点坐标

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
   <style>
        #canvas{
            background-color: pink;
        }
   </style>
</head>     
<body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <script>
        const canvas = document.getElementById("canvas");
        const ctx = canvas.getContext("2d");
        ctx.beginPath();
        ctx.moveTo (100,100);
        ctx.quadraticCurveTo(150,0,200,100);
        ctx.stroke()
    </script>
</body>
</html>

三次贝塞尔曲线

  • ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)

文字

  • 实心文字 ctx.fillText(text,x,y[,maxWidth])

  • 描边文字 ctx.strokeText(text,x,y[,maxWidth])

  • text是文本的内容,xy是文字的起点坐标,maxWidth是文本最大宽度,超过这个宽度文字会被压缩

文字样式

  • ctx.font = "30px bond 家族"

  • ctx.textAlign = "center || left || right"

    center :文本中心在 (x,y) ;left 文本左边框在(x,y);right:文本右边框在(x,y)

  • 基线对齐 ctx.textBaseLine = "top || middle || hanging ||bottom"

图片

创建一个图片

const img = new image()

设置图片路径

img.src = "xxx/xxx"

绘制图片

  • ctx.drawImage(img对象,x,y,w, h);

//img.src是异步加载的,下载图片需要时间,所以需要在图片加载完成后再绘制
 img.onload = function () {
// 在图片加载完成后绘制
ctx.drawImage(img,0, 0, 300, 300); // 调整图片尺寸以适应画布
};

如果要图片进行裁剪

上边的 drawImage 要多四个参数,对应原图片裁剪的区域

  • ctx.drawImage(img,sx,sy,sW,sH,dx,dy,dW,dH)

  • 裁剪:s参数:

样式

  • 填充颜色:ctx.fillStyle = ""

  • 轮廓颜色:ctx.strokeStyle = ""

  • 默认黑色

线段样式

  • ctx.lineWidth = ""线段宽度

  • ctx.lineCap ="butt || round || square"线帽形状

  • ctx.lineJoin = "round || belev || miter(默认)"

  • 虚线长度:ctx.setLineDash([实线长度,间隙长度])

样式的保存

  • ctx.save(),保存save代码上面的样式配置,存到一个栈中

  • ctx.resotre(),取出栈顶样式的配置

变形

  • 移动坐标系!不是移动画板:ctx.translate(X,Y)

  • 旋转坐标系:ctx.rotate( 弧度角度 )

  • 缩放图形:ctx.scale(x,y)

案例

1、小画板

2025/4/11

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        :root{
            --color1:red;
            --color2:green;
            --color3:hwb(60 27% 22%);  
        }
        *{
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
        body{
            background: url("../imgs/flowers.jpg") no-repeat;
        }
        .nav{
            width: 100%;
            height: 30px;
            /* background-color: red; */
            padding-left: 140px;
            margin-top:5px ;
        }
        .name{
            float: left;
            width: 100px;
            height: 100%;
            background:linear-gradient(to right, #ff7e5f, #feb47b); 
            text-align: center;
            line-height: 30px;
            font-size: 14px;
            border-radius:5px ;
            margin-right:10px ;
        }
        button{
            float: left;
            border: none;
            height:100% ;
            width:100px;
            outline: none;
            background:linear-gradient(to right, #ff7e5f, #feb47b); 
            text-align: center;
            font-size: 14px;
            border-radius:5px ;
            margin-right: 10px

        }
        .container{
            width: 1000px;
            height: 80vh;
            background-color: hwb(60 69% 19%);
            margin: 40px auto;
            border-radius: 20px;
        }
        .top{
            width: 100%;
            height: 40px;
            background-color: lch(86.45% 21.48 86.02);
            border-radius:20px 20px 0 0 ;
            padding: 8px 0 0 15px ;
        }
        .dot{
            float: left;
            width: 26px;
            height: 26px;
            border-radius: 50%;
            margin-right: 10px;
        }
       

        .dot:nth-child(1){
            background-color: var(--color1);
        }
        .dot:nth-child(2){
            background-color: var(--color2); 
        }
        .dot:nth-child(3){
            background-color: var(--color3); 
        }
        .painting{
            width: 100%;
            height: calc(100% - 40px);
            border-radius: 0 0 20px 20px;
            background-color: hsl(45, 57%, 86%);
        }
    </style>
</head>
<body>
    <div class="nav">
        <div class="name">画板</div>
        <button class="reset">重置</button>
        <button class="save">保存</button>
    </div>
    <div class="container">
        <div class="top">
            <div class="dot"></div>
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
        <div class="painting">
            <canvas class="canvas" style="border-radius: inherit;"></canvas>
        </div>
    </div>
    <script>
        const painting = document.querySelector(".painting");
        const paintingRect = painting.getBoundingClientRect();
        const canvas = document.querySelector(".canvas");
        const ctx = canvas.getContext("2d");
        const dots = document.querySelectorAll(".dot");
        dots.forEach((dot, index) => {
            dot.onclick = () => {
                // console.log(index);
                switch (index) {
                    case 0:
                        ctx.strokeStyle = "red";
                        break;
                    case 1:
                        ctx.strokeStyle = "green";
                        break;
                    case 2:
                        ctx.strokeStyle = "hwb(60 27% 22%)";
                        break;
                }
            }
        })
        //重置
        document.querySelector('.reset').onclick = (e) => {
            e.preventDefault();
            ctx.clearRect(0, 0, paintingRect.width, paintingRect.height);
            ctx.fillStyle = "hsl(45, 57%, 86%)";
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }
        //保存
        document.querySelector('.save').onclick = (e) => {
            e.preventDefault();
            const img  = canvas.toDataURL('image/png');
            const a = document.createElement('a');
            a.href = img;
            a.download = '画板';
            a.target='_blank';
            a.click();
        }
        canvas.width = paintingRect.width;
        canvas.height = paintingRect.height;
        //必须在设置canvas的宽高后 才能使用成功
        ctx.fillStyle = "hsl(45, 57%, 86%)";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        let startPoint = { x: undefined, y: undefined }; // 起始点
        let isDraw = false; // 是否绘画
        ctx.strokeStyle = "red";
        canvas.onmousedown = e => {
            startPoint = { x: e.offsetX, y: e.offsetY }; // 起始点
            isDraw = true; // 开始绘画
        }
        canvas.onmousemove = e => {
            //    console.log('移动了');
            let x = e.offsetX;
            let y = e.offsetY;
            newPoint = { x: e.offsetX, y: e.offsetY }; // 新的起始点
            if (isDraw) { // 开始绘画
                drawing(newPoint.x, newPoint.y, startPoint.x, startPoint.y); // 调用绘画函数
            }
        }
        canvas.onmouseup = e => {
            // console.log('抬起了');
            isDraw = false; // 停止绘画
             // 重置后重新绘制背景

        }
        let drawing = (x, y, startX, startY) => {
            ctx.beginPath();
            ctx.lineWidth = 2; // 线宽
            ctx.lineCap = "round"; // 线帽
            ctx.moveTo(startX, startY); // 初始坐标 
            ctx.lineTo(x, y); // 结束坐标
            ctx.stroke(); // 绘制
            startPoint = { x: x, y: y }; // 起始点
            ctx.closePath()
        }
    </script>
</body>        
</html>

2、小时钟

示例:

function animate(time) {
    const now = new Date();
    const sec = now.getSeconds();
    const min = now.getMinutes();
    const hour = now.getHours() % 12; // 12小时制
    const ctx = document.getElementById('canvas').getContext('2d');

    // 清除画布
    ctx.clearRect(0, 0, 600, 600);

    // 保存初始状态
    ctx.save();
    ctx.translate(300, 300); // 将坐标原点移到画布中心
    ctx.rotate(-Math.PI / 2); // 将坐标原点旋转90度

    // 绘制时钟刻度
    ctx.strokeStyle = '#000';
    ctx.lineWidth = 5;
    ctx.lineCap = 'round';

    // 绘制小时刻度
    ctx.save();
    for (let i = 0; i < 12; i++) {
        ctx.beginPath();
        ctx.moveTo(100, 0);
        ctx.lineTo(120, 0);
        ctx.stroke();
        ctx.rotate(Math.PI / 6); // 每次旋转30度
    }
    ctx.restore();

    // 绘制分钟刻度
    ctx.save();
    ctx.lineWidth = 3;
    for (let i = 0; i < 60; i++) {
        if (i % 5 !== 0) { // 跳过小时刻度的位置
            ctx.beginPath();
            ctx.moveTo(110, 0);
            ctx.lineTo(120, 0);
            ctx.stroke();
        }
        ctx.rotate(Math.PI / 30); // 每次旋转6度
    }
    ctx.restore();

    // 绘制时针
    ctx.save();
    ctx.lineWidth = 14;
    ctx.rotate(
        hour * (Math.PI / 6) +
        (min * Math.PI / 360) +
        (sec * Math.PI / 21600)
    );
    ctx.beginPath();
    ctx.moveTo(-20, 0);
    ctx.lineTo(80, 0);
    ctx.stroke();
    ctx.restore();

    // 绘制分针
    ctx.save();
    ctx.lineWidth = 10;
    ctx.rotate(
        (min * Math.PI / 30) +
        (sec * Math.PI / 1800)
    );
    ctx.beginPath();
    ctx.moveTo(-20, 0);
    ctx.lineTo(100, 0);
    ctx.stroke();
    ctx.restore();

    // 绘制秒针
    ctx.save();
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 2;
    ctx.rotate(sec * Math.PI / 30);
    ctx.beginPath();
    ctx.moveTo(-20, 0);
    ctx.lineTo(120, 0);
    ctx.stroke();
    ctx.restore();

    // 恢复初始状态
    ctx.restore();

    // 动画循环
    window.requestAnimationFrame(animate);
}

// 启动动画
window.requestAnimationFrame(animate);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值