前端图形:SVG与Canvas

image

00、前端图形

前端代码实现图形的几种方式:CSS、SVG、Canvas(主要是JavaScript编码)

CSS也是可以画图的,需要借助于高宽、边框borderclip-path裁剪、渐变色背景等属性来模拟实现各种图形,当然只能实现一些简单的图形。

border:用四条边框样式属性的各种组合变换,实现一些简单的图形。网上也有画一些稍微复杂的图形,如哆啦A梦,但代码量稍多,可读性不好,并不推荐。

<div class="gcss">
  <p class="border">border</p>
  <p class="rborder">圆角按钮</p>
  <p class="radio"></p>radio
  <p class="triangle1"></p>三角形
  <p class="triangle2"></p>三角形
</div>
<style>
  .gcss p {
    display: inline-block;
    text-align: center; vertical-align: middle;
  }
  .border {
    border: 30px solid;
    border-color: aqua tan violet peru;
    border-radius: 20px;
  }        
  .rborder {
    background-color: #b1ccf3;
    width: 100px; height: 40px; line-height: 40px;
    border-radius: 20px;
  }
  .radio {
    width: 40px; height: 40px;
    border-radius: 50%;
    border: 10px solid;
  }
  .triangle1 {
    border: 50px solid #0001;
    border-left-color: red;
  }
  .triangle2 {
    border-left: 50px solid #0001;
    border-right: 50px solid #0001;
    border-bottom: 50px solid red;
  }
</style>

image.png


01、< svg>矢量图形

<svg>可缩放矢量图形(Scalable Vector Graphics,SVG),是一种基于 XML(数学)描述的二维的矢量图形,内容可以直接插入网页,成为DOM的一部分,然后用 JavaScript 和 CSS 进行操作。SVG 内容也可以写在一个独立文件中,然后用CSS(background-url)、<img><object><embed><iframe>来引用。

大多数现代浏览器都支持SVG 图形,越来越多的项目在使用SVG图形,简单的像图标,复杂的一些图表Chart也有不少是基于SVG实现的。相比于位图,体积更小,可无线缩放而不失真。

比较 矢量图形 位图
存储的数据 存储元素、算法数据 存储像素数据
存储大小
缩放效果 无线缩放,不失真 固定大小,放大会失真
可维护性 很容易修改 修改麻烦
扩展性 支持CSS、JS 不支持
文件格式 .svg,直接嵌入数据到页面 .bmp/.png/.jpg/.gif,<img>可嵌入svg文件
支持的元素 <svg><img><iframe><object> <img><iframe>
兼容性 IE9开始支持 较好
渲染性能 复杂的SVG会占用很多时间 稳定
网络传输性能 和页面数据一起,体积小,速度快 需单独请求图片资源
缓存 随网页内容一起,不可单独缓存 图片可单独缓存

1.1、< svg>元素

<svg>内部支持多种图形算法,基础的如线line、圆形<circle>、矩形rect、文本text,复杂的有折线polyline、多边形polygon、路径数据path等。这些图形都以子元素的形式组合,因此也就都支持CSS、JS的操作了。iconfont-阿里巴巴矢量图标库上有非常丰富的<svg>矢量图形。

元素/属性 描述 值/示例
<svg> 矢量图形元素
 viewBox SVG 画布显示区域,这里 1 单位等同于 1 屏幕单位,SVG里的坐标、尺寸都是基于此画布区域 viewBox="0 0 300 200"
 width、height 宽度、高度 width="300" height="200"
 xmlns xml标签的命名空间,为了区分html、svg,可以省略
<line> 线段
 x1、y1 起点x、y坐标 <line x1="0" y1="100" />
 x2、y2 终点x、y坐标 x2="300" y2="100"
<rect> 矩形:<rect x="5" y="50" height="100" width="290"/>
 x、y 起点坐标
 width、height 矩形的宽、高
 rx、ry x、y方向的圆角半径。r=radius 半径 rx="50" ry="50"
