前端图形:SVG与Canvas
00、前端图形
前端代码实现图形的几种方式:CSS、SVG、Canvas(主要是JavaScript编码)
CSS也是可以画图的,需要借助于高宽、边框border
、clip-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>
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> | 路径数据,<path> 最重要的属性,由多组命令+ 坐标点组成 |
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>
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.3、svg工具/资源/库
🔸矢量图标资源
🔸SVG工具
路径<path>可以绘制任意的图形,直接编码是比较困难的,特别是C
贝塞尔曲线。所以一般都是用专业工具来绘制<svg>图形的,也有在线工具。
- 专业工具AI:Adobe Illustrator
- 在线SVG编辑器,Method Draw
- SVGator,一个在线SVG动画制作工具,导入SVG文件,进行可视化的动画制作。
🔸动画库
- 第三方库GSAP,来自GreenSock,可以更简单的实现更丰富的动画效果,不光支持SVG,包括页面中任意可以通过JS访问的元素、属性,都可以实现动画。
- anime.js,功能强大,非常轻量,压缩后大小9K。
- SVG动画库:mo.js、SVG.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>
弧度 = ( Math.PI / 180 ) * 角度,360度的弧度= Math.PI*2
2.2、动画
Canvas的动画就是不停的重绘,其实任何动画都是这样,前端实现动画有以下几种姿势:
- setInterval(function, delay):定时重绘,适合不需要交互的场景。
- setTimeout(function, delay):定时重绘,适合通过键盘或者鼠标事件来捕捉用户的交互,再用
setTimeout
执行相应的动作。 - requestAnimationFrame(callback):动画专用API,这个方法更加平缓并更加有效率,当系统准备好了重绘条件的时候,才调用绘制动画帧。
requestAnimationFrame (render)
是HTML5提供的动画API,他向浏览器申请执行一个动画帧渲染函数,浏览器在下次重绘(刷新)之前调用这个render
回调函数。requestAnimationFrame
还做了很多优化以提升性能和流畅度。
- 稳定60帧:浏览器的刷新周期大多和屏幕刷新率匹配,为每秒60次,非常稳定高效。不像
setInterval
、setTimeout
还要在任务队列里排队等待,就可能发生丢帧和卡顿现象。 - 合并优化:浏览器会优化动画的执行,把每一帧的所有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>
用canvas实现的一个彩球大战:codepen
03、WebGL
WebGL 作为一种WEB 3D绘图技术,依托于<canvas>
元素。WebGL是运行在GPU上的,可面向底层显卡编程,可调用底层的接口,实现硬件加速,在2D图形绘制上性能会优于Canvas2D。
传说原生的WebGL比较难学,可考虑使用使用第三方的WebGL库,如Three.js、Cesium.js(3D地图)、Babylon.js
©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址