HTML5新特性之Canvas

<canvas>是⼀个HTML元素,我们可以将它简单理解为⼀个画板,通过Canvas提供的绘制api我们就可以绘制出各种图形。

一、基础

1、渲染上下文

● getContext('2d')

● getContext('webgl')

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>

    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') //画笔
    </script>
</body>

2、绘制图形

坐标系:

a、线(线,三⻆形,矩形):

  • 绘制: moveTo, lineTo,stroke
  • 设置样式:lineWidth,strokeStyle
  • 路径: beginPath,closePath
<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>

    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') //画笔

        //绘制线段 
        ctx.beginPath() //新建路径,两条线不会互相干扰
        ctx.moveTo(0,10)  //线段起点
        ctx.lineTo(200,10)   //终点
        ctx.strokeStyle = 'orange' //颜色
        ctx.lineWidth = 10
        ctx.stroke() //绘制

        ctx.beginPath()
        ctx.moveTo(0,50)  
        ctx.lineTo(200,50)  
        ctx.strokeStyle = 'green'
        ctx.lineWidth = 10
        ctx.stroke() //绘制

        //绘制三角形
        ctx.beginPath() 
        ctx.moveTo(10,70)  
        ctx.lineTo(200,70)  
        ctx.lineTo(50,200)  
        //ctx.lineTo(10,70)  //图形是闭合的,所以最后一个坐标,等于起始坐标
        ctx.closePath() //让路径闭合
        ctx.fillStyle = 'yellow'
        ctx.fill() //用黄色填充内部
        ctx.lineWidth = 1
        ctx.strokeStyle = 'red'
        ctx.stroke() 

        //绘制矩形 rect()
        ctx.beginPath() 
        ctx.strokeRect(0,220,200,100)
        ctx.fillStyle ='red'
        ctx.fillRect(0,220,200,100)
    </script>
</body>

b、弧线(弧、圆弧/圆)

arc(x, y, radius, startAngle, endAngle, anticlockwise);

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>

    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        //arc 画弧
        ctx.beginPath()
        //圆心x坐标,圆心y坐标,半径,开始角度,结束角度,顺时针
        ctx.arc(400,400,100,0,Math.PI / 3,false) 
        ctx.strokeStyle = '#9013FE'
        ctx.stroke()
    </script>
</body>

 

c、贝塞尔曲线:

  • 二阶贝塞尔曲线

参数:控制点坐标 ,结束点坐标

⼆阶贝塞尔曲线:quadraticCurveTo(cpx, cpy, x, y);

二阶贝塞尔曲线调试工具:Canvas Quadratic Curve Example (site ointstatic.com)

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>

    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        //绘制贝塞尔曲线 二阶
        ctx.beginPath()
        // 起点
        ctx.moveTo(100,100)
        //控制点,结束点
        ctx.quadraticCurveTo(200,500,400,400)
        ctx.stroke()

        //绘制辅助线
        ctx.fillStyle =  'red'
        ctx.fillRect(100,100,10,10)
        ctx.fillRect(200,500,10,10)
        ctx.fillRect(400,400,10,10)
    </script>
</body>
  • 三阶贝塞尔曲线

三阶贝塞尔曲线:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

三阶比二阶多了一个控制点

 三阶贝塞尔曲线调试工具:Canvas Bézier Curve Example (sitepointstatic.com)

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>

    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        //绘制贝塞尔曲线 二阶
        ctx.beginPath()
        // 起点
        ctx.moveTo(100,100)
        //控制点,结束点
        ctx.quadraticCurveTo(200,500,400,400)
        ctx.stroke()

        //绘制辅助线
        ctx.fillStyle =  'red'
        ctx.fillRect(100,100,10,10)
        ctx.fillRect(200,500,10,10)
        ctx.fillRect(400,400,10,10)
    </script>
</body>

效果:

3、绘图样式

a、线条样式:

  • lineWidth(设置线段的宽度)
  • lineCap(设置线段两端的形状)

  • setLineDash(虚线)

b、渐变

沿着某个方向:

  • 线性渐变 ctx.createLinearGradient(x0, y0, x1, y1);

放射状:

  • 径向渐变 ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);

c、纹理样式

  • ctx.createPattern(image,repetition)