<circle/ellipse> 圆和椭圆:<circle cx="150" cy="100" r="80"/>
 cx、cy 圆心的x、y坐标
 r 圆的半径长度
 rx、ry 椭圆的x、y半径
<polyline/polygon> 折线、多边形,两者数据结构相似,多边形是自动首尾连接封闭成一个区域(Polygon /ˈpɒlɪɡən/ 多边形)
 points x、y坐标的集合,多个坐标逗号,分割 points="0 0, 20 40, 70 80/>
<path> 路径,很常用、很强大的图形绘制,数据在属性d
<d> 路径数据&lt;path&gt;最重要的属性,由多组命令+ 坐标点组成 d="M 50 5 H250 V195 H50 Z"
M x y 移动画笔到坐标点x、y M50 5
L x y 划线到坐标x、y L 250 0
H x 绘制水平线,到坐标x;小写h的坐标为相对位置 H 250
V y 绘制垂直线,到坐标y;小写v的坐标为相对位置 V195
Z 闭合路径(closepath),放到最后用于闭合路径
  C* 绘制曲线,包括贝塞尔曲线、圆弧。
<text> 文本标签,支持CSS样式中的文本样式
  x、y 文本开始位置
  font-size 字体大小
  <textPath> 文字绘制的路径,这个就比较有趣了 <textPath xlink:href="#path1">
公共属性 部分属性可以用CSS设置,支持hover伪类
  stroke 笔画颜色(stroke /stroʊk/ 笔画) ,包括线段、形状线条。 stroke="red"
  stroke-width 画笔线宽 stroke-width="10"
  fill 填充颜色,填充一个区域(矩形、圆形等) fill="#0001"

❗小提示:注意服务器添加对svg的支持,及gzip压缩。

<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
    <circle cx="150" cy="100" r="80" fill="green" />
    <circle cx="150" cy="100" r="70" fill="#fff" />
    <text x="150" y="125" font-size="60" text-anchor="middle" fill="orange">SVG</text>
    <line x1="0" y1="100" x2="300" y2="100" stroke="white" stroke-width="8"/>
</svg>
<svg class="icon" height="200" viewBox="0 0 300 200" version="1.1">
    <rect x="5" y="50" rx="50" ry="50" height="100" width="290" fill="white" stroke="blue" stroke-width="10"/>
    <path d="M 50 5 H250 V195 H50 Z" stroke="red" stroke-width="10" fill="#00000001" />
    <text x="145" y="125" font-size="60" text-anchor="middle" fill="#fab">Path</text>
</svg>
<style>
    svg:hover{
        background-color: aliceblue;
        stroke: red;
        stroke-width: 1px;
        fill: red;
    }
</style>

image.png

1.2、动画

SVG 本身就是一个HTML元素,因此动画可以用CSS的动画来实现(参考 CSS动画),SVG中也有专门用于实现动画的<animate>子元素。这里示例采用JavaScript+transform变换实现旋转效果。

<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
    <circle cx="150" cy="100" r="80" fill="green" />
    <circle cx="150" cy="100" r="70" fill="#fff" />
    <text class="svgc" x="150" y="125" font-size="60" text-anchor="middle" fill="orange">SVG</text>
    <line class="svgc" x1="0" y1="100" x2="300" y2="100" stroke="white" stroke-width="8" />
</svg>
<script>
    let svgcs = document.querySelectorAll(".svgc");
    //设置中心点
    svgcs.forEach(element => {
        element.setAttribute("transform-origin", '150 100');
    });
    let deg = 0;
    setInterval(() => {
        deg = deg > 360 ? 0 : deg + 4;
        svgcs.forEach(element => {
            element.setAttribute("transform", `rotate(${deg})`);
        });
    }, 100);
</script>
<!-- 用CSS动画实现的版本 -->
<style>
    .svgc {
        transform-origin: 150px 100px;
        animation: svgc-routate 2s linear 1s infinite;
    }
    @keyframes svgc-routate {
        from {
            transform: rotate(0deg);
        }
        to {
            transform: rotate(360deg);
        }
    }
</style>

1.gif

