• CSS基础12-canvas


    canvas

    介绍

    H5新特性,canvas被用来绘制图形,制作图片集合,甚至用来实现动画效果

    属性值

    只有width和height属性

    画线

    1. 通过const cvs = document.querySelector(‘canvas’)拿到DOM元素
    2. 通过const ctx = cvs.getcontext(“2d”)拿到CanvasRenderingContext2D的类型对象
    3. 开启一条路径ctx.beginPath()
    4. 开始位置ctx.moveTo(x,y)
    5. 结束位置ctx.lineTo(x,y)
    6. 进行上色ctx.stroke()
    7. 关闭路径ctx.closePath()
    8. 设置绘线的粗细ctx.lineWidth,默认值为1.0
    9. 设置线段端点显示的样子,butt,round 和 square
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <title>Documenttitle>
        <style>
            canvas{
                margin: 0 auto;
                border: 1px solid #000;
                display: block;
            }
        style>
    head>
    <body>
        <canvas width="500" height="500">canvas>
        <script>
            const canvas = document.querySelector('canvas');
            const ctx = canvas.getContext('2d');
    		// 画虚线
            for (let i = 0; i < 20; i++) {
                drawLine(100+10*i,100,105+10*i,100,'green',2)
            }
    		
            function drawLine(x1,y1,x2,y2,color,width){
                ctx.beginPath();
                ctx.moveTo(x1,y1);
                ctx.lineTo(x2,y2);
                ctx.strokeStyle=color;
                ctx.lineWidth=width;
                ctx.stroke();
                ctx.closePath();
            }
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    画矩形

    • rect(x,y,width,height)
      • x,y矩形坐标
      • width,height宽高
    • strokeRect(x,y,width,height)
      • 描边矩形
    • fillRect(x,y,width,height)
      • 填空矩形
      • 会自动闭合路径
    • clearRect(x,y,width,height)
      • 清除矩形
      • 清空画布,类似与橡皮檫

    画圆

    • arc(x,y,radius,startAngle,endAngle,counterclockwise)
      • x,y描述圆心坐标
      • radius圆形半径
      • startAngle,endAngle起始角度和结束角度
      • counterclockwise顺时针和逆时针

    绘制文字

    • fillText()绘制无填充文字
    • strokeText()绘制有填充文字
    • measureText()返回文本宽度对象
    • font='red’CSS样式
    • textBaseline='bottom’设置底线对齐绘制基线
    • textAlign='left’设置文字对齐方式
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <title>Documenttitle>
        <style>
            canvas{
                margin: 0 auto;
                border: 1px solid #000;
                display: block;
            }
        style>
    head>
    <body>
        <canvas width="600" height="600">canvas>
        <script>
       		// 绘制饼状图并显示比例
            const canvas = document.querySelector('canvas');
            const ctx = canvas.getContext('2d');
    
            const data = [{
                value:'苹果',
                color:'red',
                num:0.1,
            },{
                value: '香蕉',
                color:'yellow',
                num:0.3,
            },{
                value: '梨子',
                color: 'green',
                num:0.4,
            },{
                value: '橘子',
                color: 'orange',
                num:0.2
            }]
            let temAngle=-90;
            for (let i = 0; i < data.length; i++) {
                const angle=data[i].num*360;
                drawArc(300,300,200,angle,data[i].color,data[i].num)
            }
            function drawArc(x1,y1,radius,angle,color,num){
                ctx.beginPath();
                ctx.moveTo(x1,y1);
                ctx.fillStyle=color;
                const startAngle=temAngle*Math.PI/180;
                const endAngle=(temAngle+angle)*Math.PI/180;
                ctx.arc(x1,y1,radius,startAngle,endAngle);
                // 绘制文字
                const text=num*100+'%';
                const textAngle=temAngle+1/2*angle;
                const x=x1+Math.cos(textAngle*Math.PI/180)*(radius+20);
                const y=y1+Math.sin(textAngle*Math.PI/180)*radius;
                if(textAngle>90&&textAngle<270){
                    ctx.textAlign='end'
                }
                ctx.fillText(text,x,y)
                ctx.fill();
                temAngle+=angle;
            }
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    绘制图片

    • context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
      • imgDOM对象||也可以是画布,也就是把一个画布整体的渲染到另外一个画布上
      • sx,sy裁剪框左上角X和Y坐标
      • swidth,sheight裁剪宽高
      • x,y图片坐标
      • width,height图片宽高
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <title>Document</title>
        <style>
            canvas{
                margin: 0 auto;
                border: 1px solid #000;
                display: block;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
        	// 绘制帧动画
            const canvas = document.querySelector('canvas');
            const ctx = canvas.getContext('2d');
    
            const img=new Image();
            img.src='./imgs/ying.jpeg';
            img.onload=function (){
                let frameIndex=0;
                let dirIndex=0;
                setInterval(()=>{
                    ctx.clearRect(0,0,canvas.width,canvas.height);
                    ctx.drawImage(
                        img,
                        frameIndex*130,
                        dirIndex*164,
                        130,
                        164,
                        100,
                        100,
                        130*2,
                        164*2
                    )
                    frameIndex++;
                    frameIndex%=3;
                    console.log(frameIndex)
                    if(frameIndex===2){
                        dirIndex++
                    }
                    dirIndex%=2;
                },1000/4)
            }
        </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    阴影与线性渐变和圆形渐变

    阴影

    • shadowColor : 设置或返回用于阴影的颜色
    • shadowBlur : 设置或返回用于阴影的模糊级别,大于 1 的正整数,数值越高,模糊程度越大
    • shadowOffsetX: 设置或返回阴影距形状的水平距离
    • shadowOffsetY: 设置或返回阴影距形状的垂直距离

    线性渐变

    • ctx.createLinearGradient(x0,y0,x1,y1);
      • x0,y0 起始坐标
      • x1,y1 结束坐标

    圆形渐变

    • context.createRadialGradient(x0,y0,r0,x1,y1,r1)
      • x0: 渐变的开始圆的 x 坐标
      • y0: 渐变的开始圆的 y 坐标
      • r0: 开始圆的半径
      • x1: 渐变的结束圆的 x 坐标
      • y1: 渐变的结束圆的 y 坐标
      • r1: 结束圆的半径
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <title>Documenttitle>
        <style>
            canvas{
                margin: 0 auto;
                border: 1px solid #000;
                display: block;
            }
        style>
    head>
    <body>
        <canvas width="500" height="500">canvas>
        <script>
            const canvas = document.querySelector('canvas');
            const ctx = canvas.getContext('2d');
    
            ctx.fillStyle='rgba(255,0,0,0.9)';
            ctx.shadowColor='teal';
            ctx.shadowBlur=10;
            ctx.shadowOffsetX=10;
            ctx.shadowOffsetY=10;
            // 创建线性渐变
            const grd=ctx.createLinearGradient(0,0,170,0);
            grd.addColorStop(0,'black'); // 添加渐变颜色
            grd.addColorStop(0.5,'red')
            grd.addColorStop(1,'white');
            ctx.fillStyle=grd;
            ctx.fillRect(0,0,300,300)
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    位移画布

    移动

    ctx.translate(x,y) 方法重新映射画布上的 (0,0) 位置

    旋转

    context.rotate(angle); 方法旋转当前的绘图

    缩放

    context.scale(scalewidth,scaleheight)

    保存

    ctx.save() 保存当前环境的状态
    可以把当前绘制环境进行保存到缓存中。

    重置

    ctx.restore() 返回之前保存过的路径状态和属性
    获取最近缓存的 ctx;需要和save()配合使用

    将图片输出为base64格式

    • canvas.toDataURL(type, encoderOptions)
      • type,设置输出的类型,比如 image/png image/jpeg
      • encoderOptions: 0-1 之间的数字,用于标识输出图片的质量,1 表示无损压缩,类型为: image/jpeg 或者 image/webp 才起作用

    cnavas对图片裁剪

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>裁剪图片title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .wrap{
                width: 800px;
                margin: 0 auto;
            }
            .canvasBox{
                width: 600px;
                height: 600px;
                background-color: #eee;
                position: relative;
            }
            .mark{
                width: 50%;
                height: 50%;
                background-color: rgba(0,0,0,0.5);
                position: absolute;
                top: 150px;
                left: 150px;
                /*display: none;*/
            }
            ul{
                display: flex;
                width: 600px;
                justify-content: space-between;
            }
            li{
                list-style: none;
            }
        style>
    head>
    <body>
        <div class="wrap">
            <div class="canvasBox">
                <canvas>canvas>
                <div class="mark">div>
            div>
            <ul>
                <li>
                    <label for="file">选择图片label>
                    <input
                            id="file"
                            type="file"
                            accept="image/*"
                            style="display: none"
                    />
                li>
                <li onclick="scale(1)">放大li>
                <li onclick="scale(0)">缩小li>
                <li onclick="save()">保存li>
            ul>
            <img src="" id="img"/>
        div>
        <script>
            /**
             * 准备数据
             * 1. 画布大小,遮罩大小和位置,上传大小和位置
             */
            let img;
            let imgW;
            let imgH;
            let imgOriginW;
            let imgOriginH;
            let startX;
            let startY;
            const canvasW=600;
            const canvasH=600;
    
            const fileNode=document.querySelector('#file');
            const canvas = document.querySelector('canvas');
            const ctx = canvas.getContext('2d');
            const canvasBoxNode=document.querySelector('.canvasBox');
            const imgNode=document.querySelector('#img');
            // 拿到file文件
            fileNode.onchange=function (e){
                const file=e.target.files[0];
                if(!file) return;
                canvas.width=canvasW;
                canvas.height=canvasH;
                // 文件读取
                const fileExample=new FileReader();
                fileExample.readAsDataURL(file);
                fileExample.onload=(e)=>{
                    img=new Image();
                    img.src=e.target.result;
                    img.onload=()=>{
                        imgW=img.width;
                        imgH=img.height;
                        imgOriginW=100;
                        imgOriginH=100;
                        drawImage()
                    }
                }
                fileNode.value='';
            }
            // 绘制canvas
            function drawImage(){
                ctx.clearRect(0,0,canvas.width,canvas.height);
                ctx.drawImage(img,imgOriginW,imgOriginH,imgW,imgH)
            }
            // 缩放
            function scale(flag){
                if(!img) return;
                let n=imgW/imgH;
                const n1=20;
                const n2=n1/n;
                if(flag){
                    imgW+=n1;
                    imgH+=n2;
                }else {
                    imgW-=n1;
                    imgH-=n2;
                }
                drawImage()
            }
            // 刚移入记录位置
            canvasBoxNode.ontouchstart=function (e){
                const point=e.changedTouches[0];
                startX=point.clientX;
                startY=point.clientY;
            }
            // 移动记录位置
            canvasBoxNode.ontouchmove=function (e){
                if(!img) return;
                const point=e.changedTouches[0];
                const x=point.clientX-startX;
                const y=point.clientX-startY;
                console.log(x,y)
                if(Math.abs(x)<20||Math.abs(y)<20) return;
                imgOriginW+=x;
                imgOriginH+=y;
                drawImage();
                startX=point.clientX;
                startY=point.clientY;
            }
            // 保存遮罩内图片
            function save(){
                if(!img) return;
                const imageData=ctx.getImageData(150,150,300,300);
                const canvas2=document.createElement('canvas');
                const canvas2Ctx=canvas2.getContext('2d');
                canvas2.width=300;
                canvas2.height=300;
                canvas2Ctx.putImageData(imageData,0,0,0,0,300,300);
                imgNode.src=canvas2.toDataURL('image/png')
                console.log(canvas2.toDataURL('image/png'))
            }
        script>
    body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
  • 相关阅读:
    IO流(字节流与字符流) 和 File对象 详解与使用
    虚拟dom比真实dom还快吗?90%回答掉坑里了
    Node.js| Node.js 修改模块全局安装路径、缓存路径、配置镜像源以及修改完毕后全局安装报错问题
    安装Frida工具
    零信任策略下K8s安全监控最佳实践(K+)
    游戏服务器架构的历史、现在以及未来(云游戏)
    pcb导线宽度计算
    【C语言】指针和数组的深入理解(第四期)
    【接口加密】接口加密的未来发展与应用场景
    基于Spring Boot和Kubernetes的RESTful微服务
  • 原文地址:https://blog.csdn.net/weixin_64925940/article/details/126680910