<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        // ctx.lineWidth = 15 //宽度15
        ctx.lineCap = "round" //两端形状为圆角
        ctx.beginPath()
        ctx.moveTo(50,50)
        ctx.lineTo(300,50)
        //参数是一个数组,表示实线和虚线的长度
        ctx.setLineDash([20,40,50]) //绘制虚线
        ctx.stroke()

        //线性渐变
        //前两个参数:起始点坐标,后两个参数:结束点坐标
        var gradient = ctx.createLinearGradient(0,0,200,0)  
        //在起始位置是绿色,0代表起始
        gradient.addColorStop(0,"green") 
        //在终止位置是蓝色,1代表终止
        gradient.addColorStop(1,"blue")
        ctx.fillStyle = gradient
        ctx.fillRect(50,200,200,100)
        
        //径向渐变
        //圆1圆心坐标,圆1半径,圆2圆心坐标,圆2半径
        var gradient2 = ctx.createRadialGradient(150,450,150,150,450,0)
        gradient2.addColorStop(0,"white") 
        gradient2.addColorStop(1,"green")
        ctx.fillStyle = gradient2
        ctx.fillRect(50,350,200,200)

        //纹理样式
        let img = new Image()
        img.src = './images/pattern.png'
        img.onload = function(){
            var pattern = ctx.createPattern(img,'repeat')
            ctx.fillStyle = pattern
            ctx.fillRect(50,600,300,100)
        }
    </script>
</body>

4、绘制文本

绘制方式:

  • 描边:stokeText()
  • 填充:fillText()

绘制样式:

font、textAlign、direction、textBaseline

阴影:shadowOffsetX和shadowOffsetY、shadowBlur、shadowColor

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d')
        //88像素 罗马字体 
        ctx.font = "88px Times New Roman"
        //设置阴影
        ctx.shadowOffsetX = 2 //阴影往下偏移两个像素
                ctx.shadowOffsetY = 2 //引用往右偏移两个像素
        ctx.shadowBlur = 2 //模糊值
        ctx.shadowColor = "rgba(255,0,0,0.5)" //颜色
        //第一个参数使绘制的文本内容 第二个和第三个参数是绘制的位置坐标
        // ctx.fillText("hello canvas",100,100)

        //用纹理
        let img = new Image()
        img.src = './images/pattern.png'
        img.onload = function(){
            var pattern = ctx.createPattern(img,'repeat')
            ctx.fillStyle = pattern
            ctx.fillText("hello canvas",100,100)
        }

    </script>
</body>

5、绘制图片

drawImage用法

  • drawImage(image,dx,dy);
  • drawImage(image,dx,dy,dWidth,dHeight);
  • drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight);

        param1:要绘制的图选购对象

        param2、param3:从原图像中开始裁剪的坐标

        param4、param5:从原图像裁剪的宽度、高度

        param6、param7:在目标canvas上绘制的坐标

        param8、param9:在目标canvas上绘制的宽度、高度

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 
        let img = new Image()
        img.src = './images/pattern.png'
        //图片加载完成时触发
        img.onload = function(){
            //第一个参数是绘制的图片,
            //第二个和第三个参数是绘制的位置
            //第四个和第五个参数是绘制的大小
            ctx.drawImage(img,0,0,400,200)
        }
        
    </script>
</body>

二、进阶

1、变形

a、平移(translate)、旋转(rotate)、缩放(scale) ——指的是坐标系的平移和旋转

b、状态的保存与恢复

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        ctx.fillStyle = "red"
        ctx.fillRect(0,0,100,100)
        //将当前的所有状态进行保存
        ctx.save()
        //translate 平移--给坐标系向下、向右平移了400像素
        ctx.translate(400,400)
        ctx.fillRect(0,0,100,100)

        //恢复状态--相当于坐标系又回到(0,0)
        ctx.restore()
        ctx.fillStyle = "black"
        ctx.fillRect(0,0,50,50)

        //旋转
        ctx.save()
        ctx.fillStyle = "yellow"
        ctx.rotate(Math.PI/6) //旋转30°
        ctx.fillRect(300,0,100,100)
        ctx.restore()

        //缩放
        ctx.save()
        ctx.fillStyle = "blue"
        ctx.scale(0.5,0.5) //缩小一半
        ctx.fillRect(400,400,100,100)
        ctx.restore()

    </script>