1.3、svg工具/资源/库

🔸矢量图标资源

  • Iconfont-阿里巴巴矢量图标库,功能很强大,及其提供丰富的矢量图标库,提供矢量图标下载、在线存储、格式转换等功能。应该就这个就够了!
  • flaticon,国外的矢量图标库

🔸SVG工具

路径<path>可以绘制任意的图形,直接编码是比较困难的,特别是C贝塞尔曲线。所以一般都是用专业工具来绘制<svg>图形的,也有在线工具。

🔸动画库

  • 第三方库GSAP,来自GreenSock,可以更简单的实现更丰富的动画效果,不光支持SVG,包括页面中任意可以通过JS访问的元素、属性,都可以实现动画。
  • anime.js,功能强大,非常轻量,压缩后大小9K。
  • SVG动画库:mo.jsSVG.js

02、< canvas>基础

<canvas>只是一块平平无奇的画布而已(在HTML中就一个<canvas>元素),提供了一点点API,由JS进行绘制各种图形。2D的canvas绘制和<SVG>挺像,都是一些线、矩形、圆、path路径数据。这里就先简单了解一下,<canvas>功能是很强大的,很多可视化图表都是基于<canvas>实现的。

🔸基本特点

  • 双标签,必须包含闭合标签。标签内可以放提示文字,当浏览器不支持<canvas>会显示。
  • 坐标系以左上角为中心点,和SVG一样。

2.1、canvas API

canvas 属性/方法 描述
height、width 元素高度、宽度:<canvas width="400" height="400"></canvas>
getContext(contextType) 获取用于绘制的渲染上下文,contextType:
🔸 "2d":CanvasRenderingContext2D 二维渲染上下文
🔸 "webgl": WebGLRenderingContext 三维渲染上下文,OpenGL ES 2.0
🔸 "webgl2":WebGL2RenderingContext 三维渲染上下文,OpenGL ES 3.0
CanvasRenderingContext2D
fillStyle 填充(内部)颜色 ctx.fillStyle='rgba(250,0,0,0.6)'
strokeStyle 画笔(边框)颜色 ctx.strokeStyle='red',同上,支持color、渐变色画刷、图片画刷
font 字体设置 ctx.font = "bold 48px serif"
lineWidth 线宽 ctx.lineWidth = 15;
toDataURL(type, quality) 可以将canvas绘制的图形转换为Data URI 格式的图像,从而导出为位图文件
基础图形绘制
fillRect(x, y, width, height) 填充一个矩形
strokeRect(x, y, width, height) 绘制一个矩形框
clearRect(x, y, width, height) 清除指定区域的的像素
fillText(text, x, y, [maxWidth]) 填充文本
strokeText(text, x, y [, maxWidth]) 绘制文本(轮廓)
drawImage() 绘制位图
路径绘制 创建路径 > 创建路径 > 渲染路径
beginPath() 开始一个新的路径,清除已有路径列表
closePath() 闭合一个路径
moveTo(x, y) 移动画笔到坐标x、y
lineTo(x, y) 画线到坐标x、y
ellipse(x, y, radiusX, radiusY,r,sa,ea) 椭圆路径 ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle)
rect(x, y, width, height) 矩形路径
arc(x, y, radius, startAngle, endAngle) 绘制圆弧路径
quadraticCurveTo(cp1x, cp1y, x, y) 二次贝塞尔曲线
fill([path], [fillRule]) 填充当前、已存在的路径
stroke([path]) 描边子路径
Path2D 可以单独申明一个路径对象,支持上面的绘制,还支持SVG的path数据。
<canvas id="canvas" width="400" height="400"></canvas>
<script>
    ctx = document.getElementById('canvas').getContext('2d');
    // ctx.alpha = false;
    ctx.fillStyle = 'rgba(250,0,0,0.6)';
    ctx.fillRect(10, 10, 40, 40);
    ctx.fillRect(30, 30, 40, 40);
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 3;
    ctx.strokeRect(90, 10, 150, 50);
    ctx.fillStyle = 'rgba(0,0,250,0.5)';
    ctx.font = '20px 微软雅黑';
    ctx.fillText("文本Text", 120, 40)
    //path
    ctx.beginPath();
    ctx.moveTo(90, 80);
    ctx.lineTo(90, 200);
    ctx.lineTo(190, 200);
    ctx.closePath();
    ctx.arc(120, 120, 50, 0, 360);
    ctx.fill();
</script>

image.png

弧度 = ( Math.PI / 180 ) * 角度,360度的弧度= Math.PI*2

2.2、动画

Canvas的动画就是不停的重绘,其实任何动画都是这样,前端实现动画有以下几种姿势:

  1. setInterval(function, delay):定时重绘,适合不需要交互的场景。
  2. setTimeout(function, delay):定时重绘,适合通过键盘或者鼠标事件来捕捉用户的交互,再用 setTimeout 执行相应的动作。
  3. requestAnimationFrame(callback):动画专用API,这个方法更加平缓并更加有效率,当系统准备好了重绘条件的时候,才调用绘制动画帧。

requestAnimationFrame (render)是HTML5提供的动画API,他向浏览器申请执行一个动画帧渲染函数,浏览器在下次重绘(刷新)之前调用这个render回调函数。requestAnimationFrame还做了很多优化以提升性能和流畅度。

  • 稳定60帧:浏览器的刷新周期大多和屏幕刷新率匹配,为每秒60次,非常稳定高效。不像setIntervalsetTimeout还要在任务队列里排队等待,就可能发生丢帧和卡顿现象。
  • 合并优化:浏览器会优化动画的执行,把每一帧的所有DOM操作集中起来,在一个渲染周期内执行。
  • 隐藏优化:当前窗口或者标签页不可见时停止运行requestAnimationFrame
<canvas id="canvas" width="400" height="400"></canvas>
<script>
    ctx = document.getElementById('canvas').getContext('2d');
    //动画旋转
    let eangle = 0;
    function drawEllipse() {
        ctx.clearRect(255, 150, 140, 140);
        ctx.beginPath();
        ctx.fillStyle = 'rgba(0,250,0,0.3)';
        eangle = eangle > 360 ? 0 : eangle + 1;
        ctx.ellipse(320, 230, 60, 30, eangle * Math.PI / 180, 0, 2 * Math.PI);
        ctx.stroke();
        ctx.fill();
        ctx.beginPath();
        ctx.ellipse(320, 230, 30, 60, eangle * Math.PI / 180, 0, 2 * Math.PI);
        ctx.fillStyle = 'rgba(0,0,250,0.3)';
        ctx.stroke();
        ctx.fill();
    }
    setInterval(drawEllipse, 20);
    //水平移动
    let lx = 40;
    let ltr = true;
    function drawRect() {
        if (lx > 300) ltr = false;
        if (lx < 40) ltr = true;
        // ctx.clearRect(lx, 220, 40, 40);
        ctx.fillStyle = 'rgb(255,255,255,0.4)';
        ctx.fillRect(20, 290, 340, 60);
        lx = ltr ? lx + 2 : lx - 2;
        ctx.fillStyle = 'rgb(110,22,250)';
        ctx.fillRect(lx, 300, 40, 40);
        ctx.strokeRect(lx, 300, 40, 40);
        requestAnimationFrame(drawRect);  //永远不停的提交渲染申请,递归自己
    }
    requestAnimationFrame(drawRect);
</script>

1.gif

用canvas实现的一个彩球大战:codepen


03、WebGL

WebGL 作为一种WEB 3D绘图技术,依托于<canvas>元素。WebGL是运行在GPU上的,可面向底层显卡编程,可调用底层的接口,实现硬件加速,在2D图形绘制上性能会优于Canvas2D。
传说原生的WebGL比较难学,可考虑使用使用第三方的WebGL库,如Three.js、Cesium.js(3D地图)、Babylon.js

MDN:WebGL 教程


©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址

posted @ 2022-10-20 20:16  安木夕  阅读(1514)  评论(0编辑  收藏  举报