</body>

c、transform、setTransform

<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        //矩阵变换 默认值:ctx.transform(1,0,0,1,0,0)
        //平移 只更改e、f的值
        ctx.save()
        ctx.transform(1,0,0,1,400,400)
        ctx.fillStyle = 'pink'
        ctx.fillRect(0,0,100,100)
        ctx.restore()

        //缩放 只更改a、d的值
        ctx.save()
        ctx.transform(0.5,0,0,0.5,0,0)
        ctx.fillStyle = 'orange'
        ctx.fillRect(0,0,100,100)
        ctx.restore()

        //倾斜 只更改b、c的值
        ctx.save()
        ctx.transform(1,-0.5,-0.5,1,0,0)
        ctx.fillStyle = 'purple'
        ctx.fillRect(100,100,100,100)
        ctx.restore()
    </script>
</body>

2、合成(重要)

  • destination-over: 先绘制的图形盖在后绘制的图形上
  • destination-out :后绘制的图形会把先绘制的图形进行挖空,形成一个镂空效果
<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        ctx.globalCompositeOperation = 'destination-over';

        ctx.fillStyle = 'blue'
        ctx.fillRect(10,10,100,100)
        ctx.fillStyle = 'red'
        ctx.fillRect(50,50,100,100)
    </script>
</body>

3、裁剪——clip():

  • 需要配合路径去使用(例如:context.arc()、context.rect()),路径所绘制出的图形就是裁剪出的范围,任何后续的绘图操作只会在裁剪范围内显示
  • 在裁剪之前要保存一下当前状态,在裁剪之后恢复状态,以免影响其他绘制
<body>
    <canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
    <script>
        const c = document.getElementById('canvas')
        const ctx = c.getContext('2d') 

        //裁剪
        ctx.rect(0,0,225,100)
        ctx.clip()

        //绘制文字
        ctx.fillStyle = "green"
        ctx.font = "44px Times New Roman"
        ctx.fillText('hello canvas',50,50)

    </script>
</body>


三、实战

1、放大镜或缩小镜(探照灯/望远镜)

技术点:离屏渲染

将一个canvas对象绘制到另一个canvas对象上

实现思路:

  • 准备两个canvas,将两个图片分别绘制在这两个canvas上,一大一小,大的canvas对用户不可见;
  • 在小图点击鼠标时,计算出在大图上相应的点击坐标,这个坐标就是是圆心;
  • 根据圆心的坐标,减去放大镜的半径,能计算出大图左上角的坐标;
  • 在小图中,根据小图的圆心,和放大镜的半径,裁剪出一个圆;
  • 把大图上的图片进行裁剪,绘制到小图上;
<body>
    <!-- 展示的canvas -->
    <canvas id="canvas" style="display:block;margin:0 auto;border:1px solid #aaa;">
        您的浏览器尚不支持canvas
    </canvas>
    <!-- 不可见的canvas -->
    <canvas id="offCanvas" style="display: none">
    </canvas>

    <script>
        //准备两个画布
        var canvas = document.getElementById("canvas")
        var context = canvas.getContext("2d")

        var offCanvas = document.getElementById("offCanvas")
        var offContext = offCanvas.getContext("2d")

        //设置两个canvas的宽高,大图是小图的2倍(相当于放大2倍)
        canvas.width = 920 / 2
        canvas.height = 1598 / 2

        offCanvas.width = 920
        offCanvas.height = 1598
        
        //在每个canvas中绘制一张图片
        var image = new Image()
        image.src = "./images/3.jpg"
        image.onload = function () {
            context.drawImage(image, 0, 0, canvas.width, canvas.height)
            offContext.drawImage(image, 0, 0, offCanvas.width, offCanvas.height)
        }

        //缩放比
        var scale = 2
        var isMouseDown = false //鼠标是否按下

        //鼠标点下去触发事件
        canvas.onmousedown = function (e) {
            e.preventDefault() //阻止默认的鼠标按下行为
            isMouseDown = true
            //记录鼠标点击位置的坐标
            point = {
                x: e.offsetX,
                y: e.offsetY
            }
            drawCanvasWithMagnifier(true, point)
        }

        //鼠标抬起触发事件
        canvas.onmouseup = function (e) {
            e.preventDefault()
            isMouseDown = false
            drawCanvasWithMagnifier(false)
        }

        //鼠标移出
        canvas.onmouseout = function (e) {
            e.preventDefault()
            isMouseDown = false
            drawCanvasWithMagnifier(false)
        }

        //鼠标移动的时候
        canvas.onmousemove = function (e) {
            e.preventDefault()
            if (isMouseDown == true) {
                point = {
                    x: e.offsetX,
                    y: e.offsetY
                }
                console.log(point.x, point.y)
                drawCanvasWithMagnifier(true, point)
            }
        }

        //画放大镜
        function drawCanvasWithMagnifier(isShowMagnifier, point) {
            // 清空画布上的所有内容
            context.clearRect(0, 0, canvas.width, canvas.height)
            // 在画布上绘制图像
            context.drawImage(image, 0, 0, canvas.width, canvas.height)
            //是否绘制放大镜
            if (isShowMagnifier == true) {
                drawMagnifier(point)
            }
        }

        //画放大镜
        function drawMagnifier(point) {

            // 放大镜的半径
            var mr = 200

            //点击的坐标 * 缩放值 = 大的canvas点的坐标
            var imageLG_cx = point.x * scale
            var imageLG_cy = point.y * scale

            //(sx,sy)是大的canvas左上角的坐标
            var sx = imageLG_cx - mr
            var sy = imageLG_cy - mr

            //(dx,dy)是小的canvas的左上角坐标
            var dx = point.x - mr
            var dy = point.y - mr

            context.save() 
            context.lineWidth = 10.0
            context.strokeStyle = "#ccc"

            //定义一个圆形的裁剪区域,
            context.beginPath()
            context.arc(point.x, point.y, mr, 0, Math.PI * 2, false)
            context.stroke()
            context.clip()

            //绘制图形,但是只在裁剪区域中可见
            context.drawImage(offCanvas, sx, sy, 2 * mr, 2 * mr, dx, dy, 2 * mr, 2 * mr)
            context.restore() 
        }

    </script>
</body>

效果:

1

2、彩票刮刮乐

技术点:图像合成

利用图像合成让绘制的内容与与原矩形重合部分清空

<body>
    <div id="ggl"
        style=" background: url(../images/ggl.png);background-position: center center;background-size: cover;">
        <div class="wrapper">
            <div class="text">特等奖</div>
            <canvas id="myCanvas" width="400px" height="200px"></canvas>
        </div>

    </div>
    <script type="text/javascript">

        let canvas = document.getElementById('myCanvas');
        let ctx = canvas.getContext('2d');
        
        let isClick = false;
        
        // 绘制灰色涂层
        ctx.fillStyle = '#909399';
        ctx.fillRect(0, 0, 400, 200);
        
        //定义画笔是圆角
        ctx.lineCap = 'round'
        ctx.lineJoin = 'round'
        ctx.lineWidth = 20

        // 鼠标点击按下事件
        canvas.addEventListener('mousedown', (e) => {
            let x = e.offsetX;
            let y = e.offsetY;
            ctx.beginPath();
            // 设置绘制的起点为当前点击的位置
            ctx.moveTo(x, y)
            isClick = true
        })

        // 鼠标移动事件
        canvas.addEventListener('mousemove', (e) => {
            if (!isClick) {
                return
            }
            let x = e.offsetX;
            let y = e.offsetY;
            // 绘制的内容与原矩形重合部分清空
            ctx.globalCompositeOperation = 'destination-out'
            ctx.lineTo(x, y)
            ctx.stroke()
        })

        canvas.addEventListener('mouseup', (e) => {
            isClick = false
        })
    </script>
</body>
<style>
    * {
        margin: 0;
        padding: 0;
    }

    #ggl {
        position: relative;
        width: 729px;
        height: 1073px;
        background-color: yellow;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .wrapper {
        background-color: white;
        position: relative;
        width: 400px;
        height: 200px;
        border-radius: 50px;
        overflow: hidden;
    }

    .text {
        text-align: center;
        line-height: 150px;
        font-size: 28px;
        font-weight: bold;
        color: brown;
    }

    #myCanvas {
        position: absolute;
        top: 0;
        left: 0;
        width: 400px;
        height: 200px;
        margin: 0 auto;

    }
</style>

效果:

2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值